YuHo のブログ

Laravel, PHP, JavaScript

Laravel 5.6 でブログサイトを制作する2 #10 Redirect を高機能にする

今回は既存アプリの PostController と CommentController の Redirect をより高機能にします。

開発

PostController

もしログインユーザーが Post の update を認可されるなら、Redirect はユーザーをその Post のページへ送るようにします。
これまではトップページへ飛ばすようになっていました。
逆に Post の update が許可されないなら、ユーザーをフォーム入力情報付きで前回のページへ飛ばします。
上の2つは意味がほぼ同じですが、学習のために2通りの方法で実現してみました。
destroy のほうもほぼ同じ要領で開発します。

[blog/app/Http/Controllers/PostController.php]

<?php

public function update(Request $request, Post $post)
{
    if ($request->user()->can('update', $post)) {
        $post->title = $request->input('title');
        $post->content = $request->input('content');
        $post->save();

        return redirect()->route('posts.show', ['post' => $post]);
    } else {
        return back()->withInput();
    }
}

public function destroy(Request $request, Post $post)
{
    if ($request->user()->can('delete', $post)) {

        return redirect()->route('users.show', ['user' => $post->user_id]);
    } else {
        return back();
    }
}

CommentController

CommentController の開発は PostController のそれとほぼ同じ手順です。

[blog/app/Http/Controllers/CommentController.php]

<?php

public function update(Request $request, Comment $comment)
{
    if ($request->user()->can('update', $comment)) {

        $comment->content = $request->input('content');

        $request->user()->comments()->save($comment);

        return redirect()->route('posts.show', ['post' => $comment->post_id]);
    } else {
        return back()->withInput();
    }
}

public function destroy(Comment $comment)
{
    if ($request->user()->can('delete', $comment)) {
        $comment->delete();

        return redirect()->route('posts.show', ['post' => $comment->post_id]);
    } else {
        return back();
    }
}

解説

ユーザーを直前の位置に送る

  1. back() メソッドを利用する
  2. それをリターンする
<?php

return back();

ユーザーを名称 Route に送る

  1. 引数を持たない redirect() メソッドに route() メソッドを鎖付けする
  2. route() の第一引数は Route の名称
  3. 第二引数に Route パラメータを渡すこともできる
<?php

return redirect()->route('home');
// return redirect()->route('users.show', ['user' => $id]);

名称 Route のパラメータにモデルの id を入れる

  1. route() メソッドの第二引数にモデルのインスタンスを渡す
  2. モデルのインスタンスから id プロパティが自動的に引き出される
<?php
return redirect()->route('users.show', [$user]);

Laravel 5.6 でブログサイトを制作する2 #9 EnterController を作成する

開発

今回はクロージャとして定義してあった enter Route の処理を Controller に移します。
この Route の役割は、ランダムなユーザー・アカウントを1つ選んで、そこにログインすることです。

Controller ファイルを作成する

まずは Controller ファイルを作成します。

php artisan make:controller EnterController

Route ファイルを編集する

次に Route ファイルを編集します。
名称が "enter" である既存の Route のクロージャ部分を直前に作成した EnterController クラスに変更します。
EnterController はただ1つのメソッドを持つコントローラーとする予定なので、クラス名の後にメソッド名を記述する必要はありません。

[blog/routes/web.php]

<?php

Route::get('enter', 'EnterController')->name('enter');

Controller ファイルを記述する

最後に Controller ファイルを編集します。
EnterController はただ1つのメソッドを持つ Controller とします。
そのような場合には __invoke() メソッドを利用します。
Controller の __invoke() メソッドは Route から指定する際に、@??? の部分が必要ありません。
そのため、Route の記述が少しだけ簡潔になります。

[app/Http/Controller/EnterController.php]

<?php

public function __invoke()
{
    $user = App\User::find(1);

    Auth::login($user, true);

    return redirect('/');
}

解説

Controller ファイルを作成する

  1. プロジェクトのフォルダに移動する
  2. php artisan コマンドを実行する
php artisan make:controller MyController

Controller ファイルを編集する(ただ1つのメソッドを持つ Controller を作成する)

  1. 作成した Controller ファイルに __invoke() メソッドを定義する
    1. Route ファイルからそのメソッドを指定する際には @XXX の部分は記述する必要がない。
<?php

public function __invoke($id)
{
    return view('my.template', ['user' => User::findOrFail($id)]);
}

