- 投稿日:2020-04-09T21:45:36+09:00
【感想】【Laravel x Vue.js】SPAクイズアプリケーションを作ってみよう!を終えて
はじめに
今回、私が学習した教材は【Laravel x Vue.js】SPAクイズアプリケーションを作ってみよう!です。
かなりボリューム大・難易度も高めだったので、個人的な備忘録も兼ねて感想を書きます。
動機
購入したのは2月。当時は全然理解できなくて挫折。
ですが、LaravelとVue.js各々でポートフォリオ制作を終えた今、次なる挑戦はやはりLaravel+Vue一択。
スキル的にも2か月前に比べてだいぶ変わったはず。
そう思い、再びこの教材に挑戦してみました。
感想
この教材ではタイトル通りLaravel+VueでSPA開発を体験できます。(環境構築はMac)私はWindows&XAMPP(SQLite⇒MySQL)に置き換えて挑戦しました。
良かった点
①Laravel-Adminを扱っている点。以前から気になっていたのですが、これを扱う教材は初めてだったのでとても参考になりました。
②SPAかつ複数ページのサイト構築を学べる点。
以下は難しすぎて、頭を整理する際に作ったものです。笑
他でここまで教材にしてくれているのはこれくらいな気がします。⇒Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう
印象的だった点
・LaravelとVueをディレクトリを切り離して作成し、後程1つにまとめるという作り方。・Login/Register含めすべてのビューをVue側で作成。機能面をLaravel側で実装している。
難しかった点
・webpackやら環境構築。設定面は見よう見まねでしか現状はできないです。・複数ページに渡るSPA開発を1つのプロジェクトで扱う。コンポーネントファイルもたくさん作ります。
※有名なこちらの本(PHPフレームワーク Laravel実践開発)でもLaravelでのVue.js実装方法は説明されていますが、本当に導入部分の説明のみです。(コンポーネントファイル1つでの実装。Vue Routerも扱わない。)・api.phpの組み込み(というより6章以降総じて難しい。)
・Laravel-Adminの実装
活用ライブラリ・機能など
CSS
Bootstrap3、Sass、※BEM規約
JavaScript
browserSync、vue-social-sharing、Chart.js、vue-chartjs、Vue Router(グローバルビフォーガイド)、Axios、VeeValidate、vue-loading-overlay、vue-notification、
PHP
Carbon(日付計算)、Factory(自動ダミーデータ生成機能)、helpers(文字列ライブラリ)、Laravel-admin
その他
Font Awesome、SQLite(DB)、JSON Viewer、ngrok(windowsはこちらで対応、1周目は割愛した。)、Basic認証
おわりに
作りながら学べる系の教材としては、かなり難しい部類に入ると思います。僕もまだ1周終えた段階で全然理解できていません。
ですが、これを理解したら何ができるだろうかと妄想を膨らませると非常にワクワクします。笑
※Laravel-Adminは作るアプリによってはToo muchな気もしました。GUIでDB弄れれば問題ない気もします。ここら辺も踏まえて復習していきます。
引き続き頑張ります◎
- 投稿日:2020-04-09T18:26:33+09:00
Laravel blade基本
Laravelではbladeテンプレートを使用してviewを作成できます。
resources/viewsフォルダ以下にviewファイルを置き、拡張子をOOO.blade.phpにすることでbladeテンプレートとして扱えます。viewファイル読み込み
viewヘルパー関数を使うことでviewファイルの読み込みと変数の受け渡しを行うことができます。
viewヘルパー関数の第一引数にviewファイルのパスを、第二引数にviewに渡す変数を指定します。
viewファイルパスは
resources/views以下を指定し、ディレクトリは/(スラッシュ)ではなく.(ドット)で繋げ、拡張子なしのファイル名を記述します。
例えばresources/views/hoge/fuga.blade.php
を読み込む場合は以下になります。コントローラーのアクションメソッドpublic function index() { return view('hoge.fuga'); }viewファイルへの変数受け渡し
viewヘルパー関数の第二引数に連想配列を渡すことでviewファイルに変数を受け渡しすることができます。
viewファイル内では連想配列のkeyを変数名として指定できます。コントローラーのアクションメソッドpublic function index() { $data = [ 'hoge' => 'fuga', ]; return view('hoge.fuga', $data); }foge/fuga.blade.php{{ $hoge }}結果fugaまた、phpのcompact関数を使うことで簡単に変数を渡すことができます。
コントローラーのアクションメソッドpublic function index() { $hoge = 'fuga'; return view('hoge.fuga', compact('hoge')); }viewファイルで変数を使う
viewヘルパー関数で渡された変数には
マスタッシュ構文 {{ }}
内で使用可能です。viewファイル{{ $hoge }}マスタッシュ構文では変数展開時にXSS対策のため自動でHTMLエスケープが実行されます(=タグがそのまま文字列として表示される)。
動的にhtmlを作成したい場合にエスケープを無効にしたい場合は、代わりに{!! !!}
を使用してください。(中括弧は一つでok)viewファイル{!! $hoge !!}viewファイル内で
{{ }}
をそのまま使いたい場合は先頭に@をつけてください。変数展開されず出力されます。viewファイル@{{ $hoge }}viewファイルでコメントを記述する
htmlのコメント(<!-- comment -->)をそのまま記述することも可能ですが、htmlファイルとして出力されるのでユーザーから見えてしまいます。
bladeテンプレート内でのみ残るコメントにする場合は{{-- comment --}}
を使います。ディレクティブ制御構文
bladeテンプレートにはview制御を行うifやforeachが使えるディレクティブ構文が使えます。
ディレクティブ構文内では変数が使えます。
ディレクティブ構文には先頭に@を付けます。if文
@if, @elseif, @else, @endif, @unless, @isset, @empty
があります。viewファイル@if ($isActive) アクティブ @else 非アクティブ @endifviewファイル@unless ($isActive) @endunless ```lang:viewファイル @isset ($fuga) fugaが定義されていない @endissetviewファイル@empty ($array) arrayが空です @endemptySwitch文
@switch, @case, @break, @default, @endswitch
があります。viewファイル@switch($i) @case(0) 0です。 @break @case(1) 1です。 @break @default でふぉるとです @endswitch繰り返し
@foreach, @endforeach, @for, @continue, @break
が使えます。
繰り返し構文の中では$loop変数が使えます。viewファイル@foreach ($books as $book) @continue ($loop->first) @if ($loop->last) 最後 @endif {{ $book->title }} @endforeach
- 投稿日:2020-04-09T14:41:22+09:00
PHP組み込みのDateTimeクラスの定数を使えば日時フォーマットを頑張って書かなくて良い
PHPの場合はDateTimeクラスに定数が定義されているので、それを使うとdateフォーマット表現が簡単でした。
Laravel の Request Validation にも使えます。ref: https://www.php.net/manual/ja/class.datetime.php
例えば
2020-04-08T00:00:00+09:00
みたいなフォーマットを表現したいとき、
DateTime::RFC3339
で表現できます。
この定数の中にはY-m-d\TH:i:sP
が入っています。ここで最後のPが何を意味するかという情報はPHPのdate関数の項目にまとまっています。
ref: https://www.php.net/manual/ja/function.date.php一部抜粋。
O グリニッジ標準時 (GMT) との時差。時間と分の間にコロンは入りません。 例: +0200 P グリニッジ標準時 (GMT) との時差。時間と分をコロンで区切った形式 (PHP 5.1.3 で追加)。 例: +02:00 T タイムゾーンの略称 例: EST, MDT ...
- 投稿日:2020-04-09T11:00:34+09:00
Laravel触って1年経ったので過去の戒め(ソースコード)を改善してみた
Laravelのリファクタ記事にしようと思ったが、ただただ間違ったコードを直すだけの記事でした。
久々に過去練習用として作成したサイトのソースコードを覗いたら無駄な処理書いてたり、
Laravelの機能活かせれてなかったりそもそもPHP使えてなかったりと散々だったので、
いい反面教師コードだと思って改善記事(改善というか修正レベル)書いてみました。目に付いた部分しか改善しないので、一貫性はありませんし、
他の方が絶対やらないような事も多々ありますが、戒めとしてどうか生温かい目で見守ってください。環境
Laravel5.7
PHP7.1注意
改善とはいえ、これがベストとは思ってません。
あくまで出来る時間で簡単な改善(修正)を行っただけです。
その辺り、ご容赦くださいm(._.)m関係ないところは一部省略します。
例外処理等も省略します。フォームリクエストの謎の使い方
FormRequest便利ですよね。
Controllerにバリデーションを書かずに済むので良く使っています。
(他にもControllerで受け取る前の前加工とか)さて過去のソースコードを覗いてみましょう
過去コード
CreateController.phpuse App\Http\Requests\Validation; public function store(Validation $request) { //Validation Requestからルールを取得 $rules = $request->rules(); //Validation Requestからエラーメッセージを取得 $messages = $request->messages(); $data = Validator::make($request->all(), $rules, $messages); }フォームリクエストから受け取ってる時点でバリデーション処理は済んでるはずなので、色々と不要ですね。
それとValidationというクラス名だと分かりづらいので、クラス名も変更します。
改善コード
CreateController.phpuse App\Http\Requests\ProjectRequest; public function store(ProjectRequest $request) { $data = $request->all(); }ProjectRequestと命名した理由は案件登録で使用するのが一つと作成と更新で使用するのであえて
CreateProjectRequest
としませんでした。改善ポイント
- 無駄な記述を削除して本来の使い方を実装
- クラス名の役割が抽象的すぎたので、少し具体的にして明確化出来た?
fillable便利だよ
fillableもありがたいですよね〜
デフォルトで存在するUser.php見ると最初から存在するコイツですねUser.phpprotected $fillable = [ 'name', 'email', 'password', ];何が便利なのか、まずは悪い方の過去のコードを見てみましょう。
過去コード
CreateController.phpuse App\Http\Requests\ProjectRequest; public function store(ProjectRequest $request) { $project = new Project; $project->name = $request->name; $project->responsible_id = $request->responsible; $project->category_id = $request->category; $project->description = $request->description; $project->url = $request->url; $project->release_year_at = $request->release_year; $project->release_month_at = $request->release_month; $project->image_id = $image_id; $project->start_year_at = $request->start_year; $project->start_month_at = $request->start_month; $project->end_year_at = $request->end_year; $project->end_month_at = $request->end_month; $project->save(); }うーん行が無駄に多いのと保存処理のたびにこれを書くのはめんどくさいですね。
では改善コード
fillableも勿論活用しますが、そもそも保存処理をControllerでやるのはMVCとしてよろしくないのでModelに責務を分けましょう。
改善コード
ちなみに引数にモデル(というかクラス名)をタイプヒントするとLaravelがインスタンスを用意してくれます。
CreateController.phpuse App\Http\Requests\ProjectRequest; use App\Project; public function store(ProjectRequest $request, Project $project) { $results = $project->storeProject($request->all()); }ではModel
Project.phpprotected $fillable = [ 'responsible_id', 'category_id', 'image_id', 'name', 'url', 'description', 'release_year_at', 'release_month_at', 'start_year_at', 'start_month_at', 'end_year_at', 'end_month_at', ]; public function storeProject(array $data) { return $this->create($data); }保存の部分が1行で済みました!
fillable
というのは保存するときのホワイトリストなんですね〜
勿論更新の時にも適用されます!逆にブラックリストのguardedもあります
[Laravel 5.7] Eloquent Modelのfillableとguardedの違いProject.phppublic function storeProject(array $data, int $id) { return $this->where('id', $id)->update($data); }改善ポイント
- 入力内容が増えるたびに書き足す必要がなくなった
- MVCで責務をちゃんと分けた
- コード行短縮
resourceの恩恵を得ていない
Controllerを作成する時に良くみますね〜
説明は別記事に託します。Laravelのリソースコントローラを理解して使ってみよう!
ではルーティングをみてみましょう。
web.phpRoute::get('project/', 'ProjectsController@index'); Route::get('project/create', 'CreatesController@index'); Route::post('project/create', 'CreatesController@store'); Route::get('project/edit', 'CreatesController@edit'); Route::post('vupdate', 'CreatesController@update'); Route::get('project/delete', 'CreatesController@delete');突っ込みどころは多いですけど一旦スルー
改善コード
web.phpRoute::prefix('project')->name('project.')->group(function () { Route::resource('/', ProjectsController, ['except' => 'show']); });改善ポイント
- Projectに関連した物しかなく、restfullで完結できそうだったので、ProjectsControllerファイルにまとめました(これはたまたまなので、無理矢理真似なくてOK)
- groupにまとめて無駄な記述が減らせたのと関連したルーティングが見易くなった。
- Routeメソッドをresourceに変える事でメソッド毎に定義しなくて済んだ(showは使わないのでexceptで除外していますが本来はonly派)
ちょこっと番外編(LaravelというかPHP)
検索機能を付けていたのだが、過去のコードを見てみましょう。
過去コード
ProjectsControllerpublic function index(Request $request) { $query = Project::query(); if (isset($request)) { $category_id = $request->get('category_id'); $responsible_id = $request->get('responsible_id'); $tag_ids = $request->get('tag_id'); $release_year = $request->get('release_year'); $release_month = $request->get('release_month'); $start_year = $request->get('start_year'); $start_month = $request->get('start_month'); $end_year = $request->get('end_year'); $end_month = $request->get('end_month'); //カテゴリ if (!empty($category_id)) { $query->where('category_id', 'like', '%' .$category_id .'%'); } //ユーザー if (!empty($responsible_id)) { $query->where('responsible_id', 'like', '%' .$responsible_id .'%'); } //リリース年 if (!empty($release_year)) { $query->where('release_year_at', 'like', '%' .$release_year .'%'); } //リリース月 if (!empty($release_month)) { $query->where('release_month_at', 'like', '%' .$release_month .'%'); } //開発開始年 if (!empty($start_year)) { $query->where('start_year_at', 'like', '%' .$start_year .'%'); } //開発開始月 if (!empty($start_month)) { $query->where('start_month_at', 'like', '%' .$start_month .'%'); } //開発終了年 if (!empty($end_year)) { $query->where('end_year_at', 'like', '%' .$end_year .'%'); } //開発終了月 if (!empty($end_month)) { $query->where('end_month_at', 'like', '%' .$end_month .'%'); } } }なげーーーー
しかも検索条件増えるたびに書き足さないといけない、、
という事で今回は複雑な検索条件は無かったので短縮化ProjectsControllerpublic function index(Request $request, Project $project) { $query = $project->searchProject($request); }Modelに記述
Project.phppublic function searchProject(Request $request) { $query = $this->query(); if ($request->query() !== '') { foreach ($request->all() as $key => $val) { !empty($val) ? $query->where($key, 'LIKE', '%' .$val .'%') : ''; } } return $query; }これで検索条件が増えても安心ですね^^(簡単な条件に限るけど)
改善ポイント
- コード行短縮
- 条件が増えるたびに書き足す必要がない
Bladeのoldヘルパー使えてる?
良く編集画面とかで動的に変わる選択ボックスとか見かけますよね??(この辺割と記事ない印象)
そういう時入力値を優先させて、old()がなければDBの値を反映して一致した時にselected
とかif文でゴニョゴニョしまくってませんか?勿論過去の僕はしてます。
しかも型を厳密にチェックしてません。
悪い子です。
過去コード
edit.blade.php<select name="category_id" class="form-control"> @foreach ($categories as $category) <option value="{{ $category->id }}" @if(!empty(old('category'))) @if(old('category') == $category->id) selected @endif @elseif($project->category_id == $category->id) selected @endif >{{ $category->name }}</option> @endforeach </select>上のコードもold()の第二引数を使用すればここまでスッキリかけます!
(ちょっと横に長いけど、改行してもよし)
改善コード
edit.blade.php<select name="category_id" class="form-control"> @foreach ($categories as $category) <option value="{{ $category->id }}" @if ($category->id === (int) old('category_id', $project->category_id)) selected @endif>{{ $category->name }}</option> @endforeach </select>ちなみにold()はstringで返ってくるのでintに変換してます。
改善ポイント
- 無駄なif文を省いて見易くなった
終わり
誰もこんなクソコードを量産して欲しく無かったので戒めとして書きました。
皆さんも昔のソースコード久々に覗いてみては?
乾いた笑いが出ると思います。
- 投稿日:2020-04-09T06:35:44+09:00
Laravel Facade自作
LaravelのFacadeを自作する。
Hoge::fuga()
で'piyo'を返すHogeファザードを作成する。まずHogeクラスを作成する。
App/Support/Hoge.php<?php namespace App\Support; class Hoge { public function fuga() { return 'piyo'; } }そしてHogeクラスを呼び出すファサードを作成。
App/Support/Facades/Hoge.php<?php namespace App\Support\Facades; use Illuminate\Support\Facades\Facade; class Hoge extends Facade { public static function getFacadeAccessor() { return 'hoge'; } }Hogeクラスをサービスコンテナに登録するProviderを作成。
Providerはmakeコマンドを使うことでapp/Provivdersフォルダ以下に雛形が作成されます。$ php artisan make:provider HogeProvider Provider created successfully.app/Providers/HogeProvider.php<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class HogeProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { // } /** * Bootstrap services. * * @return void */ public function boot() { // } }register関数内でHogeクラスをサービスコンテナに登録し、HogeProviderがLaravelに読み込まれるようにconfig/app.phpに登録します。
app/Providers/HogeProvider.phppublic function register() { $this->app->singleton('hoge', \App\Support\Hoge::class); }config/app.php'providers' => [ /* * Laravel Framework Service Providers... */ 省略 /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\HogeProvider::class, //追加 ],これで使えるか確認してみましょう。
HogeFacadeをuseすることでHoge::fuga()
が使えるようになります。適当なクラスuse App\Support\Facades\Hoge; echo Hoge::fuga(); 出力: piyofacadeのuseなしにfacadeを使えるようにする
config/app.phpにaliasを指定することでuseなしにfacadeを使うことが可能になります。
config/app.php'aliases' => [ 省略 'View' => Illuminate\Support\Facades\View::class, 'Hoge' => App\Support\Facades\Hoge::class, //追加 ],これで準備okです。
\Hoge::fuga()
で呼べるようになります。ideでコード補完できるようにする
providerを登録しておけばide-helperの自動生成ファイルを元にideコード補完ができるようになります。
$ php artisan ide-helper:generate
- 投稿日:2020-04-09T04:20:18+09:00
GitHub Actions で LaravelのCI/CD環境を構築する(MySQL, Deployer)
概要
GitHub Actionsを使って、LaravelのプロジェクトをテストしてからAWSのEC2上にデプロイする方法を書いていきます。
テストDBにはMySQLを使用し、デプロイにはDeployerを使用します。各々の細かい説明はしません。
設定ファイル
GitHub Actionsの設定はYMLファイルです。
.github/workflows
ディレクトリに設定ファイルを置くとワークフローとして処理されます。テストのjob
まずはテストが実行されるようにファイルを書いていきます。
on: push jobs: test: name: phpunit test runs-on: ubuntu-latest services: mysql: image: mysql:8.0.19 ports: - 3306:3306 options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10 env: MYSQL_ROOT_PASSWORD: pass MYSQL_DATABASE: test env: DB_CONNECTION: mysql DB_HOST: 127.0.0.1 DB_PORT: 3306 DB_DATABASE: test DB_USERNAME: root DB_PASSWORD: pass steps: - uses: actions/checkout@v2 - name: cache vendor id: cache uses: actions/cache@v1 with: path: ./vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: | ${{ runner.os }}-composer- - name: composer install if: steps.cache.outputs.cache-hit != 'true' run: composer install -n --prefer-dist - name: copy .env run: cp .env.ci .env - name: generate key run: php artisan key:generate - name: migrate run: php artisan migrate - name: unit test run: ./vendor/bin/phpunitMySQL
今回はテストDBにMySQLを使用するのでMySQLを使いたいのですが、ワークフローが実行されるマシン(ランナー)であるubuntuには5系しか入っていないので、Dockerコンテナを立ててMySQLを使用します。
services内のimageでDockerのimageを指定でき、DockerHubからimageをpullできます。
執筆時点ではoptionを指定していないと、MySQLコンテナが立ち上がる前にPHPUnitが走ってしまいテストがコケてしまう現象がでたため、MySQLコンテナが立ち上がるまで待ちます。
envで環境変数を設定しています。
jobs.<id>.env
の位置に書くことによって、このjob内で使える環境変数として設定できます。注意点としては、今回はランナー上でコンテナをたてているので
DB_HOST: 127.0.0.1
にしてください。コンテナ名にしても接続はできません。PHPUnit
毎回composer installをすると時間がかかってしまうので、キャッシュができるアクションがあるのでそれを指定しています。
使い方はここに書いてあるコードを書いておけばとりあえず大丈夫です。.envに関しては今回は雛形(.env.ci)をコピーしてますが、
APP_KEY
が設定できればどの方法でもOKです。あとはmigrateなりphpunitなりのコマンドを叩けばテストが走るjobが完成します。
デプロイのjob
次はデプロイに関して設定をしていきます。
さきほどと同じファイルの続きに書いていきます。
jobs: //... deploy: name: deploy runs-on: ubuntu-latest needs: test if: github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v2 - name: cache vendor id: cache uses: actions/cache@v1 with: path: ./vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: | ${{ runner.os }}-composer- - name: composer install if: steps.cache.outputs.cache-hit != 'true' run: composer install -n --prefer-dist - name: ssh_key copy run: | mkdir -p /home/runner/.ssh touch /home/runner/.ssh/id_rsa echo "${{ secrets.SECRET_KEY }}" > /home/runner/.ssh/id_rsa chmod 600 /home/runner/.ssh/id_rsa - name: add known hosts run: ssh-keyscan 【 ip or host_name 】 >> ~/.ssh/known_hosts - name: deploy run: ./vendor/bin/dep deploy masterまずテストと大きく違うところはneedsとifです。
GitHub Actionsではjobは並列に実行されるのでそのまま書いてしまうとテストとデプロイが一緒に走ってしまうことになります。
テストをパスしてからデプロイをしたいのでneedsでどのjobが終わってから実行するかを指定します。
またデプロイはどのブランチでもデプロイするわけではないので、ifを使ってmasterブランチのときに実行するようにします。
Deployer
Deployerでデプロイするには秘密鍵が必要になります。公開鍵はあらかじめサーバーにあげておいてください。
秘密鍵などの情報をそのまま書いてしまうとセキュリティ的にNGなので、GitHub ActionsではあらかじめGitHubに暗号化して登録しておきます。
(https://help.github.com/ja/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets)登録した秘密鍵を適切な場所にコピーしてあげます。(Deployerだと
hosts.yml
に秘密鍵のファイルを指定できるオプションがあります)このままssh接続をしてしまうと接続時にコンソールで
yes or no
を入力しないとすすめなくなってしまいエラーになってしまうので、known_hosts
にあらかじめ記述しておくことでこれを回避することができます。最後にdeployコマンドを叩くようにするとデプロイが開始されます。
pushする
以上のファイルを保存してGitHubにpushすると、pushをトリガーにしてmasterだとテスト&デプロイが、それ以外だとテストが走ります。
Circle CIのようにGitHub Actionsのテストが通ったらPRをマージできるようにするなどの設定も可能です。
まとめ
Circle CIを使ったことがある人ならそこまで抵抗なくできると思います。
GitHub Actionsでは他の人が作ったものをActionとして利用できるので、それを利用することでより手軽に様々なことができるようになるかもしれません。
GitHub Actionsの検索ちなみにSlackへの通知はこちらのアクションなどがあるので便利です。
- 投稿日:2020-04-09T02:44:30+09:00
Laravel Middlewareを自作する
LaravelのMiddleware覚書。
今回はviewにアクセス日時を表示するViewDateMiddlewareを作成します。Middleware作成用のartisanコマンドが用意されているのでそれを使います。
引数にミドルウェア名を渡せばokです。
php artisan make:middleware ミドルウェア名
help$ php artisan make:middleware --help Description: Create a new middleware class Usage: make:middleware <name> Arguments: name The name of the class以下のコマンドで
app/Http/Middleware/ViewDateMiddleware.php
が作成されます。ViewDateMiddleware.phpを作成$ php artisan make:middleware ViewDateMiddleware Middleware created successfully.自動生成されたMiddlewareにはhandle関数のみ定義されています。
第一引数にRequest, 第二引数にClosureが渡されます。Requestはhttpリクエストでinput値等が取得できます。
Closureを実行することで次のMiddlewareが実行されます。
Controllerロジックより前に処理を入れたい場合は$next($request)
より前にコードを記述してください。
Controllerロジックの後に処理を入れたい場合は$next($request)
より後ろにコードを記述してください。ViewDateMiddleware.php<?php namespace App\Http\Middleware; use Closure; class ViewDateMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { return $next($request); } }MiddlewareはApplicationサービスコンテナによってインスタンス化されるため、コントラクタ引数に任意のクラスを指定することでDIされます。
今回はviewにアクセス日時を表示できる変数を渡すのでViewのFactoryを引数に指定します。ViewDateMiddleware<?php namespace App\Http\Middleware; use Closure; use Illuminate\Contracts\View\Factory; class ViewDateMiddleware { private $view; public function __construct(Factory $view) { $this->view = $view; } 省略 }handle関数内でFactoryのshare関数を使うことでviewで使える変数を定義できます。
ViewDateMiddlewarepublic function handle($request, Closure $next) { $this->view->share('access_date', time()); return $next($request); }index.blade.php等のviewファイルで
{{ $access_date }}
で使えます。index.blade.php{{ $access_date }}そしてブラウザで見てみるとエラーがでました。
Undefined variableなのでaccess_date変数の定義に失敗しています。
Middlewareはlaravelライフサイクルの中で読み込まれるようkernelやroute, controllerの中で指定する必要があります。
kernelにはMiddlewareを指定する箇所は3箇所あります。
・$middleware
全てのhttp通信で処理されるミドルウェアを追加・$middlewareGroups
webやapi等の特定routeGroupで使用するミドルウェアを追加・$routeMiddleware
ミドルウェアのalias名を指定できる。個々のrouteでalias名を使ってmiddlewareを指定できる。今回は全てのwebリクエストで読み込まれるようにしたいのでkernelのmiddlewareGroupsに追記します。
App/Http/Kernel.phpprotected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \App\Http\Middleware\ViewDateMiddleware::class, //ここに追加 ], 'api' => [ 'throttle:60,1', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];これでミドエルウェアが実行されてviewでaccess_date変数が使用できるようになりました。
↓結果html
middlewareに引数を渡す
routeで個別にmiddlewareを指定する際にmiddlewareに引数を渡せます。
先ほどのmiddlewareを改造し、routeごとに時刻フォーマットを指定できるようにします。まずはkernel.phpの$middlewareGroupsに追記したmiddlewareを$routeMiddlewareに移動させます。
viewData
aliasでmiddlewareを指定できるようにしました。protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'ViewDate' => \App\Http\Middleware\ViewDateMiddleware::class, //追加 ];続いてrouteでmiddlewareを指定します。
middleware名:引数
のフォーマットで指定することでmiddlewareに引数を渡すことができます。
viewDate-Ymd
にアクセスしたらY/m/d
を、
viewDate-Ym
にアクセスしたらY/m
を引数にするように指定しました。web.phpRoute::get('/viewDate-Ymd', function() { return view('index'); })->middleware('viewDate:Y/m/d'); Route::get('/viewDate-Ym', function () { return view('index'); })->middleware('viewDate:Y/m');これで
http://127.0.0.1:8000/viewDate-Ymd
にアクセスすると
http://127.0.0.1:8000/viewDate-Ym
にアクセスすると
と表示されるようになりました。
- 投稿日:2020-04-09T00:49:13+09:00
Laravel の Policy ってどんな場合に使うの?
特定のリソースに対するユーザーアクションを認可
する仕組みだそうです。
例えば、ブログの記事削除は本人にしかできないようにすることが可能になる仕組みのことです。Policyを使わないで書くと以下のようになり、
public function delete(Article $article) { if (request()->article()->isNot($article->user)) { return response([], 403); } $article->delete(); }
ArticlePolicy
を作成すると、ArticlePlicy.phppublic function delete(User $user, Article $article) { return $user->id == $article->user_id; }以下のように書ける。
public function delete(Article $article) { $this->authorize('delete', $article); $article->delete(); }また、ミドルウェアによる認可もできる。以下のように書ける。
api.phpRoute::delete('/articles/{article}', 'ArticlesController@destroy')->middleware('can:delete,article');今の所、アカウントの種類によって認可するアクションを切り分けたいときに使う認識でいます。