Laravelで開発したタスク管理サイトChekeraをアップデートしたので使った技術を紹介する

概要

Laravelを使って個人開発をしているChekeraをこの度アップデートしました。

このアップデートで使用した技術と設計が技術寄りの話しなのでQiita向けかなと思い紹介してみます。

タスク管理サイト:Chekera

https://chekera.net/

スクリーンショット 2018-11-24 21.24.39.png

アップデート内容

タスクをグルーピンする「プロジェクト管理」を新たに追加しました。

スクリーンショット 2018-11-24 22.20.16.png

今までのChekeraはただタスクを作成するだけでしたが、私のライフスタイルではいろんな案件を同時に進行していく必要があったのでグループごとにタスクを管理したかったのです。ですが、今のChekeraだと「プロジェクト」の機能がなかったのでグルーピングすることが出来ませんでした。

なので新しいプロジェクトが作成できるように「新規プロジェクト」を作成できるように機能追加しました。

ただし、削除機能はまだ実装できていません。その代わり、プロジェクトの切り替えと「プロジェクト名」を変更できるようにはしています。
スクリーンショット 2018-11-24 22.22.07.png

アップデート前のChekeraはユーザー認証用の「User」テーブルとタスク管理用の「Task」テーブルしかありませんでしたので新しいテーブルが必要になります。

アップデート前

  • Usersテーブル(ユーザー認証用)
  • Tasksテーブル(task管理用)

アップデート後

  • Usersテーブル(ユーザー認証用)
  • Tasksテーブル(task管理用)
  • Projectsテーブル(プロジェクト管理用)

とDBの構成が変わります。  
DBのカラム構成は後述します。

導入した技術について

今回のプロジェクト新規作成に対して次のようなテクニックを実装しています。

  • リレーションシップ
  • モーダルView(プロジェクト作成に必要)

DB構成について

各テーブルのカラムについてはソースコードを載せることにします。

CreateUsersTable.php
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->integer('current_project_id')->nullable();
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
CreateTasksTable.php
class CreateTasksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->charset = 'utf8';
            $table->collation = 'utf8_unicode_ci';

            $table->increments('id');
            $table->string('name');
            $table->longText('text');
            $table->integer('user_id')->nullable();
            $table->integer('project_id')->nullable();
            $table->boolean('done')->default(false);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}
CreateProjectsTable.php
class CreateProjectsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('projects', function (Blueprint $table) {
            $table->charset = 'utf8';
            $table->collation = 'utf8_unicode_ci';

            $table->increments('id');
            $table->integer('user_id');
            $table->string('title');
            $table->timestamps();
        });
    }

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

ちなみにクラス名は本来はマイグレーションファイルなのでテーブルごとのクラス名はありません
こちらの表記は簡略化しています。
ソースコードとして
日付_create_XXX_table.phpとなっています。

DBにリレーションを紐づける

プロジェクト新規作成の機能でプロジェクトのデータとタスクのデータをそれぞれUserに持たせる実装がシンプルなのですが、今の知識ではどう考えても大変だと感じましたのでリレーションシップを使って分割することにしました。

今まではUserTaskの2つのモデルクラスだけで完結出来ていました。
Taskにuser_idを追加してユーザー情報を紐づけることでログインしたユーザー情報からTaskを引っ張ってきて画面に表示すれば良かったわけです。

ここでプロジェクトの概念を追加する際にUserかTaskのどちらかにproject_idのカラムを追加して管理しても良かったのですが、「プロジェクトの選択」で実装が死ねると思ったのでリレーションシップをつけてやることにしました。

アップデート前

  • User
  • Task

アップデート後

User -> Project -> Task

と言う流れです。

ソースコードで言えば、

User.php
class User extends Authenticatable
{

    public function projects()
    {
        return $this->hasMany('App\Project');
    }
}
Project.php
class Project extends Model
{
    //
    public function user()
    {
        return $this->belongsTo('App\User');
    }

    public function tasks()
    {
        return $this->hasMany('App\Task');
    }
}
Task.php
class Task extends Model
{
    //
    public function project()
    {
        return $this->belongsTo('App\Project');
    }
}

リレーションシップを実装することが出来て

User -> Project -> Task

にすることが出来ました。

Railsも書き方は違えど同じことが出来たはず。(使ったことないけど)

もうここまでできる時点でLaravel(PHP)= Rails(Ruby)って感じです。

リレーションシップで関連付けたモデルのインスタンスの取り出し方

話しが脱線しましたが、これをすることでTaskのデータを取得する際には
Taskインスタンスを作ることなくユーザー情報かプロジェクト情報からTaskデータを取得できるようになります。

sample.php
$tasks = User::find(Auth::id())->projects->where('id', $user->current_project_id)->first()->tasks
// or
$tasks = Project::find(0)->tasks

みたいな書き方でタスク情報を取得できるようになります。

反対にTaskの情報からそのTaskを管理するProject情報とUser情報を取得することも出来ます。

sample.php
$project = $tasks[0]->project

$user = $tasks[0]->project->user

といった感じでユーザー情報にアクセスできるようになります。

これをすることでわざわざ複数のSQLを書かずともそれぞれの情報を取得できるようになるので管理の問題でも速度的にはちょっとは軽くなっているはずです。(このレベルだと実感わかない。)

まあ、そんな感じでサーバーサイドのエンジニアから見たら常識レベルの話かもしれませんが、フロントエンドでもないモバイルエンジニアから見たら結構ハマるポイントはありました。  

これをしたことで今後別の個人開発をする際に「XXX app」 みたいな名前のプロジェクトを作成してそれに対するタスクを管理できるようになりました。

これでまた新しいサービスでも考えるのが楽しくなりそうです。

ちなみにアップデートの際にherokuに新しいブランチをデプロイするのですがその時は必ずDBの構成を変更するので

# マイグレーションを実行
$ heroku run php artisan migrate

マイグレーションの処理が必要だったりします。

と言うことでこれで無事Chekeraのアップデートが完了しました。

よければ使ってやってください。(PC環境を推奨)