🔰

LaravelでTodoリストを実装してみよう!~実装編1:Todo一覧の閲覧機能を実装~

2024/05/04に公開

はじめに

前回の記事で環境構築を済ませたので、今回はTodoリストの機能を一つずつ作っていこうと思います。
前回の記事はこちら↓
https://zenn.dev/kenberu1200/articles/278f4c8ec12be5

テーブルの作成

まずは、Todoリストのデータを格納するテーブルを作ります。

マイグレーションファイルを生成

テーブルの設計ファイルを生成するには以下のコマンドを実行します。
todolistsの部分がテーブル名になるので好きな名前を付けてください。

sail artisan make:migration create_todolists_table

コマンドを実行すると、以下のようなファイルが生成されます。

database/migrations/2024_04_24_004237_create_todolists_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('todolists', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('todolists');
    }
};

upメソッドの中で呼び出されているcreateメソッドで、$table->型名(カラム名)のように記述していくことで、テーブルのカラムを設定することができます。

前々回の設計編でTodolistsテーブルを以下のように定義したので、それに合わせて書き換えていきます。

Todolistsテーブルの設計
カラム名 説明
ID(PK) Todoをユニークに識別するためのID
ユーザーID(FK) どのユーザーのTodoかを識別するためのIDでユーザーテーブルからの外部キー
内容 どんなTodoかを記録するカラム
期限 いつまでに終わらせるかを記録するカラム
完了状態 そのTodoが完了したものかを判別するカラム(True/False)
作成日時 いつ作成されたのかを記録するカラム
更新日時 いつ更新されたかを記録するカラム
database/migrations/2024_04_24_004237_create_todolists_table.php
public function up(): void
    {
        Schema::create('todolists', function (Blueprint $table) {
            $table->id();
+           $table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
+           $table->string('content');
+           $table->date('deadline');
+           $table->boolean('status')->default(0);
            $table->timestamps();
        });
    }

user_idは外部キーなのでforeignId('user_id')->constrained('users')を用いて外部キーであることとどこのテーブルから持ってきているのかを指定します。

cascadeOnDelete()を指定した場合、外部キー下のテーブル(今回はusersテーブル)から引っ張ってきているIDの持つレコードが削除されたら、一緒にcascadeOnDeleteを指定されたレコードも削除されます。

今回のTodoアプリの場合は、ユーザーが削除されたら、ユーザーが作成したTodoも一緒に削除されるということです。

$table->boolean('status')->default(0)は、完了状態を表すstatusカラムをboolean型にしてデフォルトの値を0に設定しています。

マイグレーション

前節で、todolistsテーブルの設計ができたので、mysqlに反映していきます。
データーベースに反映するには、以下のコマンドを実行します。

sail artisan migrate

処理が完了したら、テーブルが作られているか確認してみましょう。

まずmysqlにアクセスします。

sail mysql

次に、todolistsテーブルがきちんと生成されている確認するために、以下のコマンドを実行します。

show columns from todolists

テーブルが生成されていれば、以下のようなテーブルが表示されると思います。

+------------+-----------------+------+-----+---------+----------------+
| Field      | Type            | Null | Key | Default | Extra          |
+------------+-----------------+------+-----+---------+----------------+
| id         | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| user_id    | bigint unsigned | NO   | MUL | NULL    |                |
| content    | varchar(255)    | NO   |     | NULL    |                |
| deadline   | date            | NO   |     | NULL    |                |
| status     | tinyint(1)      | NO   |     | 0       |                |
| created_at | timestamp       | YES  |     | NULL    |                |
| updated_at | timestamp       | YES  |     | NULL    |                |
+------------+-----------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

これで、Todolistsテーブルを無事つくりことができました!

Eloquentモデルの生成

前節で、生成したいテーブルを設計してデーターベースに反映しました。

次に、PHPからデーターベースを操作するためにEloquent ORMのモデルを作成します。

Eloquent ORMとは、Lravel利用されるオブジェクトリレーショナルマッパー(ORM)です。

ORMとは、リレーショナルデーターベースとオブジェクトを相互変換して、オブジェクト指向プログラミング言語を用いて、データーベースを操作するための機能やソフトウェアのことを指します。

実際に、モデルを生成していきましょう。生成するには以下のコマンドを実行します。

sail artisan make:model Todolist

処理が完了すると、app/Models/Todolist.phpというファイルが生成されます。

