Laravelコマンド メモ

マイグレーション周り

マイグレーションファイル作成

$ php artisan make:migration create_posts_table --create=posts

年月日時分秒_create_posts_table.phpといったファイルが作成される。--create=postsオプションにより、そのファイルはpostsテーブルを作成(create)する内容になる。


カラム追加

$ php artisan make:migration add_user_id_to_posts_table --table=posts

user_idカラムをpostsテーブルに追加。--table=postsでテーブル名指定。


マイグレーションファイル書くときの注意点

nullable()unique()unsigned()onDelete('cascade')

<?php
public function up()
    {
        Schema::create('tweets', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('user_id')->unsigned();
            $table->text('body');
            $table->boolean('not_published');
            $table->timestamps();

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

モデル周り

モデル作成

$ php artisan make:model Post

ストロングパラメーターの指定

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tweet extends Model
{
    protected $fillable = [
        'body',
    ];
}

Railsでいうbelongs_to :user(関連テーブル参照のメソッドを作成する)

Post.php

<?php
use Illuminate\Database\Eloquent\Relations\BelongsTo;

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

上記の定義により、以下のメソッドが使えるようになる。

<?php
$post->user;        
 //=> Userモデルのインスタンスが返る
$article->user->name; 
//=> Userモデルのインスタンスのnameプロパティの値が返る
$article->user->hoge(); 
//=> Userモデルのインスタンスのhogeメソッドの戻り値が返る
$article->user();
//=> BelongsToクラスのインスタンスが返る

belongsToMany(中間テーブルの場合)(Railsと少し違う)

Post.php

<?php
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
public function likes() : BelongsToMany
    {
        return $this->belongsToMany('App\User', 'likes')->withTimestamps();
    }
}

->withTimestamps()がいる。

上記の定義により、以下のメソッドが使えるようになる。

<?php

$post->like
//=>Userオブジェクトが詰まったcollectionが返る。
// =>
// illuminate\Database\Eloquent\Collection{#3306
//     all : [App\User{#3391...id:1,..}, App\User{#3392.....}, App\User{.....}]
// } 

$post->like()
//=> BelongsToManyが返る

つまり、User--< Like >--Postの関係の場合Post.phpは以下になる。

Post.php

<?php
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
    protected $fillable = [
        'title',
        'body'
    ];
    
    public function user(): BelongsTo
    {
        return $this->belongsTo('App\User');
    }

    public function likes():BelongsToMany
    {
        return $this->belongsToMany('App\User', 'likes')->withTimestamps();
    }
}

ルーティング周り

ルーティングチェック

$ docker-compose exec workspace php artisan route:list

ルーティング例

<?php
//resourceではnameは自動でつく
//prefixで/sns/tweetsになる
//namespaceでSnsディレクトリのTweetコントローラーを参照する
//middleware('auth')はrequire_login
Route::prefix('sns')
->namespace('Sns')
->middleware('auth')
->group(function () {
    Route::resource('tweets', 'TweetController');
});
<?php

//only使う場合
Route::resource('hoge', 'HogeController', ['only' => ['index', 'create', 'edit', 'store', 'destroy']]);

//resource使わない場合
Route::prefix('sns')
     ->namespace('Sns')
     ->middleware('auth')
     ->group(function () {
         Route::get('tweets/{tweet}', 'TweetController@show')->name('sns.tweets.show');
         Route::post('tweets', 'TweetController@store')->name('sns.tweets.store');
         Route::get('tweets/{tweet}/edit', 'TweetController@edit')->name('sns.tweets.edit');
     });


//お手軽実験用にこんな書き方も
Route::get('/welcome', function(){
    return view('welcome');
});

コントローラー周り

コマンド

$ php artisan make:controller PostController -r

rはresourceの略。

削除機能

<?php
//本当はこう

public function destroy($id)
    {
        $tweet = Tweet::find($id);
        $tweet->delete();
        return redirect()->route('tweets.index');
    }
<?php
//でもこう書く(Dependency Injectionとかいうやつ)

public function destroy(Tweet $tweet)
    {
        //$tweet = Tweet::find($id);
        //$tweetインスタンスが使える
        $tweet->delete();
        return redirect()->route('tweets.index');
    }

