- 投稿日:2020-04-14T23:24:55+09:00
Laravelを知らない中級(中年)プログラマーがマイグレーションファイルの仕組みを調べてみたけど全くわからない!その3「Repositoryクラス」
INDEX
Laravelを知らない中級(中年)プログラマーがマイグレーションファイルの仕組みを調べてみたけど全くわからない!
Repositoryクラス
前回
DatabaseManager
の$this->app['config']['database.default']
ってなんだろうというところまで読みました。それを追ってみましょう。
DatabaseManager
クラスのコンストラクタは以下のように定義されています。Illuminate/Database/DatabaseManager::__construct/** * Create a new database manager instance. * * @param \Illuminate\Contracts\Foundation\Application $app * @param \Illuminate\Database\Connectors\ConnectionFactory $factory * @return void */ public function __construct($app, ConnectionFactory $factory) { $this->app = $app; $this->factory = $factory; $this->reconnector = function ($connection) { $this->reconnect($connection->getName()); }; }
$this->app
には生成される時の第一引数が代入されています。コードヒントにはIlluminate\Contracts\Foundation\Application
とあります。生成する時の手順はまだはっきりとわかりませんが、$this->app
がApplication
インスタンスなのは確かなようです。
前回のコードリーディングでApplication
クラスはContainer
クラスを継承していて、ArrayAccess
インターフェースを実装してることがわかっています。配列としてアクセスすると名前解決され指定されたクラスのインスタンスが渡されます。
エイリアス設定を参照すると、実体はIlluminate\Contracts\Config\Repository
インターフェースを実装したIlluminate\Config\Repository
だということがわかります。見てみましょう!Illuminate/Config/Repository抜粋<?php namespace Illuminate\Config; use ArrayAccess; use Illuminate\Contracts\Config\Repository as ConfigContract; use Illuminate\Support\Arr; class Repository implements ArrayAccess, ConfigContract { /** * All of the configuration items. * * @var array */ protected $items = []; /** * Create a new configuration repository. * * @param array $items * @return void */ public function __construct(array $items = []) { $this->items = $items; } /* ========== 中略 ========== */ /** * Get the specified configuration value. * * @param array|string $key * @param mixed $default * @return mixed */ public function get($key, $default = null) { if (is_array($key)) { return $this->getMany($key); } return Arr::get($this->items, $key, $default); } /* ========== 中略 ========== */ /** * Get a configuration option. * * @param string $key * @return mixed */ public function offsetGet($key) { return $this->get($key); } /* ========== 中略 ========== */ }このクラスも
ArrayAccess
が実装されています。offsetGet()
メソッドはreturn $this->get($key)
と定義されています。配列で渡された時の処理を挟んでいますが、実行するのはreturn Arr::get($this->items, $key, $default)
のようです。Arr
クラスを見てみましょう。Arr::get
Arr
クラスの定義ファイルはIlluminate/Support/Arr.php
です。get()
メソッドは以下のように定義されています。Illuminate/Support/Arr::get()|accessible()/** * Get an item from an array using "dot" notation. * * @param \ArrayAccess|array $array * @param string|int|null $key * @param mixed $default * @return mixed */ public static function get($array, $key, $default = null) { if (! static::accessible($array)) { return value($default); } if (is_null($key)) { return $array; } if (static::exists($array, $key)) { return $array[$key]; } if (strpos($key, '.') === false) { return $array[$key] ?? value($default); } foreach (explode('.', $key) as $segment) { if (static::accessible($array) && static::exists($array, $segment)) { $array = $array[$segment]; } else { return value($default); } } return $array; } /** * Determine whether the given value is array accessible. * * @param mixed $value * @return bool */ public static function accessible($value) { return is_array($value) || $value instanceof ArrayAccess; } /** * Determine if the given key exists in the provided array. * * @param \ArrayAccess|array $array * @param string|int $key * @return bool */ public static function exists($array, $key) { if ($array instanceof ArrayAccess) { return $array->offsetExists($key); } return array_key_exists($key, $array); }説明の通り、「ドット」表記を使用して配列から値を取得するメソッドのようです。第一引数に
ArrayAccess
か配列を、第二引数にキー、第三引数にデフォルト値を受け取ります。受け取った第一引数がArrayAccess
のインスタンスでなく且つ配列でもないものならデフォルト値を戻す、キーがnull
なら第一引数をそのまま戻す。第一引数がArrayAccess
で且つ、その中に第二引数がオフセットとして存在する場合、若しくは第一引数が配列であり且つ第二引数がキーとして存在する場合は第一引数の第二引数オフセット、もしくは第一引数配列のキーが第二引数の値を戻す。第二引数に「.
」が含まれていない場合は、第一引数の第二引数オフセットもしくは第一引数配列にキーが第二引数の値があればそれを、なければ第三引数デフォルト値を戻す。冗長な部分があるきがしますが、気にせず次に行きましょう。ここからがメインです。キーを「
.
」で分割し配列にしforeach()
で回します。 「.
」で区切られた文字列の値で第一引数をネストして、オフセットであるか、配列のキーに存在するかを確認していきます。正当性の確認が取れた場合第一引数から第二引数の「.」記法で指定されたオブジェクト若しくは配列を戻し、正当性が取れない場合はデフォルト値を戻します。 まぁ、平たく言えばドットシンタックスを使ってオブジェクトか配列を階層構造を追って取得する感じでしょうか。 本題に戻りましょう。$this->items
Arr::get()
の挙動は理解できましたがこれが引数として受け取る$array
がまだ追えていません。config
の定数なのではないかと予想はできますが、一応追ってみましょう。Arr::get
を呼び出しているRepository::get()
には、Arr::get($this->items, $key, $default)
とあります。$this->items
はRepository
クラスのコンストラクタでセットされるもののようです。このインスタンスはDatabaseManager
インスタンスの$this->app['config']
で、$this->app
はDatabaseManager::__construct
で$app
がセットされています。つまり Laravelアプリケーション自体で初期化したものであるだろうと推測できます。次回
次回はLarabelアプリケーションの初期設定の流れを追ってみましょう。
- 投稿日:2020-04-14T16:29:59+09:00
LaravelでDBのレコードを削除する4つの方法
- 投稿日:2020-04-14T14:27:35+09:00
【Laravel】Seederの実行時の「Class UsersTableSeeder does not exist」というエラー
- 投稿日:2020-04-14T12:15:17+09:00
LaravelでFacadeの処理を追いたいとき
課題
- laravelで実装を追っていく際に
facade
が登場することが多いが、それ以降どこのクラスを見ればいいのかわからなくなることが多い。- なので、今回は
facade
を読む為に必要なことを最小限載せる。Facadeを追っていく。
今回は例として、laravelに標準で組み込まれている
SendsPasswordResetEmails
トレイトを元に進める。まず、このトレイトには
broker()
という関数があり、その中で/** * Get the broker to be used during password reset. * * @return \Illuminate\Contracts\Auth\PasswordBroker */ public function broker() { return Password::broker(); }とある。ここで返り値だけ見れば
PasswordBroker
が返却されると分かるのでそれでいいかもしれないがもう少しちゃんと追う。ここの
Passsword::broker()
のPassword
はIlluminate\Support\Facades\Password;
である。
なので、その中身を確認すると<?php namespace Illuminate\Support\Facades; use Illuminate\Contracts\Auth\PasswordBroker; /** * @method static string sendResetLink(array $credentials) * @method static mixed reset(array $credentials, \Closure $callback) * * @see \Illuminate\Auth\Passwords\PasswordBroker */ class Password extends Facade { /** * Constant representing a successfully sent reminder. * * @var string */ const RESET_LINK_SENT = PasswordBroker::RESET_LINK_SENT; /** * Constant representing a successfully reset password. * * @var string */ const PASSWORD_RESET = PasswordBroker::PASSWORD_RESET; /** * Constant representing the user not found response. * * @var string */ const INVALID_USER = PasswordBroker::INVALID_USER; /** * Constant representing an invalid token. * * @var string */ const INVALID_TOKEN = PasswordBroker::INVALID_TOKEN; /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'auth.password'; } }こんな感じ。ただ、これだけだと
Password::broker()
のbroker()
関数はどこやねんとなる。私はいつもここで詰まる。笑
注目すべきはここである。/** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'auth.password'; }
return "auth.password"
とあるが、このauth.password
でどのクラスが返却されるかを調べる為には
Illuminate\Foundation\Application.php
の中のこの部分を見れば分かる。'auth.password' => [\Illuminate\Auth\Passwords\PasswordBrokerManager::class, \Illuminate\Contracts\Auth\PasswordBrokerFactory::class],一つ目が具象クラスで、二つ目が抽象クラス。一つ目のクラスは二つ目をimplementして使用されている。
この場合、返却されるのは\Illuminate\Auth\Passwords\PasswordBrokerManager
である。よって、初めの
Password::broker()
の中身を調べたいときは、PasswordBrokerManager
クラスのbroker()
関数を調べれば良い。補足
Facadeでどのクラスが返却されるかを早く調べたいときは、Facadeクラス冒頭コメントにある
@see
以降を見れば分かる。
今回の例で言うと<?php namespace Illuminate\Support\Facades; use Illuminate\Contracts\Auth\PasswordBroker; /** * @method static string sendResetLink(array $credentials) * @method static mixed reset(array $credentials, \Closure $callback) * * @see \Illuminate\Auth\Passwords\PasswordBroker //ここに注目 */とあるので、
Password
ファサードで返却されるのはPasswordBroker
クラスであることが分かる。
- 投稿日:2020-04-14T01:13:42+09:00
Laravel セレクトボックスを実装する
目的
- Laravelでセレクトボックスを実装するときの方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.3) ハードウェア MacBook Pro (16-inch ,2019) プロセッサ 2.6 GHz 6コアIntel Core i7 メモリ 16 GB 2667 MHz DDR4 グラフィックス AMD Radeon Pro 5300M 4 GB Intel UHD Graphics 630 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Homwbrewを用いて導入 Laravel バージョン 7.0.8 commposerを用いて導入 MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いて導入 実施方法概要
- laravelcollectiveのインストール
- ビューファイルにコードの記載
実施方法詳細
laravelcollectiveのインストール
アプリ名ディレクトリで下記コマンドを実行する。
$ composer require laravelcollective/htmlビューファイルにコードの記載
簡単な解説
先に記載したセレクトボックスのコードをHTMLのselect要素で記載するとどうなるか比較してみる。
Form::select()メソッド
{{Form::select('age', ['Under 18', '19 to 64', 'Over 65'])}}HTMLのselect要素
<select name="age"> <option value="0">Under 18</option> <option value="1">19 to 64</option> <option value="2">Over 65</option> </select>セレクトボックスでのデフォルト表示を変更したい時は下記のようにする。また、記載内容をHTMLのselect要素と比較する。
Form::select()メソッドで「Over 65」をデフォルト表示に設定
{{Form::select('age', ['Under 18', '19 to 64', 'Over 65'], 2)}}HTMLのselect要素
<select name="age"> <option value="0">Under 18</option> <option value="1">19 to 64</option> <option value="2" selected="selected">Over 65</option> </select>セレクトボックスで選択した際の値を任意に決めたい時は下記のようにする。また、記載内容をHTMLのselect要素と比較する。
Form::select()メソッド
{{Form::select('age', ['first_ans' => 'Under 18', 'second_ans' => '19 to 64', 'third_ans' => 'Over 65'])}}HTMLのselect要素
<select name="age"> <option value="first_ans">Under 18</option> <option value="second_ans">19 to 64</option> <option value="third_ans">Over 65</option> </select>実装の例
/add
にてセレクトボックスを表示する。当該ビューファイルに値を送信するボタンを設置し、押下をトリガーとして動作する。/regist
にセレクトボックスでの入力値を飛ばし、リンクするコントローラのアクションで値を受け取る一連の処理を記載する。- コントローラ名はPostControllerとする。
- セレクトボックスを表示するビューファイル名はaddとする。
PostControllerのregistアクションで入力値を取得する。
当該処理に必要なルーティングファイルの内容のみ抜粋して記載する。
アプリ名ディレクトリ/routes/web.phpRoute::get('/add', 'PostController@add'); Route::post('/regist', 'PostController@regist');当該処理に必要なコントローラファイルの内容のみ抜粋して記載する。
アプリ名ディレクトリ/app/Http/Controller/PostController.phpclass PostController extends Controller { public function add_todo() { return view('post.'); } public function regist(Request $request) { $age => $request->age, //DBへのデータ格納処理 //リダイレクト処理 } }当該処理に必要なセレクトボックスを表示するビューファイルの内容のみ抜粋して記載する。
アプリ名ディレクトリ/resources/view/post/add.blade.php<form action="/regist" method="post"> @csrf {{Form::select('age', ['Under 18', '19 to 64', 'Over 65'])}} <input type="submit" value="send"> </form>ビューファイルで「Under 18」を選択して、ブラウザの「send」ボタンをクリックした場合、PostController内のregistアクション内の
$age
には0が格納される。ビューファイルで「19 to 64」を選択して、ブラウザの「send」ボタンをクリックした場合、PostController内のregistアクション内の
$age
には1が格納される。ビューファイルで「Over 65」を選択して、ブラウザの「send」ボタンをクリックした場合、PostController内のregistアクション内の
$age
には2が格納される。