app/Models/Todolist.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Todolist extends Model
{
    use HasFactory;
}

簡素な記述ですが、これでEloquentモデルとして機能します。

これで、データーベースを操作する準備が整いました。

シーディングとファクトリーの生成

次に、疑似データを生成して、実際にテーブルにデータを挿入してみましょう!

このような疑似データを生成する機能をLaravelは備えているようです。

それが、SeederFactoryです。

Seederとは、データベースにデータの初期値を挿入するための機能で、Factoryとは、Eloquentモデルに対してデフォルトの属性値を設定する機能です。

Factoryで生成する疑似データの形を記述して、Seederで生成する命令を記述するイメージです。
(少なくとも私は、このイメージで理解しています。間違ってたらごめんなさい)

実際に、FactorySeederを生成して、疑似データを作っていきましょう。

ファクトリーの生成

まず、Factoryを生成します。生成には以下のコマンドを実行します。

sail artisan make:factory TodolistFactory --model=Todolist

--model=モデル名で、関連付けるEloquentモデルを指定します。

今回はTodolistモデルのファクトリーを作りたかったので、Todolistモデルを指定しています。

処理が終了すると、database/factories/TodolistFactory.phpというファイルが生成されます。

database/factories/TodolistFactory.php
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Todolist>
 */
class TodolistFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            //
        ];
    }
}

definitionメソッドの内容を以下のように加筆します。

database/factories/TodolistFactory.php
public function definition(): array
    {
        return [
+            'user_id'  => 1,
+            'content'  => $this->faker->realText(30),
+            'deadline' => $this->faker->date(),
+            'status'   => $this->faker->boolean(),
        ];
    }

$this->fakerはダミーを生成してくれるFakerライブラリを呼び出しています。

それぞれのカラムに適した型のダミーを生成しています。

Seederの生成:TodoSeeder

Factoryを記述したので、続いてSeederを生成して記述していきます。
Seederを生成するには以下のコマンドを実行します。

sail artisan make:seeder TodoSeeder

処理が完了すると、database/seeders/TodoSeeder.phpが生成されます。

database/seeders/TodoSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class TodoSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}

TodoSeeder.phpを以下のように書き換えます.

database/seeders/TodoSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
+ use App\Models\Todolist;

class TodoSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
+        Todolist::factory()->count(30)->create();
    }
}

factory()->count(30)->create()で、Factory設定した属性のダミーを30個生成します。

Seederの生成:UserSeeder

Todolistsテーブルには外部キーとしてUsersテーブルのidを参照しているので、Usersテーブルのダミーデータも生成します。

以下のコマンドを実行して、UsersテーブルのSeederを生成します。

sail artisan make:seeder UserSeeder

Todolistsと同じ要領で、UserSeeder.phpを書き換えていきます。

database/seeders/UserSeeder.php
<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        User::factory()->create();
    }
}

ダミーデータ生成

それでは、実際に以下のコマンドを実行して、ダミーデータを生成してみましょう!

 sail artisan db:seed

実際に生成できたのか確認してみます。まずは、mysqlにアクセス!

sail mysql

ひとまずUsersのダミーデータを確認しましょう。

select * from users;

以下のような、何かしらのレコードが入っていればOK!

+----+-----------+------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
| id | name      | email            | email_verified_at   | password                                                     | remember_token | created_at          | updated_at          |
+----+-----------+------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
|  1 | Test User | test@example.com | 2024-04-24 05:19:59 | $2y$12$AWig4FfBKdyEnfX7ofOxteu3nGkYilAzN.bQydyRn9HwzbaD.tNaS | Oq1Kyv2z1p     | 2024-04-24 05:19:59 | 2024-04-24 05:19:59 |
+----+-----------+------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
1 row in set (0.00 sec)

続いて、Todolistsのほうも確認!

select * from todolists;
Empty set (0.01 sec)

あれ?何も生成されてない。。。?

では、生成したいダミーデータを指定してシーディングしてみます。(exitでmysqlから抜けるの忘れずに!)

sail artisan db:seed --class=TodoSeeder

処理が終了したら再度mysqlにアクセスして確かめてみましょう

