
テーブルを作る。
php artisan make:migration create_categories_table
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('type_id') ;
$table->tinyInteger('category_number');
$table->string(' category_name', 100);
$table->timestamp('updated_at')->useCurrent()->nullable();
$table->timestamp('created_at')->useCurrent()->nullable();
});
php artisan migrate
次にモデルを作る。
php artisan make:model Category
コントロールを作る。(resourceオプション)
php artisan make:controller CategoryController --resource
use App\Http\Controllers\CategoryController;
Route::resource('categories', CategoryController::class);
ルーティング情報を確認する。
php artisan route:list

ビューを作る。TailwindCSSを導入。まず、Node.jsのバージョンを確認。14.18.0以上ならOK。
node -v
TailWindCSSをインストール。
npm install -D tailwindcss postcss autoprefixer
以下を実行。
npx tailwindcss init -p
tailwind.config.js、resources/css/app.cssを以下のように変更。
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
"./resources/**/*.vue",
],
theme: {
extend: {},
},
plugins: [],
}
@tailwind base;
@tailwind components;
@tailwind utilities;
サーバーを起動。
npm run dev
php artisan serve
これで、TailWind CSSが適用される。
Formファサードをインストール。
composer require "laravelcollective/html"
コントロールを作る。
use App\Models\Category;//追記
public function index()
{
$categories = Category::with('type')->get();
return view('categories.index',compact('categories'));
}
ビューを作る。これが結構時間がかかる。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Category画面</title>
@vite('resources/css/app.css')
</head>
<body class="bg-gray-50">
<header class="bg-lime-900">
<div class="max-w-7xl mx-auto px-4 sm:px-6">
<div class="py-6">
<p class="text-white text-xl">Category画面</p>
</div>
</div>
</header>
<main>
<section class="text-gray-600 w-full flex flex-col items-center px-2">
<form action="/categories" method="post" class="m-10 shadow-md rounded-md bg-white w-full max-w-2xl p-10">
@csrf
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">収支区分<span class="text-red-500"> * </span></label>
<div class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700">
<input type="radio" name="type_id" value="1">
<label class="text-lg mr-10">収入</label>
<input type="radio" name="type_id" value="2">
<label class="text-lg">支出</label>
</div>
</div>
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">科目番号<span class="text-red-500"> * </span></label>
<select name="category_number" class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700 border border-gray-200 rounded focus:outline-none focus:bg-white">
<option value="">選択してください...</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
</select>
</div>
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">科目名<span class="text-red-500"> * </span></label>
<input class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700 border border-gray-200 rounded focus:outline-none focus:bg-white"
placeholder="科目名..." name="category_name" value="{{ old('category_name') }}" />
</div>
@error('category_name')
<div class="sm:w-1/3 sm:text-right -mt-6">
<p class="text-red-500">
{{ $message }}
</p>
</div>
@enderror
<div class="flex justify-center">
<button type="submit" class="mt-8 p-4 bg-lime-700 text-white w-full max-w-xs hover:bg-lime-500 transition-colors">
追加
</button>
</div>
</form>
</section>
@if ($categories->isNotEmpty())
<div class="flex justify-center mb-10">
<table class="w-full max-w-2xl p-10 sm:table-auto">
<thead class="bg-gray-50" >
<tr>
<th class="text-right py-2">No.</th>
<th class="text-left py-2">収支区分</th>
<th class="text-right pl-4 py-2">科目番号/</th>
<th class="text-left py-2">科目名</th>
<th class="py-2"></th>
<th class="py-2"></th>
</tr>
</thead>
@foreach ($categories as $category)
<tbody>
<tr class="bg-gray-200">
<td>
<div class="text-right">
{{$category->type_id}}
</div>
</td>
<td>
<div class="text-left">
{{$category->type->type_name}}
</div>
</td>
<td>
<div class="text-right">
{{$category->category_number}}
</div>
</td>
<td>
<divc lass="text-left">
{{$category->category_name}}
</div>
</td>
<td>
<div>
<a href="/categories/{{ $category->id }}/edit/"
class="flex justify-center py-4 w-20 underline underline-offset-2 text-sky-600 md:hover:bg-sky-100 transition-colors">編集</a>
</div>
</td>
<td>
<div>
<form onsubmit="return DeleteCategory();"
action="/categories/{{ $category->id }}" method="post"
class="text-gray-500 font-medium"
>
@csrf
@method('DELETE')
<button type="submit"
class="py-4 w-20 md:hover:bg-sky-100 transition-colors">削除</button>
</form>
</div>
</td>
</tr>
</tbody>
@endforeach
</table>
</div>
@endif
</main>
<footer class="bg-lime-900">
<div class="max-w-7xl mx-auto px-4 sm:px-6">
<div class="flex justify-center">
<a href="/"
class="text-center m-4 w-30 underline underline-offset-2 text-white md:hover:bg-lime-700 transition-colors">HOMEに戻る</a>
<a href="/categorylistdownload"
class="text-center m-4 w-30 underline underline-offset-2 text-white md:hover:bg-lime-700 transition-colors">CSVダウンロード</a>
</div>
</div>
</footer>
<script>
function DeleteCategory() {
if (confirm('本当に削除しますか?')) {
return true;
} else {
return false;
}
}
</script>
</body>
</html>

