PHP

Laravel 実践開発 秀和システム 演習メモ

 


PHPフレームワーク Laravel実践開発

Laravelやってて事前知識のつまづきが多く、基礎的な事でググる時間のコストが無駄なので、2日かけて基礎として1冊やるぞ。

この本は『PHPフレームワーク Laravel入門』をクリアしている中級?向けに書かれているので、最初は入門本からやるのが良いです。

最初からこの本をやると死ぬと思います。Amazonのレビューを見ると初心者に優しくなくて低評価みたいになってるようです;

本書は、2017年9月刊行の大好評『PHPフレームワークLaravel入門』を補足する続編。読者の「これも知りたかった!」という声に応えました!

私には素晴らしい内容でありがたいのです。

 

もくじ

この記事の対象読者

 

ざっくり内容

  • ルーティング, ミドルウェア
  • ファイルシステム, 画像操作
  • リクエスト, レスポンス
  • サービスコンテナ
  • Collection
  • キュー, イベント
  • Vue.js フロントエンドとの連携
  • テスト, モックの作成

完全に実践的な内容になっています。

 

ご注意

入門本をひたすらやっても『自分で何かを0から作れるようにはならない』ので、短期的にざくっと本を終わらせてから、実際に本を参考にググりながら自分で何かを作ってみるのがおすすめ。

だからあまりだらだら本をやるのはよくない、その為の2日縛り。

  • 読んですらすらわかる内容はやらない。
    プログラミングは暗記する必要はないこと、カンニングOK!必要になったら本やメモ、リファレンスを見ながら実装すれば良いです。
  • 言語やフレームワークができることを覚える。実装方法を理解する
  • 曖昧な単元のみ演習する

これで効率的に学習できます。

過去にやった おすすめ本

 

就活、転職で『Laravelを利用してポートフォリオ作らなきゃ』って方はこれをまるっと1冊通してからだと進みが早いです。正誤表で一度修正してから取り組むのがおすすめです。

リファレンスの前に、サンプルコードそのままに打ち込めば動作するものをやると、動いてからどうして動くのだろう?って楽しみながら学習ができます。まず動かす、そして理解する。この順番が良い。

  • まず動かす、そして理解する。
    言語やフレームワークに慣れないうちはこの順番で大丈夫。
    何かわからないけど動くぞ楽しい!から理解の順番
  • 慣れたら、理解して動かすフェーズ
    フレームワークやクラス関連の作法に少し慣れてくると
    理解して、動かすができます。
    この状態になるとコードが読める状態なので、本を写経しなくても良いです。
    確認と記憶の定着という意味で、理解してからアウトプットとして写経するのがおすすめ。ここらへんの学び方は賛否あるのでこのへんで(๑╹ω╹๑ )
  • とはいっても私は写経もする
    本を読んで理解したと確認する意味で写経する。
    →アウトプットすることで記憶の定着を強化する
    ・読んで理解してると思っても細部を理解していなかった
    ・本のままでは実際動かなかった
  • 暗記不要
    私なんかはお酒で頭がぱっぱらぱーになっているので、関数の正しい表記なんかは暗記しても1週間もたないで忘れます;でもできることを覚えていたり、実装の理解をしていればググったり、関数リファレンスを見返すことで実装できます。
  • 正誤表を確認してから取り組む
    すごく悩んでも出来なくて・・・、本がおかしいよね?
    そうなのです。普通に本のほうがおかしいことってたくさんあるのが技術書です。編集さんはIT技術者ではないのでなかなか難しい問題。出版社のサイトから正誤表を確認してから取り組もう。

 

ある程度なれたら

一旦読んで理解してから、写経するようにしています。写経する場合も可能な限り自分でコーディングしている感覚で動作の理解をしながら行いましょう。

 

 

演習メモ

 


  • 正誤表
    結構ある。修正してから取り組もう

 

入門を終えていることが前提で書かれている為、入門書の内容は可能な限り排除されています。

  • サンプルコードがなく日本語で指示されている箇所
  • 本には書いていないが、自分なりに補足してコーディングしたことで動作した箇所
  • 大切なポイント

ここらへんをコーディングしながらブログにメモしていきます。このブログのメモだけ見てもなんのこっちゃって感じですが、目次的にはわかると思うので『これ知らなかったな』っていう要素があれば、本を買ってみるのが早いかな。

最低限クラス関連のエラー部分を補なって演習出来ないと、本のままでは動かせない印象。入門編の内容はショートカットされるので、ただサンプルを打ち込めば動かせる、そういう本ではないです。入門編を終えてから取り組むのが宜しいかと。書店で売られるのって初心者向けがほぼなのですが、技術書には珍しい本で本当の初心者向けではないかな。だからおすすめなんですよね!

 