sail mysql
select * from todolists;
+----+---------+-------------------------------+------------+--------+---------------------+---------------------+
| id | user_id | content                       | deadline   | status | created_at          | updated_at          |
+----+---------+-------------------------------+------------+--------+---------------------+---------------------+
|  1 |       1 | The twelve jurors were all.   | 1980-12-29 |      1 | 2024-04-24 05:21:32 | 2024-04-24 05:21:32 |
////省略/////
| 29 |       1 | Gryphon, and the Hatter went. | 1972-02-13 |      0 | 2024-04-24 05:21:32 | 2024-04-24 05:21:32 |
| 30 |       1 | He looked at Two. Two began.  | 1979-07-23 |      1 | 2024-04-24 05:21:32 | 2024-04-24 05:21:32 |
+----+---------+-------------------------------+------------+--------+---------------------+---------------------+
30 rows in set (0.00 sec)

30個ダミーデータが生成されています!

先ほど生成されなかったのは、おそらくusersテーブルのidを外部キーとして持っており、外部キーであるuser_idがない場合、レコードを削除する設定にしているので、todolistsテーブルのダミーデータがusersテーブルのデータより先に作られた場合、参照元であるusersテーブルのデータがないことになるので、ダミーデータが生成された瞬間に削除されていると考えられます。

ダミーデータを日本語化したい人向け

ダミーデータを日本語化するには、.envファイルを以下のように書き換えます。

.env
APP_LOCALE=ja
APP_FALLBACK_LOCALE=ja
APP_FAKER_LOCALE=ja_JP

それでは、再度シーディングを実行して、日本語になる確認します。

テーブルをマイグレーションしなおしてからシーディングするには、以下のコマンドを実行します。

sail artisan migrate:refresh --seed

念のため、TodoSeederを指定してシーディングしておきます。

sail artisan db:seed --class=TodoSeeder

mysqlにアクセスしてデーターベースを確認してみましょう。

sail mysql
select * from todolists;
+----+---------+--------------------------------+------------+--------+---------------------+---------------------+
| id | user_id | content                        | deadline   | status | created_at          | updated_at          |
+----+---------+--------------------------------+------------+--------+---------------------+---------------------+
|  1 |       1 | ?????????????????????????????? | 1990-08-22 |      1 | 2024-04-25 19:15:20 | 2024-04-25 19:15:20 |
/// 省略 ///
| 29 |       1 | ?????????????????????????????? | 1985-03-29 |      1 | 2024-04-25 19:15:20 | 2024-04-25 19:15:20 |
| 30 |       1 | ?????????????????????????????? | 2019-04-18 |      1 | 2024-04-25 19:15:20 | 2024-04-25 19:15:20 |
+----+---------+--------------------------------+------------+--------+---------------------+---------------------+
30 rows in set (0.00 sec)

あれ?うまく日本語化できてない。。。。

おそらく文字化けの類だと思われるので、文字コードを確認。

show variables like '%char%';
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | latin1                         |
| character_set_connection | latin1                         |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | latin1                         |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.01 sec)

以下の記事によると、文字コードをlatin1からutf8mb4にしてあげるとよいらしい。
https://qiita.com/Asaiii12/items/69e4420d06d91374428a

以下のコマンドで、プロジェクトのディレクトリ直下にmy.cnfファイルを作ります。

touch my.cnf

my.cnfファイルを以下のように書き換えます。

my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci

[client]
default-character-set=utf8mb4

続いて、docker-compose.ymlファイルのmysqlの設定を以下のように書き換えます。

docker-compose.yml
~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~
mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: '%'
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sail-mysql:/var/lib/mysql'
            - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
+            - './my.cnf:/etc/my.cnf'
        networks:
            - sail
        healthcheck:
            test:
                - CMD
                - mysqladmin
                - ping
                - '-p${DB_PASSWORD}'
            retries: 3
            timeout: 5s
~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~

各々のファイルを編集し終えたら、以下のコマンドで一度コンテナを終了させてから、起動しないします。

sail down
sail up -d

コンテナの起動を確認出来たら、mysqlにアクセスして、文字コードが変わっているか確認します。

sail mysql
show variables like '%char%';
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.00 sec)

ちゃんと、utf8mb4に代わっているようです。

もう一度、todolistsテーブルの中身を確認してみましょう。

