浜松のWEBシステム開発・スマートフォンアプリ開発・RTK-GNSS関連の開発はお任せください
株式会社シーポイントラボ
TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:静岡県浜松市中区富塚町1933-1 佐鳴湖パークタウンサウス2F

【Laravel】Collection中のitemが未定義でもnullでもないか検査したい時はisset()でなくget()!==nullを使うべき

 CollectionはLaravelの持つ配列をラッピングしたクラスです。Collectionを用いることによってPHPの素の配列操作よりも多彩な配列操作を一貫性を保ったまま容易に実現できます。

Illuminate\Support\Collectionクラスは配列データを操作するための、書きやすく使いやすいラッパーです。以下の例をご覧ください。配列から新しいコレクションインスタンスを作成するためにcollectヘルパを使用し、各要素に対しstrtoupperを実行し、それから空の要素を削除しています。
コレクション 5.7 Laravel

 Collectionは素晴らしいですが、題名にあるisset()の関連部分では特殊な挙動を見せます。

$arr = ['hoge' => null];
dump(isset($arr['hoge']));// false
dump(isset($arr['fuga']));// false

$col = new \Illuminate\Support\Collection(['hoge' => null]);
dump(isset($col['hoge']));// true
dump(isset($col['fuga']));// false

 Collectionを介したせいで結果が素の配列と異なってしまいました。これはCollectionのArrayAccessインタフェースの実装の仕方に原因があります。
 ArrayAccessインタフェースはオブジェクトを配列として呼び出すためのインタフェースです。これを実装すると次の画像の様にオブジェクトを配列として呼び出せます。
 
PHP: ArrayAccess – Manual
 ArrayAccessの中でissetと直接的に絡んでいるメソッドがoffsetExistsです。

このメソッドが実行されるのは、ArrayAccess を実装したオブジェクト上で isset() あるいは empty() を使用した場合です。
PHP: ArrayAccess::offsetExists – Manual

 \Illuminate\Support\CollectionのArrayAccess::offsetExistsは次の様に実装されています。

    /**
     * Determine if an item exists at an offset.
     *
     * @param  mixed  $key
     * @return bool
     */
    public function offsetExists($key)
    {
        return array_key_exists($key, $this->items);
    }

 $this->itemsがCollection内で扱われている配列です。これはkeyが存在すれば値がnullであってもissetでtrueを返すということです。このため

$col = new \Illuminate\Support\Collection(['hoge' => null]);
dump(isset($col['hoge']));// true

となります。
 一見バグの様に見えますが、Determine if an item exists at an offset.とコメントされている上、実装内容を

    public function offsetExists($key)
    {
        return isset($this->items[$key]);
    }

と変えるとこの変更が原因でLaravel自体のテストで失敗が起きます。つまり仕様です。

 このアンチパターンと化したisset()の代わりになる判定方法が次のコードです。

$col = new \Illuminate\Support\Collection(['hoge' => null]);
dump($col->get('hoge') !== null);// false
dump($col->get('fuga') !== null);// false

https://implode.io/5yDeL4
 Collection中のgetメソッドを用います。getメソッドはキーの値を返すメソッドです。次の引用の様にキーが存在しない場合、例外でなくnullを返します。

getメソッドは指定されたキーのアイテムを返します。キーが存在していない場合はnullを返します。

 これによりgetメソッドの返り値はキーの値がnullであっても、キーが存在しなくともnullになります。これでissetと同じく、変数がセットされていること、そしてNULLでないことを検査することができます。

  • この記事いいね! (0)