エラー発生。

category_nameというカラムはない、とSQLは言っている。スペルミスでもなく、テーブルも確認したが、ちゃんとある。数時間格闘して、ようやくマイグレーションを確認すると、カラム名の前に、半角のスペースが。これが原因だ。
原因が特定できたところで、カラム変更のマイグレーションでカラム名の変更を試みるもなかなかうまくいかず。
There is no column with name " category_neme" on table "categories".
おそらく、先頭の空白を認識するかしないかの問題だと思う。いったん、全く違う名前を付けて、とも思ったが、ここは力業で。マイグレーションの状態を確認。ロールバックの実行。再びマイグレーションを実行。これでうまくいった。
php artisan migrate:status
php artisan migrate:rollback
php artisan migrate
再マイグレーションの前にstring100を50に変更。これもうまくいった。
$table->string('category_name',50);
バリテーションチェック作る。
use Illuminate\Support\Facades\Validator;//追加
// バリデーションチェック
$rules = [
'type_id' => 'required',
'category_number'=> 'required',
'category_name'=> 'required|max:50',
];
//エラーメッセージ
$messages = ['type_id.required' => '必須項目です',
'category_number.required' => '必須項目です',
'category_name.required' => '必須項目です',
'max' => '50文字以下にしてください'
];
Validator::make($request->all(), $rules, $messages)->validate();
値の保持のため、以下を追記。もっといい方法があると思うが、とりあえずはこれで動いている。
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">収支区分<span class="text-red-500"> * </span></label>
<div class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700">
<input type="radio" name="type_id" id="type_id_1" value="1" @if ( old( 'type_id' ) == 1 )checked @endif>
<label class="text-lg mr-10" for="type_id_1">収入</label>
<input type="radio" name="type_id" id="type_id_2" value="2" @if ( old( 'type_id' ) == 2 )checked @endif>
<label class="text-lg" for="type_id_2">支出</label>
</div>
</div>
@error('type_id')
<div class="sm:w-1/3 sm:text-right -mt-6">
<p class="text-red-500">{{ $message }}</p>
</div>
@enderror
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">科目番号<span class="text-red-500"> * </span></label>
<select name="category_number" class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700 border border-gray-200 rounded focus:outline-none focus:bg-white">
<option value="">選択してください...</option>
@for ($i = 1; $i <= 9; $i++)
@if (old('category_number') == $i)
<option value= "{{ $i }}" selected>{{ $i }}</option>
@else
<option value= "{{ $i }}">{{ $i }}</option>
@endif
@endfor
</select>
</div>
Edit画面を作る。データの受け渡しは以下のとおり。@method(‘PUT’)を忘れずに。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>CategoryEdit画面</title>
@vite('resources/css/app.css')
</head>
<body class="bg-gray-50">
<header class="bg-lime-900">
<div class="max-w-7xl mx-auto px-4 sm:px-6">
<div class="py-6">
<p class="text-white text-xl">CategoryEdit画面</p>
</div>
</div>
</header>
<main>
<section class="text-gray-600 w-full flex flex-col items-center px-2">
<form action="/categories/{{ $category->id }}" method="post" class="m-10 shadow-md rounded-md bg-white w-full max-w-2xl p-10">
@csrf
@method('PUT')
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">収支区分<span class="text-red-500"> * </span></label>
<div class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700">
<input type="radio" name="type_id" id="type_id_1" value="1" @if ($category->type_id == 1)checked @endif>
<label class="text-lg mr-10" for="type_id_1">収入</label>
<input type="radio" name="type_id" id="type_id_2" value="2" @if ($category->type_id == 2)checked @endif>
<label class="text-lg" for="type_id_2">支出</label>
</div>
</div>
@error('type_id')
<div class="sm:w-1/3 sm:text-right -mt-6">
<p class="text-red-500">
{{ $message }}
</p>
</div>
@enderror
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">科目番号<span class="text-red-500"> * </span></label>
<select name="category_number" class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700 border border-gray-200 rounded focus:outline-none focus:bg-white">
@for ($i = 1; $i <= 9; $i++)
@if ($category->category_number== $i)
<option value= "{{ $i }}" selected>{{ $i }}</option>
@else
<option value= "{{ $i }}">{{ $i }}</option>
@endif
@endfor
</select>
</div>
@error('category_number')
<div class="sm:w-1/3 sm:text-right -mt-6">
<p class="text-red-500">
{{ $message }}
</p>
</div>
@enderror
<div class="flex sm:items-center mb-6 flex-col sm:flex-row">
<label class="sm:w-1/3 font-bold sm:text-right mb-1 pr-4">科目名<span class="text-red-500"> * </span></label>
<input class="w-full sm:w-2/3 bg-gray-200 py-2 px-3 text-gray-700 border border-gray-200 rounded focus:outline-none focus:bg-white"
placeholder="科目名..." name="category_name" value="{{ old('category_name',$category->category_name) }}" />
</div>
@error('category_name')
<div class="sm:w-1/3 sm:text-right -mt-6">
<p class="text-red-500">
{{ $message }}
</p>
</div>
@enderror
<div class="flex justify-center">
<button type="submit" class="mt-8 p-4 bg-lime-700 text-white w-full max-w-xs hover:bg-lime-500 transition-colors">
編集
</button>
</div>
</form>
</section>
</main>
<footer class="bg-lime-900">
<div class="max-w-7xl mx-auto px-4 sm:px-6">
<div class="flex justify-center">
<a href="/categories"
class="text-center m-4 w-30 underline underline-offset-2 text-white md:hover:bg-lime-700 transition-colors">HOMEに戻る</a>
</div>
</div>
</footer>
</body>
</html>
public function edit($id)
{
$category = Category::find($id);
return view('categories.edit', compact('category'));
}
public function update(Request $request, $id)
{
// バリデーションチェック
$rules = [
'type_id' => 'required',
'category_number'=> 'required',
'category_name'=> 'required|max:50',
];
//エラーメッセージ
$messages = ['type_id.required' => '必須項目です',
'category_number.required' => '必須項目です',
'category_name.required' => '必須項目です',
'max' => '50文字以下にしてください。'
];
Validator::make($request->all(), $rules, $messages)->validate();
$category = Category::find($id);
$category->type_id = (int)$request->input('type_id');
$category->category_number = (int)$request->input('category_number');
$category->category_name = $request->input('category_name');
$category->save();
return redirect('/categories');
}
削除ボタンを有効にする。ここでは、まだリレーションのチェックはしていない。
public function destroy($id)
{
Category::find($id)->delete();
return redirect('/categories');
}
リレーションチェック後。リレーションがあれば削除のボタンが押せない。

コメント