select * from todolists;
+----+---------+--------------------------------------------------------------------------------------------+------------+--------+---------------------+---------------------+
| id | user_id | content                                                                                    | deadline   | status | created_at          | updated_at          |
+----+---------+--------------------------------------------------------------------------------------------+------------+--------+---------------------+---------------------+
|  1 |       1 | 知しょうぶだと言いうちに進すすきの本の電燈でんとうと思った。                               | 1996-02-09 |      0 | 2024-04-25 20:19:13 | 2024-04-25 20:19:13 |
|  2 |       1 | 歌い出してそれをくらいていました。(ザネリがやはげしいんで。                               | 1997-07-16 |      0 | 2024-04-25 20:19:13 | 2024-04-25 20:19:13 |
~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~
| 30 |       1 | んぼうにどこでした。そこにこんなほんという光をあけていまの。                               | 2003-10-25 |      1 | 2024-04-25 20:19:13 | 2024-04-25 20:19:13 |
+----+---------+--------------------------------------------------------------------------------------------+------------+--------+---------------------+---------------------+
30 rows in set (0.00 sec)

ちゃんと日本語が表示されました!

コントローラーの作成

次に、モデルやビューを制御するコントローラーを作成して行きます。

コントローラーファイルの生成

以下のコマンドを実行して、コントローラーを生成していきます。

sail artisan make:controller Todolist/TodolistController --invokable

処理が終了すると、app/Http/Controllers/Todolist/TodolistController.phpというファイルが生成されます。

中身は以下の通り。

app/Http/Controllers/Todolist/TodolistController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TodolistController extends Controller
{
    /**
     * Handle the incoming request.
     */
    public function __invoke(Request $request)
    {
        //
    }
}

コントローラーファイルの編集

TodolistController.phpの内容を以下のように書き換えます。

app/Http/Controllers/TodolistController.php
////////// 省略 //////////
+ use App\Models\Todolist;
use Illuminate\Http\Request;

class TodolistController extends Controller
{
    /**
     * Handle the incoming request.
     */
    public function __invoke(Request $request)
    {
+        $todolists = Todolist::where([['status', '=', 0]])->get();
+        return view('todolist.todolist')->with([
+            'todolists' => $todolists]);
    }
}

Todolist::where([['status', '=', 0]])->get();によって、todolistsテーブルのstatusカラムが0のレコードを取得しています。

return view('todolist.todolist')->with(['todolists' => $todolists]);resources/views/todolist/todolist.blade.php(次章で作ります)を呼び出しています。

その際、データベースから取得したデータであるtodoliststodolist.blade.phpに渡しています。

ルーティング

コントローラーを作り終わったので、ビューを作成する前に、ルーティングを設定して画面をブラウザに表示できる状態にしましょう。

ルーティングを設定するには、routes/web.phpに書き加えていきます。

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('todolist', \App\Http\Controllers\Todolist\TodolistController::class)
    ->name('todolist');

ひとまずこれでOKです!

ビューの作成

いよいよ画面表示部分であるビューを作成していきます。

設計時に書いた完成予想画面はこんな感じ。

Viteのインストール

まず最初に、Viteをインストールしていきます。

Viteは、非常に高速な開発環境を提供してくれる、コードを本番用に構築する最新のフロントエンド・ビルド・ツールです。Laravelでアプリケーションを構築する場合、通常、Viteを使用してアプリケーションのCSSとJavaScriptファイルを本番環境用のアセットへ構築することになります。

https://readouble.com/laravel/11.x/ja/vite.html

Viteをインストールするには、以下のコマンドを実行します。

sail npm install

Taidwindcssのインストール

ビュー作成作業に入る前に、Tailwindcssをインストールする。

Tailwindcssとは画面の装飾を行うcssのフレームワークの一種です。

比較的簡単かつ効率よく画面の装飾できるので、採用しています!

(ともっともらしいこと言ってみましたが、最初に触った教本がtailwindを使っていただけです。。。)

以下のコマンドを実行して、tailwindcssをインストールします。

sail npm install -D tailwindcss postcss autoprefixer
sail npx tailwindcss init -p

処理が終了すると、tailwind.config.jspostcss.config.jsというファイルが生成されます。

それぞれのファイルの中身は以下の通り。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
}
postcss.config.js
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

tailwind.config.jsを以下のように書き換えます。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
+    "./resources/**/*.blade.php",
+    "./resources/**/*.js",
+    "./resources/**/*.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

その後、resources/css/app.cssを以下のように書き換えます。

resources/css/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;

以下のコマンドを実行して、CSSサーバーを起動するとCSSがページに適用されます。

