エンジニアのひよこ_level10

毎日更新してた人。たまに記事書きます。

【Laravel】Modelクラスでクエリビルダが動く理由【409日目】

モデルでクエリビルダ?

モデルのクラスって、テーブルのレコードをオブジェクトにしたようなイメージでした。

でも、以下のようなコードを見ることがあると思います。

User::select('id')->first();

あれ?selectってクエリビルダだよな・・・

このUserってなんだ?クエリビルダ?モデルのクラス???

元コードを読んで見る

Modelクラス

    /**
     * Handle dynamic method calls into the model.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        if (in_array($method, ['increment', 'decrement'])) {
            return call_user_func_array([$this, $method], $parameters);
        }

        $query = $this->newQuery();

        return call_user_func_array([$query, $method], $parameters);
    }

    /**
     * Handle dynamic static method calls into the method.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public static function __callStatic($method, $parameters)
    {
        $instance = new static;

        return call_user_func_array([$instance, $method], $parameters);
    }

なにしてるの?

__callStatic

__callStaticはマジックメソッド。

静的呼び出しをされたときに使われます。

User::select('id')

このとき、 new staticでモデルのインスタンスを作成。そして call_user_func_arrayで モデルクラスのインスタンスが、 selectの関数を 'id'のパラメーターを引き継いで実行。

__call

__callStatic()によって、静的呼び出しだったものが、モデルクラスのインスタンスから呼び出されることになります。

次は、 __callです。静的呼び出しじゃなくなったので。

そこでは、メソッド名が ['increment', 'decrement']のどちらかか確認して、合っていれば、そのモデルクラスのメソッドを呼び出します。

なければここ! newQueryでクエリビルダ用にインスタンスを作って、そのインスタンスselectの関数を呼び出します!

Laravelの元コードを読むのは面白いよ!

ってことで、案外簡単に追うことができました。

Laravelの元コードは、こんな書き方あるのか!と勉強になることがたくさんあるので、是非読んでみてください!

このデザインパターン使ってるのか・・・!ってなることたくさんあります。おすすめです。

参考

PHP: in_array - Manual

PHP: call_user_func_array - Manual

【Laravel】Eloquentのcreateとfillの違いとは【336日目】

■注意

コードは5.2のコードです。ですが、そんなにバージョン上がっても変わってないはずです。

■結論

基本的にcreateで大丈夫

■違い

1.createはどうせfill通るので、fillableやguardedの部分は両方使う

2.createは、静的メソッドで、以下までしてくれる

1. インスタンス化
2. 値の確認・保存
3. インスタンスを返す

3.fillは、インスタンスを用意して、fillして、save()を呼び出すの3工程が必要

■使い方の違い

create

$user = User::create(['name' => 'Uiro']);

fill

$user = new User();

$user->fill(['name' => 'Uiro']);

$user->save();

■元コード+私のコメント

createがfillを動かしてるのは以下のコメント参照

   /**
    * Save a new model and return the instance.
    *
    * @param array $attributes
    * @return static
    */
    public static function create(array $attributes = [])
      {
        $model = new static($attributes); //ここで、呼び出し元をインスタンス化してる。なので下の__constructを通る

        $model->save();

        return $model;
      }

    /**
     * Create a new Eloquent model instance.
     *
     * @param  array  $attributes
     * @return void
     */
    public function __construct(array $attributes = [])
    {
        $this->bootIfNotBooted();

        $this->syncOriginal();

        $this->fill($attributes); //fillをしている
    }

fillはsaveまでしてないので、saveを書かないといけない。

また、静的メソッドではないので、インスタンス化してください。

/**
     * Fill the model with an array of attributes.
     *
     * @param  array  $attributes
     * @return $this
     *
     * @throws \Illuminate\Database\Eloquent\MassAssignmentException
     */
    public function fill(array $attributes)
    {
        $totallyGuarded = $this->totallyGuarded();

        foreach ($this->fillableFromArray($attributes) as $key => $value) {
            $key = $this->removeTableFromKey($key);

            if ($this->isFillable($key)) {
                $this->setAttribute($key, $value);
            } elseif ($totallyGuarded) {
                throw new MassAssignmentException(sprintf(
                    'Add [%s] to fillable property to allow mass assignment on [%s].',
                    $key, get_class($this)
                ));
            }
        }

        return $this;
    }

■使い分け

1.インスタンスが既にある
2.saveを後回しにしたい

ならfillを使って、基本的にはcreateで良いと思います。

プログラミングにおける『依存』『密結合』とは【326日目】

こんなワード聞きませんか

『このクラスは、このクラスに依存しているね』
『このクラスとこのクラス、密結合になってるな・・・』

依存って何なんでしょう。
イメージとしては、
『○○さんがいないと、私生きていけない・・・』 みたいなものが思い浮かぶと思います。

だいたいあってます。

依存しているコードの例

【例はPHPですが、依存の話は別言語でも同様のお話です】

Child.php

class Child {
    private $name;

    public function __construct(Parent $parent)
    {
        $this->name = $parent->generate_name();
    }

    public function play_game(Friend $friend)
    {
        //
    }
}

これは、ChildはParentに依存しています。
play_game()もFriendに依存していますね。

Friendがいないと、遊ぶことが出来ない。
Parentに名前つけてもらわないと、生まれることすら出来ない。

こんなふうに、コンストラクタやメソッドの引数に、特定のクラスがあるなどして、
そのクラスが存在しないと生きられない!