URL直打ち対策のためポリシー設定しろ(後述)

ポリシー<->〇〇Requestにバリデーション処理をかくときに設定するauthorize()メソッドとは何が違うん?

Laravel では認可のために以下の仕組みが用意されています。

  • Gate
  • Policy
  • Request -> authorize()

参考:Laravel の "認可" まとめ(Gate, Policy 等)

思ったけどUserプロフィールを変更するURLを/mypage/acountみたいにしてたらURL直打ちの心配はないか。。逆に心配しなかんのはTweetの編集とかやろ、PATCHリクエスtweets/3みたいになるから。

引数の Request $request をTweetRequest $request にするやつ忘れるな(= バリデーション?)

ストロングパラメーターをモデルに書くの忘れるな(前述:モデルのとこ)

require_login忘れるな(ルーティングに->middleware('auth')書くだけ)

バリデーション

〇〇Requestを作成しそこに書く(フォームリクエストというらしい)

$ php artisan make:request ArticleRequest

<?php

//認可機能。ポリシーで認可機能を設定する場合はここはとりあえずtrueにしとく
public function authorize()
    {
        return true; 
    }

//ここがバリデーション
public function rules()
    {
        return [
            'title' => 'required|max:50',
            'body' => 'required|max:500',
        ];
    }
    
//バリデーションエラーメッセージに表示される項目名をカスタマイズできる(一般はresources/lang/ja/validation.phpのattributesに定義。多分ここに書くのは個別具体)
public function attributes()
    {
        return [
            'title' => 'タイトル',
            'body' => '本文',
        ];
    }

コントローラーのメソッドの引数にArticleRequest $requestをセットする。基本storeupdateだけだと思われる。

require_login

ルーティングに->middleware('auth')書くだけ

ポリシー作った場合の話だが、ポリシーは例えtrueでもログインユーザー以外は弾く(引数のUser $userを?User $userにすることで対策可能)

ちなみにviewでは@auth~@endauth@guest~@endguest

URL直打ち対策(updateとdestroyで起きるやつ)(これを認可機能という)(current_user.posts.find(params[:id])(❌Post.find(params[:id])))

<?php

$article->user_id = $request->user()->id;

//Railsだとcreateアクションは下記
//current_user.posts.build(params[:id]

※ ログイン済みのユーザーが送信したリクエストであれば、Laravelではリクエストのuserメソッドを使うことでUserクラスのインスタンスにアクセスできるので、そこからユーザーのidを取得し、これもArticleモデルのインスタンスのuser_idプロパティに代入しています。

※ 間違えた。。これ単なるstoreアクションのやつや。。。URL直打ちの被害が出るのはupdatedestroyや(他人が勝手に編集したり削除したりできるようになるやつ)

updatedestroyのURL直打ち対策には、ポリシーを使う。
$ php artisan make:policy ArticlePolicy --model=Article

※ ちなみにviewでは@if (Auth::id() === $tweet->user_id)

ストロングパラメーター(params.require(:user).permit(:name, :email, :password))

上述(モデル)

フラッシュメッセージ(ログインができました)

エラーメッセージ(タイトルを入力してください)

views/error_card_list.blade.php

@if ($errors->any())
  <div class="card-text text-left alert alert-danger">
    <ul class="mb-0">
      @foreach($errors->all() as $error)
        <li>{{ $error }}</li>
      @endforeach
    </ul>
  </div>
@endif

それぞれのviewに@include('error_card_list')書く

日本語にする。laravel/resources/langディレクトリにjaディレクトリを作成し、そこにvalidation.phpを作成し、公式ドキュメントからメッセージコピペ。

laravel/config/app.phpの設置を'locale' => 'ja',にする

アラートメッセージ(本当に削除しますか?)

N+1問題解決メソッド

def index
  @posts = Post.all
end

<% @posts.each do |post| %>
# ここでN+1が発生
  <%= post.user %>
<% end %>

N+1について(自分の記事)

Railsでは@posts = Post.includes(:user)で解決

Laravelではwith('users')メソッドを使う

<?php

// $posts = Post::all(); 
$posts = Post::with('users')->get();
foreach($posts as $post) {
    $post->user;  
}

view周り