Laravel 5.6 でブログサイトを制作する2 #8 サブドメイン用の Route を作成する

今回は、既存の blog アプリにサブドメイン用の Route を付け足します。
現在作成中のサイトのドメインは "blog.test" となっているはずです。
サブドメインとは "sub-domain.blog.test" のような物のことです。
このアプリでは上記の "sub-domain" の部分には各ユーザーのユーザー名(アカウント名)を受け付けることにします。
ユーザー名(アカウント名)とは、ここでは、各ユーザーに1つずつ割り当てられる独自の文字列を意味します。
"User001" のような物のことです。
Twitter では "@User001" などとなっている部分のことです。

開発

サブドメインを Route 処理できるようにする

まずは web.phpサブドメインを受け付けるための Route を作成します。

[blog/routes/web.php]

<?php

Route::domain('{userName}.blog.test')->group(function () {
    Route::get('/', 'UserController@show');
});

Route パラメータに制約を付ける

次にサブドメイン用の Route の Route パラメータに制約を付けておきます。
ここでは、「アルファベット1文字から始まり、それにアルファベットまたは数字が0個以上続く物」という制約にします。
これはなくてもいいですが、あるほうが少しだけいいです。

[blog/app/Providers/RouteServiceProvider.php]

<?php

public function boot()
{
    Route::pattern('userName', '[a-zA-Z][a-zA-Z0-9]*');

    parent::boot();
}

パラメータとモデルを明示的に結びつける

次にサブドメインの Route パラメータと User モデルを明示的に結びつけます。
サブドメインのパラメータは今回は "userName" となっているため、暗黙に結びつけるなら、ユーザーコントローラーの使用するメソッドの対応する引数を "App\User userName" とする必要があり、ややこしいため、こうします。

[blog/app/Providers/RouteServiceProvider.php]

<?php

public function boot()
{
    Route::pattern('userName', '[a-zA-Z][a-zA-Z0-9]*');

    parent::boot();

    Route::model('userName', App\User::class);
}

パラメータからモデルを取り出す方法を指定する

最後に、パラメータからモデルを取り出す方法を指定します。
既定の方法だと、パラメータと対応するモデルのテーブルの id カラムを比べてしまいます。
今回は、パラメータと対応するモデルの userName カラムを比べます。

[blog/app/Providers/RouteServiceProvider.php]

<?php

public function boot()
{
    Route::pattern('userName', '[a-zA-Z][a-zA-Z0-9]*');

    parent::boot();

    Route::model('userName', App\User::class);
    Route::bind('userName', function ($value) {
        return App\User::where('nameName', $value)->first() ?? abort(404);
    });
}

解説

サブドメインの Route を作成する

  1. Route::domain() メソッドを利用する
  2. 引数はサブドメインの形式を表す文字列
  3. Route::domain() メソッドに group() メソッドを鎖付けする
  4. 引数は Route を作成するためのクロージャ
[blog/routes/web.php]

<?php

Route::domain('{userName}.blog.test')->group(function () {
    Route::get('/', 'UserController@show');
});

Route パラメータに制約を付ける

  1. Route::pattern() メソッドを仕様する
  2. 第一引数が対象 Route パラメータの名称
  3. 第二引数が制約のための正規表現
  4. boot() メソッドの parent::boot の前に置く
[blog/app/Providers/RouteServiceProvider.php]

<?php

Route::pattern('userName', '[a-zA-Z][a-zA-Z0-9]*');

パラメータとモデルの結びつける

  1. Route:model() メソッドを仕様する
  2. 第一引数が対象の Route パラメータの名称
  3. 第二引数が対応するモデル
  4. boot() メソッドの parent::boot の後に置く
[blog/app/Providers/RouteServiceProvider.php]

<?php

Route::model('userName', App\User::class);

パラメータからモデルを取り出す方法を指定する

  1. Route::bind() メソッドを利用する
  2. 第一引数は対象の Route パラメータの名称
  3. 第二引数は方法を指定するクロージャ
  4. boot() メソッドの parent:;boot の後に置く
[blog/app/Providers/RouteServiceProvider.php]

<?php

Route::bind('userName', function ($value) {
    return App\User::where('nameName', $value)->first() ?? abort(404);
});

Laravel 5.6 Migration

make:migration [migration_file_name]

マイグレーションファイルを作成する。

