Edited at

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


Laravelで始めるTwitter風(Twitterクローン)のSNSツール開発


お知らせ (2019年8月23日更新)

記事の内容に誤りがあったので、実践して頂いた人に申し訳ないのですが、以下の内容で変更お願いします。

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

Migration箇所にて変更箇所が数カ所あるため見比べてみて修正お願いします🙇‍♂️


  • create_favorites_tableのuniqueキーを組み合わせ時のユニークに変更

  • Tweet.phpのusers()->user()に変更


概要

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

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


前提


  • PHPをある程度理解している

  • Homesteadを使える環境がある(Vagrant/VirtualBoxインストール済み)

  • MVC構造をある程度理解している


環境


  • Mac

  • Homestead

  • Laravel 5.8


Homestead

まずは環境から作っていきたいので、早速別記事で申し訳ないですが、僕の書いたHomestead環境の構築記事を参考に環境を作ってください。

Laravel(Homestead)とVue.jsの環境構築(Laravelだけでも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には残るがシステム上削除したとみなす機能)を使える様に設定します。

ついでに登録/更新はtextカラムだけ許可しておきます。


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 tweets()
{
return $this->hasMany(Tweet::class);
}

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

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

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

public function tweets()
{
return $this->belongsTo(Tweet::class);
}
}



Favorites


app/Models/Comment.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Favorite extends Model
{
public $timestamps = false;

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

public function tweets()
{
return $this->belongsTo(Tweet::class);
}
}



Migration実行!

php artisan migrate

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

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