Laravel

Laravel + Vueでtrello風タスク管理アプリを作ろう!![実装編①]

皆さんこんにちは!
この記事は、「Laravel + Vueでtrello風タスク管理アプリを作ろう!!」の2回目になります。
前回の、準備編を見ていない方は、先にそちらを確認して下さい。

早速ですが、前回の続きに入りたいと思います。

テーブル作成

今回作成するアプリケーションでは、次の様なデータを保存する必要があります。

今回は、「tasks」「statuses」という名前で2つテーブルを作成します。

「tasks」は、付箋側で、「statuses」はボード側を表します。

タスク「tasks」
  • タイトル—タスクのタイトルが必要です。
  • 説明—タスクの追加詳細
  • 順序—列のどこにあるかを保存し再現するために利用します。
  • ステータスID —タスクの現在どのステータスに属しているかを判定するため必要です。
  • ユーザーID —今回は、複数のユーザーで管理するボードを作るわけでは無いので、個人を特定するために保存します。
ステータス「statuses」
  • タイトル-ボード自体にもタイトルが必要です。
  • スラッグ-キーなどに使用するために用意。(クライアント側で使います。)
  • 順序-ステータスの順序を定義します。
  • ユーザーID —今回は、複数のユーザーで管理するボードを作るわけでは無いので、個人を特定するために保存します。

今回利用するテーブルは、上の2つの他、Laravel標準で用意されているUsersテーブルも利用します。

テーブル作成で重要な、データ間の繋がりのポイントは以下の内容です。

テーブルのポイント

・1人のユーザーは、複数のステータス(ボード列)を持つ
・1人のユーザーは、複数のタスク(付箋)を持つ
・1つのステータス(ボード列)は、複数のタスク(付箋)を持つ

説明が長くなってすみません。
早速始めていきたいと思います!

モデルとマイグレーションの作成

ターミナルまたはコマンドプロンプトでプロジェクトディレクトリーへ移動して、次のコマンドを実行して下さい。

今回は、あとで必要になるので、「コントローラー」も併せて作成しておきます。

php artisan make:model -mc Task
php artisan make:model -mc Status

オプションに「-mc」を付与する事で、モデル、マイグレーションファイル、コントローラが作成されます。

マイグレーションファイルの編集

先ほど実行したコマンドで、マイグレーションファイルの雛形が出来上がっているので、以下のファイルをエディタで開いて次の様に編集して下さい。
※upメソッドのみ編集すれば、良いです。

database/migrations/XXXX_create_tasks_table.php

public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description')->nullable();
            $table->smallInteger('order')->default(0);
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('status_id');
            $table->timestamps();
        });
    }

database/migrations/XXXX_create_statuses_table.php

public function up()
    {
        Schema::create('statuses', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug');
            $table->smallInteger('order')->default(0);
            $table->unsignedInteger('user_id');
            $table->timestamps();
        });
    }

マイグレーションファイルの編集が終わったら、マイグレーションを実行してデータベースへ反映させます。

php artisan migrate

テーブルが作成できたかどうかは、「phpMyAdmin」やターミナル上から、確認してみて下さい。
※Laravelのプロジェクトを作成した段階で用意されているマイグレーションファイルも同時に実行されるので、他のテーブルもできていますが、問題はありません。

モデルの編集

テーブル同士を関連づけるために、各モデルを編集します。
また、save()メソッドで保存するレコードの定義も併せて行います。

app/User.php

class User extends Authenticatable
{
    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    public function tasks()
    {
        return $this->hasMany(Task::class);
    }

    public function statuses()
    {
        return $this->hasMany(Status::class)->orderBy('order');
    }
}

app/Task.php

class Task extends Model
{
    protected $fillable = [‘title’, ‘description’, ‘order’, ‘status_id’];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function status()
    {
        return $this->belongsTo(Status::class);
    }
}

app/Status.php

class Status extends Model
{
    protected $fillable = [‘title’, ‘slug’, ‘order’];

    public $timestamps = false;