それが依存です。

相互依存の『密結合』

依存というのは一方的なものです。

『〇〇さんがいないと、私生きていけない・・・』
は片方の人が言うセリフですね。

しかし、お互いがお互いに依存している場合もあります。
それが密結合と呼ばれます。

片方バグ起こると、共倒れしてしまうやーつです。

余談

普段はプログラミングでは『疎結合』にしようとか、
極力『密結合』を避けようとしますよね。

では、『密結合』でなければ何が嬉しいの?

『〇〇さんがいないと、私生きていけない・・・』
と言われても、その〇〇さんは普通に生きていけます。

他にもバグのチェックも楽だったりと良いことがあるのですが、
それはまた次の機会に書きます。

【Laravel】download関数のpathToFileの落とし穴【321日目】

ユーザーにファイルをダウンロードさせたい

http://domain_name.com/storage/images/upfile.jpegで画像が出るものをダウンロードさせたい。

return response()->download('/storage/images/upfile.jpeg');

あれ?なぜか動かない。

最初のスラッシュは不要

return response()->download('storage/images/upfile.jpeg');

これでダウンロード出来ました。

【Laravel】Cookieを付与する。view関数でbladeも使えます【320日目】

こんな時

LaravelでCookieを付与したい。
(この保存方法だけでは、暗号化された状態で保存されます。なので、ユーザーは内容がわからないです。)

書き方

return response()
    ->view('blade_file_name')
    ->cookie('cookie_name', 'value', 30);

何をしてるの

blade_file_name.blade.phpを使って画面を表示しながら、Cookie内のcookie_nameにvalueという値を保存する。

また、Cookieの有効期限は、この場合30分になります。

viewに変数も渡したい

compact関数とかをいつもどおり追加すればいいです。

return response()
    ->view('blade_file_name', compact('number', 'input'))
    ->cookie('cookie_name', 'value', 30);

【nginx+phpfpm】Laravelでpublic配下のファイルが取得出来ない【306日目】

こんなことがありました。

docker-composeで管理、nginx+php-fpmで環境構築しました!

Laravelのチュートリアルページ表示されました!

・・・favicon.icoが反映されない?
public/app.cssや、public/app.jsが読み込めない・・・

起こってること

phpは、nginxの設定で通信をしてからリクエストを返してるので問題ない。
だから、phpファイルだけはうまくいく。

問題は、try_filesでファイルを取得しているところっぽい。

解決

nginxのコンテナに、public配下のファイルを置いていなかった(マウントしてなかった)せいでした。

今回は、docker-compose.ymlの方の設定ミス。

  nginx:
    container_name: study-laravel_nginx
    build:
      context: ./nginx
    volumes:
      - ./web:/var/www
    ports:
      - "8000:80"
    links:
      - php-fpm

volumesのパスを書き間違えてました・・・つっら・・・

おまけ:そんなこんなで作ったプルリク

ってことで、詰まって出来上がった、Laravelでbootstrapまで呼び出す実装のプルリク。参考に。

github.com

【Laravel】URLが『/hoge』のように、.phpが要らない理由【291日目】

普通のページなら

phpの普通のページだと、URLは

https://domain.jp/hoge/huga.php

のように、phpをつける必要がありました。

では、なぜLaravelはそれが必要ないのでしょう。

必ずindex.phpを開いてるから

これはURLがなにであろうと、ドキュメントルートのindex.phpを開いているからです。

https://domain.jp/hoge/huga

と書いていても、開くのは、ドキュメントルートのindex.phpです。

そして、そのindex.phpが、route.phpを読み込んで、目的のページを表示させています。

では、どうやって必ずindex.phpを読み込ませているのか

.htaccessに秘密が

    RewriteEngine On

    ## Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)/$ /$1 [L,R=301]

    ## Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]

URLの最後がスラッシュなら、残りを第一引数に渡したり、
強制的にindex.phpを呼び出したり。

あーここでいろいろしてたんですね。

nginxの場合

ちなみに、nginx+php-fpmとかで構築してるときには、nginxの方でも設定されてることもあります。

詳しくはこちらへ

willow710kut.hatenablog.com

【PHP】cloneを使う時って?オブジェクトはそのままコピーできない?【267日目】

オブジェクトのコピーはできない?

$item = new Item();
$item->value = 1;

// コピー?
$item2 = $item;

$item2->value = 2;

この時、$itemと$item2の値はどうなるでしょう?

実は両方一緒

// 2
$item->value;
// 2
$item2->value;

あれ?なんで?

インスタンスの変数のコピーは、実際は参照を渡しているだけ

// コピー?
$item2 = $item;

ここが問題。

この$itemの中には、インスタンスの『参照』を持っている。

つまり、 new Item();の保存先。実際の値は違いますが、
0x4842ffaaみたいに、住所が保存されています。

だから、住所をコピーしたところで、住所の先の家が変わるわけではないので、
$itemと$item2の中身は実質一緒ってことになります。

だから、 $item2->value = 2;で、$itemも$item2も両方書き換わったわけですね。

じゃあどうするの?

$item2 = clone $item;

これでOK。 clone $itemで、同じ内容で、新しいオブジェクトを作ってくれる。
その参照先を$item2に保存するので、$itemと$item2は異なる値になる。

こういう時に、cloneを使うんですね。

参照先

PHP: オブジェクトのクローン作成 - Manual