LoginSignup
153
171

More than 3 years have passed since last update.

【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第1回DB設計とMigration)

Last updated at Posted at 2019-08-12

Laravelで始めるTwitter風(Twitterクローン)のSNSツール開発チュートリアル

概要

スクールとかの課題だったりLaravelを初めてみたいけど何を作ろうって迷ってる人向けによくあるTwitter風のWEBサイトを作ってみます。

第1回は環境構築からDB設計->マイグレーション実行までやってみたいと思います。

前提

  • PHPをある程度理解している
  • Laravelが使える環境がある
  • MVC構造をある程度理解している

環境

  • Mac
  • Homestead
  • Laravel 5.8

Homestead

今回はHomesteadを使用していますが、DockerでもMAMPでもローカルでも何でもOKです!

DB設計

DB設計といえど要件がなければ設計のしようが無いので簡潔に要件をまとめます。

  • ユーザがログインできる(ログイン状態で無いと閲覧や投稿は出来ない)
  • ユーザ毎にツイート(記事)を投稿できる
  • ツイートに対してコメントといいねが出来る
  • コメントにはいいねとコメントはできない
  • ユーザ同士でフォローができ、自身のタイムラインにはフォローしているユーザのツイート以外見えない

とまぁこんな感じでしょうか。実際のTwitterはログインしなくても見えるのですが、
めんどくさいので今回はログイン状態時しか閲覧できないとします。

テーブルはざっとこんな感じでしょうか。

  • usersテーブル
    • ユーザを管理するテーブル
  • tweetsテーブル
    • ユーザ毎のツイートを管理するテーブル
  • commentsテーブル
    • ツイートに対してコメントする機能
  • favoritesテーブル
    • ツイートに対するいいね機能
  • followersテーブル
    • フォロー関係を管理するテーブル

画像にするとこんな感じです。

スクリーンショット 2019-08-13 13.19.00.png

テーブルの説明はこの記事が分かりやすいと思います。(Rubyですが)
【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸

Migration(マイグレーション)

Migrationとは言うならばテーブルの設計図のようなものです。
テーブルの構築やカラムの追加などの更新をチームで共有しやすいようにすることが出来ます。
これについては良記事が沢山あるので詳しくは書きません。

artisanコマンド

Laravelにはartisanコマンドというターミナルで使える便利なコマンドが用意されています。
例えば以下のコマンドを入力するとapp/Http/Controllersの中にSampleContorllerというファイルが生成されます。

php artisan make:controller SampleController

この要領でMigrationとModelを作っていきたいと思います。

Migrationファイルの作成

make:model 〇〇 -m とすることでModelとMigrationを同時に作成してくれます。
それと分かりやすいようにapp/Modelsと言うディレクトリを作成してそこにまとめて管理します。

Tweetsテーブル

php artisan make:model Models/Tweet -m

Commentsテーブル

php artisan make:model Models/Comment -m

Favoritesテーブル

php artisan make:model Models/Favorite -m

Followersテーブル

php artisan make:model Models/Follower -m

Migrationを実際に書いていく

ユーザに関しては最初から存在している2014_10_12_000000_create_users_table.phpと言うファイルが用意されているのでそちらにカラムを追加します。

Users

2014_10_12_000000_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('screen_name')->unique()->null()->comment('アカウント名');
            $table->string('name')->null()->comment('ユーザ名');
            $table->string('profile_image')->nullable()->comment('プロフィール画像');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

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

Tweets

2019_08_11_082006_create_tweets_table.php
<?php

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

class CreateTweetsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tweets', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id')->comment('ユーザID');
            $table->string('text')->comment('本文');
            $table->softDeletes();
            $table->timestamps();

            $table->index('id');
            $table->index('user_id');
            $table->index('text');

            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');
        });
    }

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

この部分でUsersテーブルと外部キー接続を宣言しています。

2019_08_11_082006_create_tweets_table.php
            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');

Comments

2019_08_11_084403_create_comments_table.php
<?php

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

class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id')->comment('ユーザID');
            $table->unsignedInteger('tweet_id')->comment('ツイートID');
            $table->string('text')->comment('本文');
            $table->softDeletes();
            $table->timestamps();

            $table->index('id');
            $table->index('user_id');
            $table->index('tweet_id');

            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');

            $table->foreign('tweet_id')
                ->references('id')
                ->on('tweets')
                ->onDelete('cascade')
                ->onUpdate('cascade');
        });
    }

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

