Laravelで始めるTwitter風(Twitterクローン)のSNSツール開発チュートリアル
概要
スクールとかの課題だったりLaravelを初めてみたいけど何を作ろうって迷ってる人向けによくあるTwitter風のWEBサイトを作ってみます。
前回
- 第1回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第1回DB設計とMigration)
 - 第2回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第2回Seeder->ログイン/新規登録)
 - 第3回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第3回ユーザ関連とフォロー機能)
 - 第4回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第4回ツイートのCRUD機能)
 - 第5回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第5回ツイートのCRUD機能 編集と削除)
 - 第6回 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第6回コメントといいね機能)
 
第2回はシーディングを実行した後にログイン/新規登録までやっていきます。
前提
- PHPをある程度理解している
 - Laravelが使える環境がある
 - MVC構造をある程度理解している
 
環境
- Mac
 - Homestead
 - Laravel 5.8
 
Seeder
前回Migrationを使ってテーブルの構築とModelのリレーションを設定しました。
今回は作ったテーブルにSeederという予めLaravelに用意された機能を利用してテストデータを投入しましょう。
ターミナルで以下のコマンドを叩くとdatabase/seeds/の中にSamplesTableSeederというファイルが生成されます。
php artisan make:seeder SamplesTableSeeder
この要領で前回作成したテーブルをまとめて作成しましょう。
php artisan make:seeder UsersTableSeeder
php artisan make:seeder TweetsTableSeeder
php artisan make:seeder CommentsTableSeeder
php artisan make:seeder FavoritesTableSeeder
php artisan make:seeder FollowersTableSeeder
では実際にテストデータを投入していきます。
UsersTableSeeder
ユーザを10人ほど登録しておきたいのでforで登録を10回繰り返します。
パスワードを変更したければHash::make('12345678')の中身を変更してください。
<?php
use Illuminate\Database\Seeder;
use App\Models\User;
class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 1; $i <= 10; $i++) {
            User::create([
                'screen_name'    => 'test_user' .$i,
                'name'           => 'TEST' .$i,
                'profile_image'  => 'https://placehold.jp/50x50.png',
                'email'          => 'test' .$i .'@test.com',
                'password'       => Hash::make('12345678'),
                'remember_token' => str_random(10),
                'created_at'     => now(),
                'updated_at'     => now()
            ]);
        }
    }
}
TweetsTableSeeder
ツイート投稿も各ユーザー毎に1件登録しておきます。
<?php
use Illuminate\Database\Seeder;
use App\Models\Tweet;
class TweetsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 1; $i <= 10; $i++) {
            Tweet::create([
                'user_id'    => $i,
                'text'       => 'これはテスト投稿' .$i,
                'created_at' => now(),
                'updated_at' => now()
            ]);
        }
    }
}
CommentsTableSeeder
ユーザID1のユーザが各ユーザに1つコメントしておきます。
<?php
use Illuminate\Database\Seeder;
use App\Models\Comment;
class CommentsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 1; $i <= 10; $i++) {
            Comment::create([
                'user_id' => 1,
                'tweet_id' => $i,
                'text' => 'これはテストコメント' .$i,
                'created_at' => now(),
                'updated_at' => now()
            ]);
        }
    }
}
FavoritesTableSeeder
ユーザID1が自分を除くツイートに対して1ついいねを付ける
<?php
use Illuminate\Database\Seeder;
use App\Models\Favorite;
class FavoritesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 2; $i <= 10; $i++) {
            Favorite::create([
                'user_id' => 1,
                'tweet_id' => $i
            ]);
        }
    }
}
FollowersTableSeeder
ユーザID1を各ユーザがフォローして置く
<?php
use Illuminate\Database\Seeder;
use App\Models\Follower;
class FollowersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 2; $i <= 10; $i++) {
            Follower::create([
                'following_id' => $i,
                'followed_id' => 1
            ]);
        }
    }
}
DatabaseSeeder
ではコマンドを叩いた時に全てのSeederデータが投入されるようにDatabaseSeeder.phpというファイルに
実行するファイル名を記述しておきます。
※$this->call()の中で上から順に実行されるため以下の並びでないとテーブルの親子関係上エラーになります
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call([
            UsersTableSeeder::class,
            TweetsTableSeeder::class,
            CommentsTableSeeder::class,
            FavoritesTableSeeder::class,
            FollowersTableSeeder::class,
        ]);
    }
}
ここまで出来たら準備は完了です。
Seeder実行
Seederの実行はターミナルで以下のコマンドを叩くだけで実行できます。
php artisan db:seed
これでテストデータの登録は完了です。
ログイン/新規登録
素のPHPをやってた人はログインと新規登録の実装がどれだけ大変か分かると思います。
しかし、Laravelでは以下のコマンドを叩くだけでAuth認証の全てを実装してくれます。
php artisan make:auth
Auth認証が実装されるとroutes/web.phpに以下のコードが記述されます。
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
細かな説明は省きますがapp/Http/Controllers/Auth/のファイルを自動で読み込むようになるので
ログイン機能が使えるようになります。
右上に
LOGIONとREGISTERが追加されている
Auth認証の参考
Laravelのユーザー認証機能(Auth)の実装フロー
ログイン
では右上のLOGINというボタンを押して先ほどSeederで登録したユーザ情報を使ってログインしてみましょう。
エラーが出ました。
これはapp/User.phpをapp/Models/User.php`に移動したために起きたエラーのようです。
以下のファイルのApp\Userとなっている部分を全てApp\Models\Userという風に変更してください。
- RegisterController.php
 - auth.php
 
そして自動でファイルを呼び出すautoloadもターミナルにて更新しておきます。
composer dump-autoload
これでログインすると無事にログインできるとと思います。
では一旦右上のLogoutボタンを押して先ほどの画面に戻り、新規登録してみましょう。
こちらもエラーが出たと思います。
こちらはusersテーブルにscreen_nameカラムを追加してnullを許可しない設定にしたけど
新規登録時にその該当カラムが見つからないよ!というエラーみたいです。
※profile_imageはnullを許可しているので一旦無視します。
app/Http/Conrollers/Auth/RegisterConroller.phpを開いて以下のように編集します。
<?php
namespace App\Http\Controllers\Auth;
use App\Models\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */
    use RegistersUsers;
    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }
    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            // 追加
            'screen_name' => ['required', 'string', 'max:255', 'unique:users'],
            'name'        => ['required', 'string', 'max:255'],
            'email'       => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password'    => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\Models\User
     */
    protected function create(array $data)
    {
        return User::create([
            // 追加
            'screen_name' => $data['screen_name'],
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}
その後にresources/views/auth/register.blade.phpも同様に修正します。
@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Register') }}</div>
                <div class="card-body">
                    <form method="POST" action="{{ route('register') }}">
                        @csrf
                        <!-- 追加 -->
                        <div class="form-group row">
                            <label for="screen_name" class="col-md-4 col-form-label text-md-right">{{ __('Account Name') }}</label>
                            <div class="col-md-6">
                                <input id="screen_name" type="text" class="form-control @error('screen_name') is-invalid @enderror" name="screen_name" value="{{ old('screen_name') }}" required autocomplete="screen_name" autofocus>
                                @error('screen_name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Register') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
これで新規登録も行けたと思います。
一旦今回はここまで( ◠‿◠ )