sail npm run dev

あとは、画面を作る際に、<head> @vite(['resources/css/app.css', 'resources/js/app.js'])Bladeディレクティブを追加したら終わりです。

ビューファイルの生成

以下のコマンドを実行して、ビューファイルを作成します。

sail artisan make:view todolist.todolist

処理が終了すると、resources/views/todolist/todolist.blade.phpが生成されます。

コンポーネントの作成:レイアウト編

ここから画面をコーディングしていくのですが、画面のレイアウトやボタン等をいちいちべた書きしていくのは非効率なので、パーツに分けてコンポーネントとしてあらかじめ作っておきます。

コンポーネントファイルの生成は、以下のコマンドを実行します。

sail artisan make:component layout.layout --view

処理が終了すると、resources/views/components/layout/layout.blade.phpが生成されます。

layout.blade.phpには、主に<head>部分を記述していきます。

resources/views/components/layout/layout.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    <title>{{ $title ?? Todoアプリ }}</title>
</head>
<body class="bg-gray-100">
    {{ $slot }}
</body>
</html>

{{slot}}に子要素(より詳細なレイアウトなど)が入ります。

TailwindcssはclassにCSSを直接記述することでCSSを反映させます。
bg-gray-100は背景色を指定しています。

続いて、ヘッダーとフッターを記述した少し詳細なレイアウトを記述します。

同じ要領でコンポーネントファイルを生成します。

sail artisan make:component layout.todolist-single --view

処理が終了すると、resources/views/components/layout/todolist-single.blade.phpが生成されます。

todolist-single.blade.phpを以下のように書き換えます。

resources/views/components/layout/todolist-single.blade.php
 <div class="flex flex-col h-screen">
    <header class="flex fixed top-0 bg-green-500 w-full items-center">
        <h1>
            <a href={{ route('todolist') }} class="flex items-center">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-10 h-10">
                    <path d="M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z" />
                    <path d="M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z" />
                    <path d="M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z" />
                  </svg>
                  <p class="text-2xl">Todoアプリ</p>
            </a>       
        </h1>
    </header>
    <div class="flex w-full justify-center items-center flex-grow py-10">
        <div class="max-w-screen-lg w-auto">
            {{ $slot }}
        </div>
    </div>
    <footer class="flex fixed bottom-0 bg-green-500 w-full justify-center items-center">
        &copy;2024 Kenberu
    </footer>
</div>

ここまで出来たら、resources/views/todolist/todolist.blade.phpを編集してページを表示させてみましょう!

resources/views/todolist/todolist.blade.php
<x-layout.layout title="リスト | Todoアプリ">
    <x-layout.todolist-single>
        {{-- ここに内容が入る --}}
    </x-layout.todolist-single>
</x-layout.layout>

CSSサーバーが立ち上がっているのをきちんと確認して、http://localhost/todolistにアクセスします。

以下のような画面になっていれば、正常に動作しています。

コンポーネントの作成:ボタン編

続いて、Todo一覧画面ので使うボタンを作っていきます。

上と同じ要領でボタンのコンポーネントファイルを生成します。

sail artisan make:component element.button-a --view

処理が終了すると、resources/views/components/element/button-list.blade.phpファイルが生成されます。

button-list.blade.phpファイルを以下のように書き換えます。

resources/views/components/element/button-list.blade.php
@props([
    'href' => '',
    'theme' => '',
])
@php
    if (!function_exists('getThemeClassForButtonA')) {
        function getThemeClassForButtonA($theme) {
            return match ($theme) {
               'add' => 'py-2 px-4 text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-500',
                'undo' => 'py-2 px-4 text-white bg-green-500 hover:bg-green-600 focus:ring-green-500',
                'edit' => 'py-4 px-4 text-white bg-green-500 hover:bg-green-600 focus:ring-green-500',
                default => '',
            };
        }
    }
@endphp
<a href="{{ $href }}" class="
    inline-flex justify-center
    ml-4
    border border-transparent
    shadow-sm
    text-lg
    font-medium
    rounded-md
    focus:outline-none focus:ring-2 focus:ring-offset-2
    {{ getThemeClassForButtonA($theme) }}">
    {{ $slot }}
</a>

次に、Todoを完了したときに押す完了ボタンを作ります。

sail artisan make:component element.button-check --view

生成されたresources/views/components/element/button-check.blade.phpファイルを以下のように書き換えます。

