現金出納簿3 テーブルcategories

テーブルを作る。

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');
    }

リレーションチェック後。リレーションがあれば削除のボタンが押せない。

コメント