20200414のlaravelに関する記事は5件です。

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->appApplicationインスタンスなのは確かなようです。
前回のコードリーディングで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->itemsRepositoryクラスのコンストラクタでセットされるもののようです。このインスタンスはDatabaseManagerインスタンスの$this->app['config']で、$this->appDatabaseManager::__construct$appがセットされています。つまり Laravelアプリケーション自体で初期化したものであるだろうと推測できます。

次回

次回はLarabelアプリケーションの初期設定の流れを追ってみましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでDBのレコードを削除する4つの方法

どれもOKでした!

whereで絞り込んで、delete()で削除

new して削除

$db_data = new Product;
$db_data->where('id', 1)->delete();

new しないで削除

Product::where('id', 1)->delete();

idで一意に絞り込んで、destroy()で削除

new して削除

$db_data = new Product;
$db_data->destroy(1);

new しないで削除

Product::destroy(1);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】Seederの実行時の「Class UsersTableSeeder does not exist」というエラー

状況

既存のSeederの名前を変更してから実行した際にエラーがこんなエラーが...

 ReflectionException  : Class UsersTableSeeder does not exist

解決策

下記を実行してからシーディングをするとうまくいきました。

$ composer dump-autoload

そもそもLaravelではクラスの参照はComposerを利用して行います。したがってクラスの変更があった場合はcomposer dump-autoloadのコマンドで更新します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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()PasswordIlluminate\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クラスであることが分かる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を用いて導入

実施方法概要

  1. laravelcollectiveのインストール
  2. ビューファイルにコードの記載

実施方法詳細

  1. laravelcollectiveのインストール

    1. アプリ名ディレクトリで下記コマンドを実行する。

      $ composer require laravelcollective/html
      
  2. ビューファイルにコードの記載

    1. セレクトボックスを表示したいビューファイルを開く。
    2. 下記のコードを記載する。

      {{Form::select('age', ['Under 18', '19 to 64', 'Over 65'])}}
      
    3. ローカルサーバを起動し、下記のように表示される事を確認する。

      • 通常時

        スクリーンショット 2020-04-13 17.42.17.png

      • セレクトボックスクリック時

        スクリーンショット 2020-04-13 17.42.24.png

簡単な解説

  1. 先に記載したセレクトボックスのコードを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>
      
  2. セレクトボックスでのデフォルト表示を変更したい時は下記のようにする。また、記載内容を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>
      
  3. セレクトボックスで選択した際の値を任意に決めたい時は下記のようにする。また、記載内容を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.php
    Route::get('/add', 'PostController@add');
    Route::post('/regist', 'PostController@regist');
    
  • 当該処理に必要なコントローラファイルの内容のみ抜粋して記載する。

    アプリ名ディレクトリ/app/Http/Controller/PostController.php
    class 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が格納される。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む