Favorites

2019_08_11_082124_create_favorites_table.php
<?php

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

class CreateFavoritesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('favorites', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id')->comment('ユーザID');
            $table->unsignedInteger('tweet_id')->comment('ツイートID');

            $table->index('id');
            $table->index('user_id');
            $table->index('tweet_id');

            $table->unique([
                'user_id',
                'tweet_id'
            ]);

            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');

            $table->foreign('tweet_id')
                ->references('id')
                ->on('tweets')
                ->onDelete('cascade')
                ->onUpdate('cascade');
        });
    }

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

Followersテーブル

ここ少しややこしいですね。簡単に言うと自分がフォローしているユーザのツイートをTLに表示するときは
自分がfollowing_idで相手(自分がフォローしているユーザ)がfollowed_idになります。

2019_08_11_082050_create_followers_table.php
<?php

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

class CreateFollowersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('followers', function (Blueprint $table) {
            $table->unsignedInteger('following_id')->comment('フォローしているユーザID');
            $table->unsignedInteger('followed_id')->comment('フォローされているユーザID');

            $table->index('following_id');
            $table->index('followed_id');

            $table->unique([
                'following_id',
                'followed_id'
            ]);
        });
    }

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

uniqueを登録する事で以下のキーの組み合わせで同じIDの登録を防ぐことができる

$table->unique([
    'following_id',
    'followed_id'
]);

Model

Modelに設定を記述する

LaravelではEroquentを使用して登録/編集する時にデフォルトでTimestampが登録するようになっていたり、
登録/更新を許可するカラムを指定したりと様々な設定が可能です。

Users

screen_nameprofile_imageを追加したので、登録/更新を許可するために
$fillableの配列にカラムを指定します。

※Userは元々app直下にあるのでapp/Modelsに移動してください。その際namespaceを変更するのを忘れずに!

app/Models/User
<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'screen_name',
        'name',
        'profile_image',
        'email',
        'password'
    ];
}

Tweets

TweetテーブルではSoftDeleteという論理削除(削除してもDBには残るがシステム上削除したとみなす機能)を使える様に設定します。
ついでに登録/更新を許可するために$fillabletextカラムだけ許可しておきます。

$fillableはLaravelで用意されているメンバ変数です。
$fillableにカラム名を定義するとそれ以外のカラムを登録/更新でエラーを吐きます。
つまりホワイトリストですね。
逆に$guardedというのはブラックリストで登録/更新できないカラムを指定します。
基本的にはどちらでも可です。

app/Models/Tweet.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\softDeletes;

class Tweet extends Model
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'text'
    ];
}

Comments

こちらもTweetと同じです。

app/Models/Comment.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\softDeletes;

class Comment extends Model
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'text'
    ];
}

Favorites

デフォルトでTimestampが設定されているのでfalseにします。

app/Models/Favorite.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Favorite extends Model
{
    public $timestamps = false;
}

Followers

Followerテーブルはincrementも使用しないという設定とprimary_keyを指定する設定を合わせて記述します。

app/Models/Follower.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Follower extends Model
{
    protected $primaryKey = [
        'following_id',
        'followed_id'
    ];
    protected $fillable = [
        'following_id',
        'followed_id'
    ];
    public $timestamps = false;
    public $incrementing = false;
}

リレーションの親子関係

Users

app/Models/User.php
    // 省略

    public function followers()
    {
        return $this->belongsToMany(self::class, 'followers', 'followed_id', 'following_id');
    }

    public function follows()
    {
        return $this->belongsToMany(self::class, 'followers', 'following_id', 'followed_id');
    }

Tweets

app/Models/Tweet.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\softDeletes;

class Tweet extends Model
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'text'
    ];

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

    public function favorites()
    {
        return $this->hasMany(Favorite::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

Comments

app/Models/Comment.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\softDeletes;

class Comment extends Model
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'text'
    ];

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

Migration実行!

php artisan migrate

これでDBは用意できました!第1回はとりあえずここまで!

次回 -> 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第2回Seeder->ログイン/新規登録)

153
171
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
153
171