タグ: Laravel

  • 【Laravel】ルートパラメータの使い方

    【Laravel】ルートパラメータの使い方

    今回はURLに変数を埋め込む「ルートパラメータ」の使い方を解説します!

    ルートパラメータの使い方

    座学よりも前に早速実践の中で使い方を見ていきましょう。

    今回はゴールとして会員登録しているあらゆるユーザーのプロフィール画面を表示することを目標にします。

    (今回は事前にユーザーのデータを10件Seederで作成してあります。)

    ダメな例

    最初にダメな例として静的な方法をご紹介して、後から理想的な方法をご紹介いたします。

    View部分を作成

    まずは実際にプロフィール画面を表示するbladeを作成します。

    ファイル名はusers.blade.phpにします。

    ファイルの中身はこんな感じにするとします(めっちゃ質素です汗)

    resources/views/users.blade.php

    Controllerの作成

    次にプロフィール表示用のControllerを作成します。

    ユーザーについてのControllerなので、ファイル名はUsersControllerにするとします。

    シェルで以下のartisanコマンドを実行してControllerを新規作成します。

    $ php artisan make:controller UsersController
    
    // 以下のようなメッセージが表示される
    INFO  Controller [app/Http/Controllers/UsersController.php] created successfully.

    「プロフィール画面を表示する」ので、メソッド名はprofileにします。

    最後にbladeを表示したいのでviewを返すようにします。

    ここまで以下。

    app/Http/Controllers/UsersController.php

    Modelの取得

    まずはID番号が1のユーザーのプロフィールを表示したいのでModel経由でID番号が1のModelを取得してViewに渡します。

    app/Http/Controllers/UsersController.php

    routesの編集

    最後にこれを表示するためにroutesを作成していきます。

    /users/1でID番号が1のユーザーのプロフィール画面を表示することにします。

    routes/web.php

    実際に見てみる

    準備ができたので、それでは実際にlocalhost/users/1にアクセスしてみましょう!

    以下のような画面が表示されていると思います。

    localhost/users/1

    1人目のプロフィール画面は表示することができました。

    問題点

    それでは2人目、3人目、アプリケーションのユーザーが増えて10000人目のユーザーはどうやって表示したらいいでしょうか??

    web.phpに/users/1から/users/10000まで10000行のコードを書くのは馬鹿げていますし、ユーザーが増えたら都度web.phpを更新する作業も面倒なのは自明です。

    こんなときは「ルートパラメータ」を使いましょう!

    理想の例

    要はURLの最後の1の部分だけが変わって、それ以外の処理は基本的に共通なので、ID番号の部分(今回の例で言うと1の部分)だけ変更しましょう。

    (やっと本題に入ります笑、たくさん勉強してLaravelスキルを身につけましょう!)

    routesにパラメータを追加

    まずはweb.phpのroutesを編集します。

    以下の画像にある通り、今まで/users/1だったURLを/users/{id}に変更しました。

    (※最低限の理解のためにルートモデルバインディングは今回はしません。)

    routes/web.php

    UsersControllerも編集します。

    メソッドに引数を追加

    以下の画像のように、profileメソッドに先ほどweb.phpに追加したURLと同じidという名前の変数を引数に取ります。

    app/Http/Controllers/UsersController.php

    この$idの中身は何なのでしょうか??

    中身を確かめるためにLaravelの開発でお馴染みのddメソッドを以下の場所に追加して、$idの中身を確認してみましょう。

    app/Http/Controllers/UsersController.php

    この状態で再度localhost/users/1にアクセスしてみましょう。

    localhost/users/1

    画面には「1」と表示されました。

    次はURLにlocalhost/users/2と入力してみると、どうなるでしょうか?

    localhost/users/2

    今度は画面に「2」と表示されました。

    察しが良い方はお気づきかもですが、ddメソッドにはlocalhost/users/xxxの「xxx」の部分に入れた文字が表示されています。

    localhost/users/3と入力すれば「3」と表示されますし、localhost/users/100と入力すれば「100」と表示されます。

    (ちなみにlocalhost/users/helloと入力すれば「hello」と表示されますし、localhost/users/お疲れ様ですと入力すると「お疲れ様です」と表示されます笑)

    ルートパラメータの正体

    これはどういうことかと言うと、web.phpで/users/{id}と入力したと思いますが、この{id}の部分はURLの/users/xxxの「xxx」の部分が反映されています。

    xxx」の値はそのままprofile($id)$idの部分に渡されて、今回はそれをそのままddメソッドで出力していました。

    つまりlocalhost/users/xxxと入力すると、profile($id)$idには「xxx」が入るので、ここからはそれを使って取得するユーザーを自在に変えていきましょう!

    以下の画像のように、$idを使って取得するユーザーを変更しましょう。

    app/Http/Controllers/UsersController.php

    この状態でlocalhost/users/1を表示してみてください。

    localhost/users/1

    次はコードを何も変えずに、localhost/users/2を表示してみましょう。

    localhost/users/2

    この状態にすればID番号が3だろうが4だろうが、10でも1000でも、なんでも表示することができます。(厳密にいえば、localhost/users/xxxの「xxx」が数字だったらですが)

    冒頭にあった以下の問題も解決することができます!

    それでは2人目、3人目、アプリケーションのユーザーが増えて10000人目のユーザーはどうやって表示したらいいでしょうか??

    web.phpに/users/1から/users/10000まで10000行のコードを書くのは馬鹿げていますし、ユーザーが増えたら都度web.phpを更新する作業も面倒なのは自明です。

    これで皆さんも便利はLaravelライフを過ごしてくださいっ!!!!

  • 【Laravel】Factoryで例外を作るやつ

    【Laravel】Factoryで例外を作るやつ

    Laravelで一部、こっちが指定した情報でデータを作成してほしいことがあったので、Factoryに例外を作成する方法をご紹介します。

    今回は「メール確認が終了していないユーザーを作成する」という趣旨で作成します。

    Factoryを修正

    Factoryファイルのdefinitionメソッドを普通に埋めた後に、新しくメソッドを作成しましょう。

    名前は分かり易ければなんでもOKです👍

    public function email_not_verified()
    {
        return $this->state(function (array $attributes) {
            return [
                'email_verified_at' => null
            ];
        });
    }

    例外のFactoryを実行

    作成した例外のFactoryを実行しましょう。

    通常のFactoryは以下のコマンドでSeederから実行します。

    // 複数個作成するときはnのその数を入力する(1個のときは空でOK)。
    User::factory(n)->create();

    これを先ほど作成した例外バージョンで実行する方法は以下です。

    User::factory()->email_not_verified()->create();

    factoryメソッドを呼んで、createメソッドで実際に作成する前に、例外のメソッド(今回で言うとemail_not_verifiedメソッド)を間に噛ませてあげるとその例外が作成できます👍

    上記を書いたあとはSeederを実行してください。(下記の例だとDBのレコードは全部リセットされます)

    php artisan migrate:refresh --seeder

    通常も例外も両方作成する

    必ずしもどちらか一方しか実行できないわけではなくて、もちろん両方とも同時に実行できます。

    何を言っているかと言うのは、以下のコードを見たら分かると思います。

    <?php
    
    namespace Database\Seeders;
    use App\Models\User;
    use Illuminate\Database\Seeder;
    
    class UserSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
        */
        public function run()
        {
            User::factory(10)->create();
            User::factory()->email_not_verified()->create();
        }
    }

    これをすると、通常のユーザーが10個と、メール確認が終了していないユーザーが1個作成されます。

  • LaravelでSeeder+Factoryの使い方

    LaravelでSeeder+Factoryの使い方

    Laravelで開発していて、SeederとFactoryの使い方でこんがらがったので、整理しました👍

    用意するもの

    以下は新しく作成するときに必要なファイルです。

    • modelファイル
    • migrationファイル
    • Seederファイル
    • Factoryファイル

    簡単に用意する方法

    上記4枚のファイルをartisanを使って簡単に作成する方法があります。

    php artisan make:model Sample -msf

    artisanでmodelを作成します。

    「-」でオプションを付与できるのでmigrationのm、Seederのs、Factoryのfを付与します。

    書く内容

    それぞれのファイルに書く内容は以下です。

    modelファイル

    $fillableを作成した方がいいです👍

    migrationファイル

    テーブルの定義を書く。

    Seederファイル

    実行するFactoryファイルを追加する。

    Factoryファイル

    実際に作成したいテストのデータを書く。

    fakerを使用するのが一般的と思われる。

    DatabaseSeederファイル

    上記の内容に加えて、Seederを司る大元のファイルであるDatabaseSeederに今回で作成したSeederファイルを登録しましょう。

    <?php
    namespace Database\Seeders;
    use Illuminate\Database\Seeder;
    class DatabaseSeeder extends Seeder
    {
        /**
         * Seed the application's database.
         *
         * @return void
         */
        public function run()
        {
            $this->call([
                SampleSeeder::class
            ]);
        }
    }

    実行方法

    migrationファイルでデータを格納する受け皿を用意しました。

    Modelを作成して、生成するデータの雛形を作成しました。

    Factoryファイルで雛形を具体的に書きました。

    Seederファイルでその雛形を実際に作成します。

    以下のコマンドを実行してください。

    php artisan migrate:refresh --seed
  • macOSでLaravel Sailをインストールする方法

    macOSでLaravel Sailをインストールする方法

    macOSで以下のコマンドを実行すると、Docker上で起動するLaravelアプリがインストールされる

    curl -s "https://laravel.build/example-app" | bash

    コマンドないの「example-app」の箇所を任意のアプリ名に書き換える。

  • Requestコントローラのauthorizeメソッドとrulesメソッド

    Requestコントローラのauthorizeメソッドとrulesメソッド

    Formのバリデーションを管理するのに便利なRequestコントローラなるものがある。

    今回はそれを扱う。

    Requestコントローラ

    以下のartisanコマンドを実行すると、form requestを作成することが出来る。

    php artisan make:request StorePostRequest

    このコマンドを実行すると、StorePostRequest.phpファイルがapp/Http/Requestsディレクトリに作成される。

    ※app/Http/Requestsディレクトリがない場合は、make:requestを実行した時点で自動的に生成してくれる。

    このRequest.phpファイルには、あらかじめauthorizeメソッドとrulesメソッドが用意されている。

    先に説明だけすると、authorizeメソッドはリクエストを送ったユーザーがリクエストを実行する権利を持っているかを判断して、rulesメソッドはデータに適用する検証ルールを返す。

    authrizeメソッド

    どんなユーザーがリクエストを実行できるかを判断する。

    デフォルトではreturn falseのみ書かれていて、誰もリクエストできない状態になっている。

    ここをreturn trueにしたり、他の処理を加えて、誰ならリクエストしていいかを制御できる。

    rulesメソッド

    rulesメソッドではルールの配列を返す必要がある。今までcontrollerに書いていたバリデーションをこのRequestコントローラに別で書くことができる。

    使い方

    これらの使い方は、バリデーションが必要なリクエストがきた時に引数に加える。

    以下がサンプル。

    use App\Http\Requests\StorePostRequest;
    
    public function store(StorePostRequest $request)
    {
        // ここに処理を書く。
        // 引数にStorePostRequestを書いてるから、ここに処理が走ってる時点でバリデーションをクリアしてる
    }

    ちなみに、これらのバリデーションにひっかかったときは、controllerは自動的に前の画面に遷移してくれるから便利。

    他のページに遷移させたいときはredirect()を使って、別途処理を書く必要がある。

    またmessagesメソッドを加えて、エラーのときのフラッシュの文言を追加することが出来る。

    結論、めちゃ便利。

  • Laravelのroutesのas、prefix、namespaceについて

    Laravelのroutesのas、prefix、namespaceについて

    Laravelのroutesにはasとprefix、そしてnamespaceがある。

    初学者の僕にとっては、それぞれ機能が似ているから少し整理したいと思う。

    namespaceについて

    namespaceはpath内のcontrollerがある場所を省略orまとめることができる。

    以下が例。

    // 登録画面
    Route::get('/register', [Auth\RegisterController::class, 'index'])->name('register');
    Route::post('/register', [Auth\RegisterController::class, 'new_user']);

    このように新規登録周りについて、上記のようなroutesが書かれている。

    これらroutesはcontrollerに共通して全てAuth\というコードが書かれている。

    これらをまとめることができるのがnamespaceである。

    以下が修正した例。

    // 登録画面
    Route::group(['namespace' => 'Auth'], function () {
        Route::get('/register', [RegisterController::class, 'index'])->name('register');
        Route::post('/register', [RegisterController::class, 'new_user']);
    });

    上記のように、controllerのパスからAuthが抜けてスッキリした。

    prefixについて

    routesをまとめるのに、他にprefixがある。

    prefixは以下。

    Route::get('/main/list','MainController@list')->name('main.list');
    Route::get('/main/edit','MainController@edit')->name('main.edit');
    Route::get('/main/search','MainController@search')->name('main.search');

    これらはroute名に共通してmainと入っている。

    これを省略できるのがprefix。

    修正した例が以下。

    Route::group(['prefix' => 'main'], function () {
        Route::get('list','MainController@list')->name('main.list');
        Route::get('edit','MainController@edit')->name('main.edit');
        Route::get('search','MainController@search')->name('main.search');
    });

    上記のようにrouteのuriから共通した/mainが省略できた。

    asについて

    最後にasを解説する。

    asはrouteのnameの共通部分を省略することができる。

    Route::get('list','MainController@list')->name('main.list');
    Route::get('edit','MainController@edit')->name('main.edit');
    Route::get('search','MainController@search')->name('main.search');

    これをasを使ってまとめると以下。

    Route::group(['as' => 'main'], function () {
        Route::get('list','MainController@list')->name('list');
        Route::get('edit','MainController@edit')->name('edit');
        Route::get('search','MainController@search')->name('search');
    });

    上記の例のように、asを用いるとname()内の共通項をまとめることができる。

  • LaravelのFormでmethodを変える方法

    LaravelのFormでmethodを変える方法

    表題の通り。

    HTTPメソッドは本来11種類あって、用途に応じてこれらを使い分ける。

    しかしHTMLのformタグでは「get」「post」の2種類のメソッドしか取り扱っていない。

    デフォルト以外のでやりとりするために、Laravelでは(?、他でも使えるかも)以下の方法でメソッドを変更することができる。

    Laravel固有の方法

    Laravel固有のタグ(?)を使う方法。

    多くの教材はこちらの方法を紹介してると思われる。

    書き方を以下に示す。

    <form action="/somewhere" method="post">
        @csrf
        @method('DELETE')
        <input type="text" name="name" id="">
        <input type="email" name="email" id="">
        <input type="password" name="password" id="">
        <button type="submit">送信する</button>
    </form>

    3行目の「@method(‘DELETE’)」のようにメソッドを@methodのあとに書くと、formタグのmethod=”xxx”を無視して、そちらが優先される。

    inputタグを使う

    自分が対応した仕事では上記の方法では何故か期待通りに動作しなかった。

    そのときに知った方法。

    Laravel以外でも動作するのかは未確認。

    <form action="/somewhere" method="post">
        @csrf
        <input type="text" name="name" id="">
        <input type="email" name="email" id="">
        <input type="password" name="password" id="">
        <button type="submit">送信する</button>
        <input type="hidden" name="_method" value="delete">
    </form>

    下から2行目にtype=”hidden”のinputタグが追加されていうのが分かるだろうか。

    この書き方でも大丈夫らしい👍

  • 【Laravel】EventとListenerについて

    【Laravel】EventとListenerについて

    仕事で既存の機能に新しい機能を追加しようと思っていた時に、既存の機能に「Event」などとこうものがあった。

    初めて聞いた用語だったので、どのようなものなのか調べてみた。

    Eventに対してListener

    僕が見たコードはEventしかなかったが、実はこのEventというのはListenerというヤツとセットになっている。

    一心同体である。

    簡単に言うと、Eventはイベント発火の「合図」の役割を担っている。

    それに対して、Listenerはその合図を実際に感知してイベントを「実行」する役割を担っている。

    Eventの長所の1つは、1つのEventが複数のListenerを実行させることができる点である。

    それを紐づけるEventServiceProvider

    先ほど紹介したEventとListenerは勝手に連動してイベントを実行するわけではない。

    これらは今紹介している「EventServiceProvider」によって紐づけられ、特定にEventには特定の Listenerが実行されるようになっている。

    Eventの書き方

    通常Eventはapp/Eventsディレクトリに書く。

    デフォルトの状態ではEventsディレクトリは存在しないかも知れないが、Atrisanコマンドによって簡単に生成できる。

    <?php
    use App\User;
    // 以下略
    
    class UserRegistered
    {
        use Dispatchable, InteractsWithSockets, SerializesModels;
        public $user;
        public function __construct(User $user)
        {
            $this->user = $user;
        }
    }

    正直よく分からん。

    Listenerの書き方

    一方でListenerは通常app/Listenersディレクトリに書く。

    artisanによって生成されるListenerファイルはhandleメソッドがデフォルトで用意されている。

    このhandleメソッドにはEventによって実際に実行される内容を書く。

    <?php
    namespace App\Listeners;
    
    use App\Events\UserRegistered;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    
    use Mail;
    use App\Mail\WelcomeMail;
    
    class SendWelcomeEmail
    {
        public function __contruct()
        {
            //
        }
    
        public function handle(UserRegistered $event)
        {
            Mail::to($event->user->email)->send(new WelcomeMail());
        }
    }

    EventServiceProviderに書き方

    EventServiceProviderファイルはApp\Providersディレクトリに書く。

    ここにlistenプロパティに、実際に紐づけたEventとListenerを書いていく。

    <?php
    namespace App\Providers;
    
    use App\Events\UserRegistered;
    use App\Events\SendWelcomeEmail;
    use App\Events\RegisterMailMagazine;
    
    class EventServideProvider extends ServiceProvider
    {
        protected $listen = [
            UserRegisterd::class => [
                SendWelcomeEmail::class,
                RegisterMailMagazine::class,
            ],
            // 続く
        ];
    
        public function boot()
        {
            parent::boot();
            //
        }
    }

    Eventを発火させる

    • Event(発火)
    • Listener(実行)
    • EventServiceProvider(紐付け)

    の3つを完了したから、あとは最初の発火をさせるコードを書くだけ。

    例えばRegisterController.phpのcreateメソッドの中でeventヘルパー関数を使って、イベントを発火させるとする。

    <?php
    use App\Events\UserRegistered;
    
    protected function create(array $data)
    {
        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password'])
        ]);
    
        event(new UserRegister($user));
    
        return $user;
    }

    こんな感じでEventを実行させたいタイミングでeventヘルパー関数を用いると、Eventが発火して、Listenerが内容を実行してくれる。

  • 【Laravel】migrationでtableを削除する

    【Laravel】migrationでtableを削除する

    LaravelにはmigrationというDBを管理できる、DBのバージョン管理のようなサポート(≒機能)があります。

    これはLaravelのSchemaファサードに属しており、(Laravelが対応する)全てのDBシステムに対してtableを作成・操作するため、特定のDBに依存しないサポートを提供します。

    migrationは通常このFacadeを使用して、DBのtableとcolumnを作成・変更します。

    この機能を使えば、全てのエンジニアが同じDBを揃えることが可能になり、わざわざ手動でDBを変更する作業を行わなくてもよくなります。

    tableを削除する方法

    Laravelのmigrationでは、既存のtableを削除するときにはdropまたはdropIfExistsメソッドを使います。

    migrationを生成するときは以下の様にmake:migrationArtisanコマンドを使用してmigrationファイルを生成します。

    php artisan make:migration create_flights_table

    先ほど挙げた通り、tableの削除はdropまたはdropIfExistsなので、恐らくファイル名は以下でok。

    php artisan make:migration drop_flights_table

    migrationの構造

    migrationファイルには2つのメソッドが必要です。upメソッドとdownメソッド。

    upメソッドはDBに新しいテーブル、カラム、またはインデックスを追加するために使用します。

    downメソッドはupメソッドで実行したのと逆の操作をする。以前の状態に戻せます。

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    class CraeteFlightsTable extends Migration
    {
        /**
         * マイグレーションの実行
         *
         * @return void
         */
        public function up()
        {
            Schema::create('flights', function (Blueprint $table) {
                $table->id();
                $table->string('name');
                $table->string('flights');
                $table->timestamp();
            });
        }
    
        /** マイグレーションを戻す
         *
         * @return void()
         */
        public function down()
        {
            Schema::drop('flights');
        }
    }

    migrationの実行

    以下のコマンドを実行すると、未処理のmigrationファイルをすべて実行することができます。

    php artisan migrate

    どのmigrationファイルが実行されているかを確認するには以下のコマンドを実行で確認できます。

    php artisan migrate:status
  • LaravelのMass Assignmentとは

    LaravelのMass Assignmentとは

    Mass Assignment(マスアサインメント)とは

    コンピュータ脆弱性の1つ。本来権限を持っていないユーザー(や悪意ある第三者)によってデータ項目を変更されること。

    通常はWebサイトにあるフォームからデータをやりとりすることを想定しているが、(例えば開発者ツールなどを使って)そのフォームのname属性を改変し(例えばname=’password’などとし)それをフォームから送信するとする。もしバックエンドで属性をvalidateせずに保存するような処理が書かれているとすると、DBに同じ名前のカラムが存在したときにそれを予期せぬ方法で上書きできてしまう。

    一行だけで新しいモデルを保存するには、createメソッドを利用できます。挿入されたモデルインスタンスが、メソッドから返されます。しかし、これを利用する前に、Eloquentモデルはデフォルトで複数代入から保護されているため、モデルへfillableguarded属性のどちらかを設定する必要があります。
    複数代入の脆弱性はリクエストを通じて予期しないHTTPパラメーターが送られた時に起き、そのパラメーターはデータベースのカラムを予期しないように変更できてしまうでしょう。たとえば悪意あるユーザーがHTTPパラメーターでis_adminパラメーターを送り、それがモデルのcreateメソッドに対して渡されると、そのユーザーは自分自身を管理者(administrator)に昇格できるのです。

    Laravel 7.x Eloquent:利用の開始 > 複数代入

    DBやテーブルは一般に分かりやすい名前のものが多いので、攻撃者による攻撃に的になり得る。

    Mass Assignmentはこの様な脆弱性、またはそれを利用した攻撃のことを指す。

    Laravelでマスアサインメントを阻止する

    Eloquentモデルを使ってcreateするとき、なにも設定しないとMassAssignmentExceptionというエラーに遭遇する。

    それを解消する2つの方法

    • ブラックリスト
    • ホワイトリスト

    ブラックリスト

    以下の様に、指定した要素をguardすることでそれ以外の要素をcreateに渡すことができる。

    class User extends Eloquent {
        protected $guarded = array('id', 'year');
    }

    上記の様に記述すればcreateからidとyear以外の要素を渡すことができる。

    idとyearをguard(=保護)している。

    ホワイトリスト

    以下の様に、指定した要素をfillableにして許可を与えることで、その要素のみをcreateから渡すkとができる。

    class User extends Eloquent {
        protected $fillable = array('name', 'age');
    }

    上記の様に書くことでcreateからnameとageの要素を変更可能(=fillable)にしている。