--create=[table_name]

make:migration のオプション。マイグレーションファイルが新規にテーブルを作成することを示す。

--table=[table_name]

make:migration のオプション。マイグレーションファイルが既存のテーブルを編集することを示す。

-path=[path_location]

make:migration のオプション。マイグレーションファイルを作成する位置をアプリのルートから指定する。

migrate

マイグレーションを実行する。

--force

migrate のオプション。既存のデータベースの情報を削除してでも migrate を実行するような場合に特に利用する。

migrate:rollback

前回分の migration を巻き戻す。

--step=[N]

migrate:rollback のオプション。巻き戻す migration の数を指定する。

migrate:reset

全ての migrateion を巻き戻す。

migrate:refresh

全ての migration を巻き戻し、全ての migration を実行する。

--seed

migrate:refresh のオプション。migration を再び実行する際に、Seeder も共に実行する。

--step=[N]

migrate:refresh のオプション。巻き戻して、再実行する migrate の数を指定する。

migrate:fresh

全てのテーブルを落として、全ての migration を実行する。

--seed

migrate:fresh のオプション。migration を再実行する際に、Seeder も共に実行する。

あとがき

いずれテーブルにまとめる
migrate:rollback の1回分の意味は?
step の1つの意味は?

Laravel 5.6 でブログサイトを制作する2 #7 Seeder ファイルを書き足す

今回は Seeder ファイルを書き足して、Factory で製造する post モデルのインスタンスに user_id を、comment モデルのインスタンスに user_id, post_id を追加します。

posts テーブルに user_id カラムを追加する

今の Post モデルには user_id がないので、マイグレーションファイルを作成して、それを付け足します。

php artisan make:migration add_user_id_to_posts_table --table=posts
[blog/database/migrations/????_??_??_??????]

<?php

public function up()
{
    Schema::table('posts', function (Blueprint $table) {
        $table->integer('user_id');
    });
}

/**
     * Reverse the migrations.
     *
     * @return void
     */
public function down()
{
    Schema::table('posts', function (Blueprint $table) {
        $table->dropColumn('user_id');
    });
}

Seeder ファイルを編集する

Seeder ファイルを編集します。

PostsTableSeeder

Post モデル用の Seeder は user_id を書き込むように書き換えます。
User モデルと Post モデルの Relationship を使います。
全ての User モデルのインスタンスを取得して、各インスタンスに対して、記録を作ります。

[blog/database/seeds/PostsTableSeeder.php]

<?php

public function run()
{
    $users = App\User::all();

    foreach($users as $user) {
        $user->posts()->save(factory(App\Post::class)->make());
    }
}

CommentsTableSeeder

Comment モデル用の Seeder には user_id と post_id を書き込むように書き換えます。
user_id は直接書き込み、post_id は Comment モデルと Post モデルの関係を利用して書き込みます。

[blog/database/seeds/CommentsTableSeeder.php]

<?php

public function run()
{
    $posts = App\Post::all();
    $users = App\User::all();
    $users_count = $users->count();

    foreach($posts as $post) {
        $comment = factory(App\Comment::class)->make();

        $user = $users[rand(0, $users_count-1)];

        $comment->user_id = $user->id;

        $post->comments()->save($comment);
    }
}

Seeder ファイルを実行する

最後にマイグレートします。

composer dump-autoload
php artisan migrate:refresh --seed

Laravel 5.6 : Column, Schema, Faker, Validation

テーブルとそのカラム、それを作成するための Schema、そのデータをシードするための Faker、それを検証するための Validation の組を考えます。
思いつき次第更新していきます。

ユーザーアカウント

ウェブサイトなどのアカウント情報です。

column Schema Faker Validation
id increment - -
name string name string
account string->unique userName string
email string->unique email email
password string password string
created_at ??? - -
updated_at ??? - -

Laravel 5.6 でブログサイトを制作する2 #6 Comment と User, Post モデルを関係付ける

User と Comment の所有の関係を作成する

<?php

class User extends Authenticatable
{
    use Notifiable;

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

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
    
    public function posts()
    {
        return $this->hasMany('App\Post');
    }
    
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

Post と Comment の所有の関係を作成する

<?php

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }
    
    public function comments() {
        return $this->hasMany('App\Comment');
    }
}

Comment と User, Post の所属の関係を作成する

<?php

class Comment extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }
    
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}