コントローラ作成

 

※私みたいにdocker-compose環境の場合は下記みたいになる。

 

 

HTTPステータスのビューテンプレート作成

コマンドで生成できるようにLaravelで用意してくれている。

私これ、知らなくて手作業で作った覚えがあるんだが…!?(怒)

illustrated-layoutに変更するとillustrated-layout.blade.phpを読み込むようになり、表示が綺麗になる。

 

 

P14

ミドルウェア作成

 

routes/web.php

useでミドルウェアを指定しないと動かなかった。

 

P16

routes/web.phpで名前空間の記述をシンプルにする

下記のように書かなくて済む

何が便利なのか?

  • 階層が深い名前空間を利用している場合にコントローラ@メソッドでシンプルに記述できる

 

P18

自分でデータを入れる必要があります。

 

/database/migrations/xxxx_create_table.php

 

 

/database/seeds/PeopleTableSeeder.php

 

/database/seeds/DatabaseSeeder.php

 

マイグレーション, シーディング

 

 

/routes/web.php

ルーティングをセットする

 

http://localhost/hello/1

idを入れてあげることでDBアクセスするクエリビルダやEloquentの技術なしで、レコードの内容が取得されて表示されます。

 

ルーティングを司る/app/Providers/RouteServiceProvider.php

本とは異なってModelsにモデルを格納した場合は、上記みたいにフルパス指定してあげる。

 

\app\Http\Controllers\HelloController.php

こうすることでも動作される。

 

/app/Providers/RouteServiceProvider.php

この記述を消すと、1が返される。

 

P39 ファイルの存在をチェックする

 

copyやremoveでエラーが出る

  • 元ファイルが存在していない場合
  • 複製先や移動先にファイルが存在している場合

 

このエラーを回避する為に、「exists」メソッドを利用して存在確認を行う。

 

 

P51 リクエストとレスポンス

 

P56

 

P68 サービスとコンテナ結合

 

サービスコンテナ?

  • 必要なクラスのインスタンスが自動的に引数として用意される機能

  • あるクラスと依存関係にあるクラスのインスタンスを管理する機能として提供

依存性注入

  • 依存関係にあるクラスのインスタンスを外部からクラスに注入する

サービスコンテナ = 『Laravelに用意されているDI機能を実装しただけのクラス』と考えることができる。

 

 

P72 明示的にインスタンスを生成する

下記はすべて同じ効果で、指定したクラスのインスタンスを明示的に取得する

 

ここの本質ではないけれど、

パラメータの値がない時にきちんとデフォルトの値を設定をしておく、大事。

 

シングルトン結合を行う

 

bind()からsingleton()に変更

これだけでMyServieクラスをシングルトンとして利用できるようになった。

 

引数を必要とする結合

MyService.php

 

AppServiceProvider.php

 

P86-87

こういう風に変更しないと動かなかった。

 

結合時のイベント処理

結合時に呼び出される

特定のクラスとの結合時に呼び出される

 

結合イベントを利用する

 

 

P93 ファサードの利用

 

サービスプロバイダを作成する

 

/app/Providers/MyServiceProvider.php

 

P97-100 MyServiceファサードを作成する

  1. app/Facadesディレクトリを作成
  2. app/Facades/MyService.phpファイルを作成

 

app/Facades/MyService.php

 

config/app.php

 

app/Providers/MyServiceProviders.php

 

app/Http/Controllers/HelloController.php

 

P101 ミドルウェアの利用

  • ミドルウェアをリクエストを拡張する仕組み
  • リクエストを操作するだけのもの

 

ミドルウェアの作成

 

ミドルウェアの雛形

 

handleメソッド

  • handleの引数で渡されたRequestインスタンスをClosureの引数に指定して呼び出したり、戻り値をreturn
  • $next($request)でRequestインスタンスを得て、これをreturnすることでクライアントにレスポンスが返される

 

routes/web.php

 

/app/Http/Controllers/HelloController.php

 

beforeとafter

  • ✖︎ミドルウェアはコントローラより前の処理を実行するもの
  • ◎ beforeとafterがあり、リクエストが処理される前、処理された後に実行させることができる

 

/app/Http/Middleware/MyMiddleware.php

 

after処理

ここでRequestインスタンスを取り出す

 

  • content()メソッド
    コンテンツを取り出す
  • setContent()メソッド
    コンテンツを設定する

