LaravelのCollection(コレクション)のforeachについて

Posted in Laravel on 9月 20, 2016

LaravelのCollectionとは

Laravelでは、、Eloquent ORMを用いてデータベースからデータを取得するとCollectionというオブジェクトで取り出されます。

$brands = Brand::all();

dd($brands);
Collection {#1811 ▼
  #items: array:8 [▼
    0 => Brand {#1812 ▶}
    1 => Brand {#1813 ▶}
  ]
}

Collectionは、オブジェクトにも関わらず配列のようにforeach処理もできる上、さまざまなメソッドが用意されているため、データベースから取り出したデータを簡単に操作することができます。

foreach ($brands as $brand){

echo $brand->brand.'<br>';

}
Nike
Asics
Adidas

Collectionのメソッドについてはまた別の機会に譲り、今回は、なぜオブジェクトのCollectionが配列のようにforeach処理ができるのかを確認し、Collectionの理解を深めたいと思います。

Collectionが実装するインターフェイス

Collectionがどのようなインターフェイスを実装しているかを確認してしましょう。

Laravel5.1のAPIを確認するとCollectionは、 ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializableの6つのインターフェイスを実装していることがわかります。

6つのインターフェイスを調べれば、Collectionがどのような振る舞いをするのか見えてくるはずです。

この中で、foreachに関連するインターフェイスがIteratorAggregateになります。

実際にオブジェクトがインターフェイスIteratorAggregateを実装すれば、foreach処理を行うことができるのかを確認してみましょう。

IteratorAggregateを実装していない場合

Nike, Acics ,Addidasの値を入れて作成した3つのオブジェクトを変数$brandsの要素として配列を作成します。

配列$brandsを入れて、Makerクラスから$makerを作成します。

Collection.php

<?php

Class Maker{

    protected $brands;

    public function __construct(array $brands){

    $this->brands = $brands;

}

Class Brand{

    public $brand;
    public $brand_name;

    public function __construct($brand, $brand_name){

           $this->brand = $brand;
           $this->brand_name = $brand_name;
    }

}

$brands[] = new Brand('Nike','ナイキ');
$brands[] = new Brand('Asics','アシックス');
$brands[] = new Brand('Addidas','アディダス');

foreach($maker as $brand){

        var_dump($brand);

}

$makerをforeach処理しても何も出力されません。

$ php collection.php

IteratorAggregateを実装した場合

次にimplementsを使用して、MakerクラスにIteratorAggregateを実装します。

<重要> IteratorAggregateは、抽象メソッドgetIteratorが定義されているので、 IteratorAggregateを実装する場合は、getIteratorメソッドを追加する必要があります。 追加していな場合は、エラーが発生します。

public function getIterator(){

return new ArrayIterator($this->brands);

}

collection.php

<?php

Class Maker implements IteratorAggregate{

public $brands;

public function __construct(array $brands){

$this->brands = $brands;

}

public function getIterator(){
return new ArrayIterator($this->brands);

}

}

Class Brand{

    public $brand;
    public $brand_name;

    public function __construct($brand, $brand_name){

           $this->brand = $brand;
           $this->brand_name = $brand_name;
    }

}

$brands[] = new Brand('Nike','ナイキ');
$brands[] = new Brand('Asics','アシックス');
$brands[] = new Brand('Addidas','アディダス');

$maker = new Maker($brands);

foreach($maker as $brand){

        var_dump($brand);

}

IteratorAggregate実装すると先程行えなかったforeach処理を行うことができます。

$ php collection.php
object(Brand)#1 (2) {
  ["brand"]=>
  string(4) "Nike"
  ["brand_name"]=>
  string(9) "ナイキ"
}
object(Brand)#2 (2) {
  ["brand"]=>
  string(5) "Asics"
  ["brand_name"]=>
  string(15) "アシックス"
}
object(Brand)#3 (2) {
  ["brand"]=>
  string(7) "Addidas"
  ["brand_name"]=>
  string(15) "アディダス"
}

Collectionが配列のようにforeach処理を行うことができるのは、IteratorAggregateインターフェイスが実装されているためだということがわかりました。

Laravel以外でもオブジェクトにforeach処理を実行させたい時は、IteratorAggregateインターフェイスを実装して、実現して下さい。