    public function tasks()
    {
        return $this->hasMany(Task::class)->orderBy('order');
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Eloquentのリレーション(関係)は、Eloquentモデルクラスのメソッドとして定義します。

詳しくは、公式ドキュメントを、参照して下さい。

ルーティングとコントローラーの編集

今回、必要となるルートを全て定義しておきます。
今回は「routes/web.php」に全てのルート定義を記述します。

Route::get('/', function () {
    if (Auth::user()) {
        return redirect()->route('home');
    }

    return redirect('/login');
}); //welcomeへ向いているので、変更して下さい。

Auth::routes(); //既にあるはず

Route::get('/home', function () {
    return redirect()->route('tasks.index');
})->name('home');

Route::group(['middleware' => 'auth'], function () {
    Route::get('tasks', 'TaskController@index')->name('tasks.index');
    Route::post('tasks', 'TaskController@store')->name('tasks.store');
    Route::put('tasks/sync', 'TaskController@sync')->name('tasks.sync');
    Route::delete('tasks/{tasks}', 'TaskController@destroy')->name('tasks.destroy');
});

Route::group(['middleware' => 'auth'], function () {
    Route::post('statuses', 'StatusController@store')->name('statuses.store');
    Route::put('statuses/sync', 'StatusController@sync')->name('statuses.sync');
    Route::delete('statuses/{status}', 'StatusController@destroy')->name('statuses.destroy');
});

今回作成するアプリケーションに必要な機能は、このルート定義を見れば掴めるかと思います。

以下のコマンドをターミナルで実行する事で、全てのルートを確認する事ができます。

php artisan route:list

ルーティングに関しての詳細な説明は省略します。

詳しくは、公式ドキュメントを、参照して下さい。

トップページ用のアクションを定義する

トップページを返すルートは以下のものになります。

Route::get('tasks', 'TaskController@index')->name('tasks.index');

http://localhost:8000/tasksへ、Getでリクエストを受けたらTaskControllerのindex()メソッドを実行する、という流れを記述してあります。

それでは、TaskControllerをエディタで開き、次の様に追記して下さい。

app/Http/Controllers/TaskController.php

class TaskController extends Controller
{
    // ここから
    public function index()
    {
        $tasks = auth()->user()->statuses()->with('tasks')->get();

        return view('tasks.index', compact('tasks'));
    }
    // ここまで
}

簡単に説明すると、「tasks/index.blade.php」へ「$tasks」という変数に入れた、データを渡す、という処理になります。

$tasksはデータベースから取得したデータが、Eloquentモデルとして入っています。

Viewの作成

それでは、画面を作りたいと思います。
今回は、デザインやBladeテンプレートについての説明は省略します。

「resources/views/」に、新たに「tasks」ディレクトリを作成して下さい。
さらにそのディレクトリ内に、「index.blade.php」を作成して、エディタで開き、下記の様に編集します。

resources/views/tasks/index.blade.php

@extends('layouts.app')
@section('content')
<div class="md:mx-4 relative overflow-hidden">
    <main class="h-full flex flex-col overflow-auto">
        
    </main>
</div>
@endsection

お疲れ様です。
これで、カンバンを作るための土台が全て出来上がりました。

デフォルトステータスボード

もし、ユーザー登録時にデフォルトでステータスの列を追加する様にしたい場合、次のコードをUser.phpへ記載しておくと良いです。
ユーザー作成時に、自動的にデータベースへレコードが挿入されます。

app/User.php

protected static function booted()
    {
        static::created(function ($user) {
            $user->statuses()->createMany([
                [
                    'title' => '未処理',
                    'slug' => 'backlog',
                    'order' => 1
                ],
                [
                    'title' => '着手',
                    'slug' => 'up_next',
                    'order' => 2
                ],
                [
                    'title' => '進行中',
                    'slug' => 'progress',
                    'order' => 3
                ],
                [
                    'title' => '完了',
                    'slug' => 'done',
                    'order' => 4
                ],
                [
                    'title' => '保留',
                    'slug' => 'on_hold',
                    'order' => 5
                ]
            ]);
        });
    }

まとめ

いかがだったでしょうか?
次回は、Vue.jsの部分(クライアントサイド)を作成したり、APIの処理を記述したりしていきます。

次回から、ブラウザーで確認しながらコードを書いていくので楽しくなってくると思います。

最後まで読んでいただいてありがとうございます。
これからもよろしくお願いします!!