after処理は返送される直前にコンテンツを操作するもの

 

ミドルウェアの利用範囲と設定

現状の知識だと、Route一つ一つに書き込まないといけずに煩雑になるという問題がある。

  • すべてのRouteで指定したい時はどうするの?
  • グループ単位で指定したい

こういった要件を満たしたい。

 

routes/web.phpで設定していたミドルウェア設定を削除してプレーンに戻す

 

app/Http/Kernel.php

グループミドルウェア:web, apiといったグループで指定できる

 

ルートミドルウェア:ミドルウェアごとに名前を設定できる

 

プライオリティの設定:ミドルウェアの実行順序を指定

Aのミドルウェアが実行された後でないと、Bのミドルウェアが実行できないといった設計の時に利用します。

 

グローバルミドルウェアに登録する

すべてのRouteに影響する

 

データベースの活用

この章は飛ばして次いこうかと思ったけれど、知らないこともありました。

 

同じ働きをするのでwhereRaw()がある

Raw系を利用する場合は、パラメータを利用して、SQLインジェクションを防止すること。

 

最初と最後のレコードを取得する

 

idを指定してレコードを取得する find()

不思議だが、[]が必要。

 

特定のフィールドのみ取得する pluck()

 

 

 

P123 chunkByIdによる分割処理

 

検索結果を指定した要素数ずつ分割処理するメソッド

  1. 引数にテーブルの参照データが要素数毎に代入される
  2. クロージャで処理される
    function(引数) { 処理 }部分
  3. return false or true
    ・trueを返すと、次のレコード群がレコードに渡される
    ・falseを返すとchunkByIdを抜けて次に進む

 

レコードのIDが奇数のものだけをまとめた処理

 

useで&をつけて参照渡しをしている。

これをしないと別の変数として扱われるので注意

 

orderByとchunkを使う

  • idではなく別の基準でレコードをなら並び替え、分割処理したい場合はchunkByIdは使えない
  • chunkとorderByを利用する

 

一定の部分だけを抜き出して処理する

一応これでhttp://localhost/hello/{id}をすると、そのid + 3をしたidから昇順で3レコード取れるサンプルコード。アプリ側で処理するからレコード数が増えると相当重くなる気がして、実装して良いコードなのかは今の私にはわからない。

 

AND検索

 

OR検索

 

2つの値の範囲

orがついたメソッドは他の条件の後にOR検索で繋げます。

 

2つの値の範囲外を検索

 

idが2〜5のレコードを取得する
http://localhost/hello/2,5

 

配列で検索値を指定

 

idが1, 2, 5を抽出したい
http://localhost/hello/1,2,5

 

 

配列に含まれている値と等しいもの以外を検索

 

指定フィールドがnullのものを検索

 

指定フィールドがnullではないものを検索

 

日付の値のチェック

 

2つのフィールドの値が等しいものを検索

 

ページネーション

指定ページのレコードを得る

 

  • http://localhost/hello/?page=1
  • http://localhost/hello/?page=2

 

Bootstrap4を有効化させる

bootstrap/app.php

 

/Http/Controllers/HelloController.php

 

 

アクセスします

  • http://localhost/hello/?page=1
  • http://localhost/hello/?page=2

 

prev, Next表記のsimplePaginate()に変更する

  • paginate(), simplePaginate()の引数は1つだけでもクエリパラメータで渡された値を自動で参照してくれる。
  • 第一パラメータは必須。

 

Eloquentの利用

App/Models/Person.phpを作っていたら

下記みたいに、Eloquentで呼び出すことができる。

 

カスタムページネーションリンク

 

App\Http\paginationフォルダを作る

App/Http/pagination/Mypagination.php

 

App/Http/Controllers/HelloController.php

 

resources/views/hello/index.blade.php

 

 

Paginatorのメソッド

  • $results->count()
    レコード全体のページ数を取得
  • $results->currentPage()
    現在のページ番号を取得
  • $results->firstItem()
    現在のページの最初のレコードがなんばんめのものなのかを取得
  • $results->hasMorePages()
    次のページがあるかどうかを返す
  • $results->lastItem()
    現在のページの最後のレコードがなんばんめのものなのかを返す
  • $results->lastPage() (simplePaginateでは使用不可)
    最後のページ番号を返す
  • $results->nextPageUrl()
    次のページを表示するURLを返す
  • $results->perPage()
    1ページあたりに表示されるレコード数を返す
  • $results->previousPageUrl()
    前のページを表示するURLを返す
  • $results->total() (simplePaginateでは使用不可)
    レコードの総数を返す
  • $results->url(ページ番号)
    そのページを表示するURLを返す

 

