Laravelで始めるTwitter風(Twitterクローン)のSNSツール開発チュートリアル
概要
以前5.8で作成した記事の6.x版です。
6.xの書き方に変えるだけじゃなくて、色々気になるところも修正していこうと思います。
Laravel5.8の記事
第1回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第1回DB設計とMigration)
スクールとかの課題だったりLaravelを初めてみたいけど何を作ろうって迷ってる人向けによくあるTwitter風のWEBサイトを作ってみます。
第1回は環境構築からDB設計->マイグレーション実行までやってみたいと思います。
前提
- PHPをある程度理解している
- Laravelが使える環境がある
- MVC構造をある程度理解している
Laravel初心者を脱却したい!という方はこちらも合わせてどうぞ!
Laravelワカンネ(゚⊿゚)から「完全に理解した()」までステップアップ
環境
- Mac
- Docker
- Laravel6.x
今回はDockerを使用していますが、VagrantでもMAMPでもローカルでも何でもOKです!
要件定義
- ユーザがログインできる(ログイン状態で無いと閲覧や投稿は出来ない)
- ユーザはツイート(記事)を投稿できる
- ツイートに対してコメントといいねが出来る
- コメントにはいいねとコメントはできない
- ユーザ同士でフォローができ、自身のタイムラインにはフォローしているユーザのツイートのみ閲覧が可能
とまぁこんな感じでしょうか。実際のTwitterはログインしなくても見えるのですが、
めんどくさいので今回はログイン状態時しか閲覧できないとします。
DB設計
テーブルはざっとこんな感じでしょうか。
- usersテーブル
- ユーザを管理するテーブル
- tweetsテーブル
- ユーザ毎のツイートを管理するテーブル
- commentsテーブル
- ツイートに対してコメントする機能
- favoritesテーブル
- ツイートに対するいいね機能
- followersテーブル
- フォロー関係を管理するテーブル
画像にするとこんな感じです。
この記事ではfavoritesテーブルは中間テーブルとして扱います!
テーブルの説明はこの記事が分かりやすいと思います。(Rubyですが)
【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸
Migration(マイグレーション)
Migrationとは言うならばテーブルの設計図のようなものです。
テーブルの構築やカラムの追加などの更新をファイルとして残すことで、チームで共有しやすいようにすることが出来ます。
これについては良記事が沢山あるので詳しくは書きません。
artisanコマンド
Laravelには artisan
コマンドというターミナルで使用できる便利なコマンドが用意されています。
例えば以下のコマンドを入力すると app/Http/Controllers
の中に SampleContorller
というファイルがクラスなどを定義した状態で生成されます。
php artisan make:controller SampleController
この要領でターミナルにteMigrationとModelを作っていきたいと思います。
Migrationファイルの作成
テーブルを作成するときは下記のようなコマンドを入力します。
php artisan make:migration create_tweets_table
Laravelではテーブル一つに付き、一つのモデルを用意することが多いので、
モデルも同時に作成したいときは make:model 〇〇 -m
とすることでModelとMigrationを同時に作成してくれるので活用していきましょう!
デフォルトではモデルは app/
に設置されてしまうので、モデルのファイルと分かりやすいように app/Models
というディレクトリを作成し、そこにまとめる管理方法が一般的です。
Tweets
テーブル
php artisan make:model Models/Tweet -m
Comments
テーブル
php artisan make:model Models/Comment -m
Followers
テーブル
php artisan make:model Models/Follower -m
Favorites
テーブル
こちらは中間テーブルとして使用する為Modelは不要
php artisan make:migration create_fovorites_table
Migrationを実際に書いていく
ユーザに関しては最初から 2014_10_12_000000_create_users_table.php
というファイルが用意されているのでそちらにカラムを追加します。
書き方など知りたければ日本語ドキュメントをみましょう
Laravel 6.x データベース:マイグレーション
Users
<?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()->comment('アカウント名');
$table->string('name')->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
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
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('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade')
->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tweets');
}
}
この部分でUsers
テーブルと外部キー接続を宣言しています。
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade')
->onUpdate('cascade');
Comments
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
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(['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('comments');
}
}
Favorites
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFavoritesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('favorites', function (Blueprint $table) {
$table->unsignedInteger('user_id')->comment('ユーザID');
$table->unsignedInteger('tweet_id')->comment('ツイート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');
}
}
Favoriteテーブルでは中間テーブルとして使用する為、user_idとtweet_idのみとしてます。
下記の部分ではテーブルにユニーク制約を設定しています。
つまりこのテーブルではuser_idとtweet_idの組み合わせは一意でなければ格納出来ないという制約です
$table->unique(['user_id', 'tweet_id']);
Followers
テーブル
ここ少しややこしいですね。簡単に言うと自分がフォローしているユーザのツイートをTLに表示するときは
自分がfollowing_id
で相手(自分がフォローしているユーザ)がfollowed_id
になります。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
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', 'followed_id']);
$table->unique(['following_id', 'followed_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('followers');
}
}
Model
Modelに設定を記述する
LaravelではEloquentを使用して登録/編集する時にデフォルトでTimestampが登録するようになっていたり、
登録/更新を許可するカラムを指定したりと様々な設定が可能です。
Users
screen_name
と profile_image
を追加したので、登録/更新を許可するために
$fillable
の配列にカラムを追加します。
※Userは元々app
直下にあるのでapp/Models
に移動してください。その際namespaceを変更するのを忘れずに!
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'screen_name',
'name',
'profile_image',
'email',
'password'
];
// 省略
}
Tweets
TweetテーブルではSoftDeleteという論理削除(削除してもDBには残るがシステム上削除したデータとして扱う機能)を使える様に設定します。
この時Migrationで $table->softDeletes
を設定しておかないと動作しないので注意!
ついでに登録/更新を許可するために $fillable
は user_id
と text
カラムだけ許可しておきます。
$fillableはLaravelで用意されているメンバ変数です。
$fillableにカラム名を定義するとそれ以外のカラムを登録/更新でエラーを吐きます。
つまりホワイトリストですね。
逆に$guardedというのはブラックリストで登録/更新できないカラムを指定します。
基本的にはどちらでも可です!
<?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 = [
'user_id',
'text'
];
}
Comments
こちらも Tweet
と同じ要領です。
<?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 = [
'user_id',
'tweet_id',
'text'
];
}
Followers
Followerテーブルはincrementを使用しないという設定とprimary_keyを指定する設定を合わせて記述します。
<?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;
}
リレーションの親子関係
Laravelでは関連するテーブルをSQLを意識せずに操作がしやすい様にEloquentという機能が用意されています。
それらを利用する為にModelに関連テーブルとの関係を定義します。
詳しく知りたければ日本語ドキュメントを見よう(投げやり)
Laravel 6.x Eloquent:リレーション
Users
// 省略
public function tweets()
{
return $this->hasMany(Tweet::class);
}
public function followings()
{
return $this->belongsToMany(self::class, 'followers', 'followed_id', 'following_id');
}
public function followed()
{
return $this->belongsToMany(self::class, 'followers', 'following_id', 'followed_id');
}
Tweets
// 省略
public function user()
{
return $this->belongsTo(User::class);
}
public function favorites()
{
return $this->belongsToMany(User::class, 'favorites');
}
public function comments()
{
return $this->hasMany(Comment::class);
}
favorites()は中間テーブルを扱う時(いいね付けたり外したり)に使用します。
Comments
// 省略
public function users()
{
return $this->hasMany(User::class);
}
public function tweets()
{
return $this->hasMany(Tweet::class);
}
Migration実行
以下のコマンドで先ほど作成したMigrationファイルを実行してDBにテーブルを作成します!
php artisan migrate
これでDBは用意できました!第1回はとりあえずここまで!