resources/views/components/element/button-check.blade.php
<form action="{{ route('checktodo', ['todoId' => $todoId]) }}" method="POST">
    @method('PUT')
    @csrf
    <button type="submit" class="
    inline-flex justify-center
    ml-4
    py-2 px-2
    text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-500
    border border-transparent
    shadow-sm
    text-lg
    font-medium
    rounded-md
    focus:outline-none focus:ring-2 focus:ring-offset-2">
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
        <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
    </svg>
    </button>
</form>

さらに、Todoを削除するときに押す削除ボタンを作ります。

sail artisan make:component element.button-deltodo --view

生成されたresources/views/components/element/button-deltodo.blade.phpファイルを以下のように書き換えます。

resources/views/components/element/button-deltodo.blade.php
<form action="{{ route('deletetodo', ['todoId' => $todoId]) }}" method="POST" onclick="return confirm('削除してもよろしいですか?');">
    @method('DELETE')
    @csrf
    <button type="submit" class="
    inline-flex justify-center
    ml-4
    py-4 px-4
    text-white bg-red-500 hover:bg-red-600 focus:ring-red-500
    border border-transparent
    shadow-sm
    text-lg
    font-medium
    rounded-md
    focus:outline-none focus:ring-2 focus:ring-offset-2">
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8">
        <path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
    </svg>  
    </button>
</form>

Todo一覧画面の作成

必要なパーツはすべて作ったので、いよいよTodo一覧画面を作成していきましょう!

resources/views/todolist/todolist.blade.phpファイルを以下のように書き換えます。

resources/views/todolist/todolist.blade.php
@props([
    'user_name' => '',
    'todolists' => [],
])
<x-layout.layout title="リスト | Todoアプリ">
    <x-layout.todolist-single>
            <ul class=" font-medium justify-start w-max">
                <li class="flex sticky top-10 mb-4 lg:col-span-1 items-center text-xl bg-gray-100 border-b-4 border-gray-500">
                    <div class="flex ml-4 w-10">
                    </div>
                    <div class="flex ml-3 w-96">{{ $user_name }}」さんのTodo
                    </div>
                    <div class="flex ml-3 w-32">
                        期限
                    </div>
                    <div class="flex w-8">
                        
                    </div>
                    <div class="flex w-8">
                        
                    </div>
                </li>
                @if ($todolists->isEmpty())
                    <li class="flex h-full w-auto justify-center items-center mb-4">
                        <div class="h-full justify-center text-4xl">Todoを入力してください</div>
                    </li>
                @else
                    @foreach($todolists as $todo)
                    <li class="flex lg:col-span-1 items-center justify-start mb-4">
                        <div class="flex items-center">
                            <x-element.button-check :todoId="$todo->id"></x-element.button-check>
                        </div>
                        <p class="ml-3 text-xl w-96 text-gray-600">
                            {{ $todo->content }}
                        </p>
                        <p class="ml-3 text-xl w-32 text-gray-600">
                            {{ $todo->deadline }}
                        </p>
                        <x-element.button-a :href="route('todolist')" theme="edit">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8">
                                <path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
                                </svg>
                        </x-element.button-a>
                        <x-element.button-deltodo :todoId="$todo->id"></x-element.button-deltodo>
                    </li>
                    @endforeach    
                @endif
                
                <li class="flex flex-wrap sticky bottom-6 lg:col-span-1 justify-center items-center text-xl bg-gray-100 border-t-4 border-gray-500">
                    <div class="flex flex-wrap w-1/2 p-2 justify-evenly">
                        <x-element.button-a :href="route('todolist')" theme="add">Todo登録</x-element.button-a>
                        <x-element.button-a :href="route('todolist')" theme="undo">Todo復元</x-element.button-a>
                    </div>
                </li>
            </ul>
    </x-layout.todolist-single>
</x-layout.layout>

ファイルを保存してから、CSSサーバーが立ち上がっているのをきちんと確認して、http://localhost/todolistにアクセスします。

以下のような画面になっていれば、正常に動作しています。

おわりに

かなり長くなりましたが、以上でTodo一覧画面は一応完成です。

まだ、完了ボタンや削除ボタンの機能を実装していないので、ボタンを押すがエラーが出ますが、次回以降実装することできちんと動くようになります。

次回は、Todo追加機能を作っていく予定です!

ではまた!!

Discussion