Eloquent リスト表示の基本形

App/Models/Person.php

 

 

App/Http/Controllers/HelloController.php

 

 

resouces/views/hello/index.blade.php

 

コレクション

  • モデルから取得されたレコード類はコレクションとして返される
  • Illuminate\Database\Eloquent名前空間のCollectionクラスのインスタンス
  • コレクションはイテレータ機能があり、foreachなどを使ってレコードを処理できる
  • レコード1つ1つが、対応するモデルクラスのインスタンスとして保管される
    →モデルを利用して取得されるのはモデルクラスのインスタンス

 

フィルター

  • filterメソッドで未成年を取得する

 

  • rejectメソッドで未成年を排除する

rejectメソッドのクロージャの引数は、モデルクラスのインスタンスが入ります。そして処理として、対象レコードを排除して返します。

 

diff 差分を取得する

 

P150 コレクションの機能:modelKyesとonlyおよびexcept

 

コレクションに関しては下記が参考になる

 

 

配列にまとめたIDのモデルを取得して

 

idが偶数のレコードをonly()で取得

 

except()は配列にまとめたID以外のモデルを取得する。

 

mergeとunique

merge: 2つのコレクションを1つにまとめる

mergeでは同じidのコレクションがあった場合は上書きされるので、レコードの重複はない。

unique:はコレクションを1つにまとめて、重複したものを除いてまとめたものを返す。

 

map

PHPのarrya_map()と同じ働きをする。配列を加工して新しい配列を作る

 

 

重要 カスタムコレクション

@see

 

実装方法

  • モデルの中でnewCollection()を実装すると、検索結果として独自のコレクションメソッドを返すことができる。
  • MyCollection()のカスタムコレクションの中で配列全体の操作や、メソッドを定義できる
  • get()するだけで、newCollection()を実装していれば、自動的にコールされて新しいコレクションで検索結果が返る

 

/app/Http/Models/Person.php

 

 

 

 

App/Http/Controllers/HelloController.php

 

 

アクセサ

デフォルトでモデルから$this->nameみたいにしてテーブルのフィールドがプロパティとして値を取得できる。

テーブルのフィールドに対応するプロパティを上書きして取得できる = アクセサ

 

/app/Models/Person.php

  • get<カラム名>Attribute()で定義する
  • 複数の場合はAndで結んだキャメル型
    getNameAndAgeAttribute()
  • 呼び出しは$this->name_and_ageみたいにスネーク型になる

 

 

resouces/views/hello/index.blade.php

 

既存のプロパティを変更する

/app/Models/Person.php

これでviewで$this->nameしているところは大文字になる。

 

 

ミューテータ

値を設定する処理を上書きするのがミューテータ。

 

/app/Models/Person.php

 

routes/web.php

ルート設定

 

 

App/Http/Controllers/HelloController.phpにsave()メソッドを追加する

これはEloquentでの更新の基本的な流れ、抑えておく。

 

 

 

http://localhost/hello/1/yamada-kun2

上記でアクセスする。

 

 

データベース上はこうなっている。ミューテータによってレコード idの1が大文字で登録されている。

 

配列の保存

/app/Models/Person.php

 

App/Http/Controllers/HelloController.php

 

routes/web.php

 

 

JSON形式でのレコード取得(toJson)

 

routes/web.php

 

JavaScriptからアクセスする

resouces/views/hello/index.blade.php

 

 

キューとジョブ

 

 

app/Jobs/Myjob.phpが作成される

 

 

app/Providers/MyJobProvider.phpを作成する

 

ジョブの登録
app/Providers/MyJobProvider.php

 

 

App/Http/Controllers/HelloController.php

 

 

データベースにアクセスする

app/Jobs/Myjob.php

 

App/Http/Controllers/HelloController.php

 

routes/web.php

 

http://localhost/hello/2

id =2のpeopleテーブルのレコードのnameフィールドに対して文字列が付与されたり、はずされたりする。

 

非同期に対応させる

 

キュー用テーブル, 実行失敗時テーブルの作成

作成

 

.envの修正

デフォルトではsync(同期)になっているので、databaseに変更

 

ワーカの実行

 

 

  • <<PendingDispatch>>->delay(日時)
  • <<PendingDispatch>>->delay(now()->addMinutes(3))

3分後に実行

 

※ソースコードを変更した場合はキューの再起動が必要

 

http://localhost/hello/3

アクセスするとキューが登録される。

 

時間が経つとキューが実行される。

 

  • php artisan queue:work
    起動
  • php artisan queue:restart
    再起動をワーカに伝える
    コードを変更した後などはキューを再起動しないと既存のキューに反映されない。
  • php artisan queue:work –once
    ワーカを起動してジョブを1つだけ実行する
  • php artisan queue:work –stop-when-empty
    溜まったジョブをすべて実行
  • php artisan queue:work –queue=名前
    特定のキューを実行する
    php artisan queue:work –queue=a,b,c
    といったカンマ区切りで複数実行できる

 

特定のキューを指定する

 

  • http://localhost/hello/1
    ・・・
  • http://localhost/hello/6

アクセスしてキューを入れる

queueフィールドに名前がついていることがわかる。

 

odd(奇数)のキューかつ溜まったジョブをすべて実行

 

クロージャをキューに登録する

 

App/Http/Controllers/HelloController.php

 

resources/views/hello/index.blade.php

 

routes/web.php

 

フォームからidに対応する数字を入れると、storage/app/public/person_access_log.txtにかきこれる

 

イベント

  • 必要な情報をまとめたオブジェクト
  • 何かのイベントを作ろうとした時にそのイベントでどういう情報が必要かを考えてイベントのクラスをまとめる

 

イベントリスナー

  • イベントの発生を監視
  • イベントが発生した時にリスナーに用意されている処理が実行される。

 

PersonEventを登録する

 

app/Providers/EventServiceProvider.php

 

 

  • app/Events/PersonEvent.php
  • app/Listeners/PersonEventListener.php

を生成

 

app/Events/PersonEvent.php

 

 

app/Listeners/PersonEventListener.php

 

 

app/Events/PersonEvent.php

 

App/Http/Controllers/HelloController.php

 

 

http://localhost/hello
フォームからidとなる数字を入力して送信すると、
storage/app/public/person_access_log.txtに追記される。

 

処理の流れ

  1. イベントクラスのインスタンス作成
  2. イベントの発行
  3. イベントリスナーのhandleでイベントクラスを受け取る

 

購読 subscrive

購読クラスの基本

 

イベントのリッスン

  • 第二引数にイベントリスナーのハンドラとなるメソッドを指定
  • $eventsにはDispatcherクラスのインスタンスが渡される
    この中のlistenメソッドを利用することでイベントのリッスンが行える

 

購読クラス

app/Listeners/MyEventSubscriber.php

 

購読クラスを登録する

app/providers/EventServiceProvider.php

 

http://localhost/hello
アクセスしてフォームにIDに対応する番号を打ち込むと、person_access_log.txtに書き込まれることを確認。

 

イベントディスカバリ

 

app/providers/EventServiceProvider.php

このメソッドを追加すると、

  • 面倒なイベントの登録処理を自動で全て登録を行ってくれる。
    →記述したイベントが全て動作してしまう。
  • しかし、今は使わないといったイベントもすべて動作してしまう、諸刃の剣
    →手作業で登録した方が、明示的で安心できることが多い。

 

キューを利用してイベント発行する

これでキューを利用してイベントを発行することができる。

  1. PersonEventが発生
  2. PersonEventListenerの実行をジョブとしてキューに登録
    ※キュー利用するから、ワーカーが起動していないとイベントリスナーのハンドラは実行されなくなる。

 

ワーカーが起動していない場合

  • キューがjobsに登録される
  • ワーカーが起動したら実行される

 

ジョブを利用するか、イベントを利用するか?

 

ジョブ

  • ジョブはそれ自体で処理を行う
  • 『必要な処理を必要なタイミングで実行させるようにキューに登録する』するだけ

 

イベント

  • イベントはリスナーに処理を委任する
  • PersonEventのイベントリスナーは、PersonEventListerだけとは限らない。
    →いくつもリスナーを用意しておいて、必要に応じて最適なリスナーのハンドラを実行することができる。
    ジョブをキューに登録するのとは大きく違う
  • サブスクライブのように、必要な一連のイベントをまとめて購読するなど、イベントの発生とハンドラ実行の設定が色々と用意されている

発生するタイミングと実行する処理を組み合わせる必要がある場合

=> 基本として、イベントを利用する。

 

 

タスクとスケジューラ

スクリプトなどを実行させる。

 

スクリプト作成

実行権限付与

 

/app/Console/Kernel.php

 

実行する

実行された。

定期的に自動実行登録するにはLinux側でCronに登録する

 

 

溜まったキューを実行する

 

 

クロージャで処理を実行する

 

callは引数に指定した具体的な実行する処理を記述したクロージャを実行する。

 

/app/Console/Kernel.php