【CakePHP3】toArray()とtoList()の挙動の違い

2020/04/12

author

masyus

CakePHP3系で、ResultSetやCollectionにしたデータを配列に変換する時はtoArray()を使うことが多いと思いますが、時にはtoList()を使うこともあると思います。両者の違いは何なのか、よく忘れやすいので改めて調べてみました。検証したCakePHPのバージョンは3.6.13、PHPは7.3です。

toArray()はキーを保持し、toList()はキーを振り直す

結論から書きますと、

  • toArray():キーを保持した状態で配列を返す
  • toList():キーを連番で振り直した状態で配列を返す

という違いがあります。例えば

<?php
namespace App\Controller;

use App\Controller\AppController;
use Cake\View\View;
use Cake\Collection\Collection;

class ArticlesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
    }

    /**
     * デバッグ
     */
    public function index()
    {
        $data = [
            'a' => [
                'id' => 1,
                'name' => 'hoge',
            ],
            'b' => [
                'id' => 2,
                'name' => 'hogehoge',
            ],
            'c' => [
                'id' => 3,
                'name' => 'fuga',
            ],
            'd' => [
                'id' => 4,
                'name' => 'fugafuga',
            ],
        ];

        $collection = new Collection($data);
        pr($collection->toArray());
        pr($collection->toList());
        exit;
    }
}

というコードがあったとします。実行結果は下記になります。

// toArray()の中身:
Array
(
    [a] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [b] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [c] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [d] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

)

// toList()の中身:
Array
(
    [0] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

)

確かにtoList()の時はキーが連番で振り直されています。

toArray()とtoList()の使い分け

基本的にはtoArray()の使用ででほぼ事足りると思います。ORMからDBへSELECT文を投げた後、ヒットした複数のレコードをEntityに変換して取得できるResultSetオブジェクトでは、そもそもキーが連番で生成されます。その為、toArray()toList()で結果が変わりません。似た状況を再現すると、下記になります。

        $data = [
            // キーが数字の連番になっていることに注目
            0 => [
                'id' => 1,
                'name' => 'hoge',
            ],
            1 => [
                'id' => 2,
                'name' => 'hogehoge',
            ],
            2 => [
                'id' => 3,
                'name' => 'fuga',
            ],
            3 => [
                'id' => 4,
                'name' => 'fugafuga',
            ],
        ];
        $collection = new Collection($data);
        pr($collection->toArray());
        pr($collection->toList());
        exit;

実行結果:

// toArray()の中身:
Array
(
    [0] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

)

// toList()の中身:
Array
(
    [0] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

)

CollectionへappendItem()等した場合の挙動

ですが、何らかのデータをCollectionへ手動追加した場合、追加の仕方でtoArray()toList()の中身に違いが生じます。例えば下記です。

        $data = [
            // キーが数字の連番
            [
                'id' => 1,
                'name' => 'hoge',
            ],
            [
                'id' => 2,
                'name' => 'hogehoge',
            ],
            [
                'id' => 3,
                'name' => 'fuga',
            ],
            [
                'id' => 4,
                'name' => 'fugafuga',
            ],
        ];
        $collection = new Collection($data);
        $appendData = [
            'id' => 5,
            'name' => 'hogefuga'
        ];
        $collection = $collection->appendItem($appendData); // 第2引数でキー指定なし
        pr($collection->toArray());
        pr($collection->toList());
        exit;

実行結果:

// toArray()の中身:
Array
(
    // 先頭のitemが$appendDataで上書きされてしまう
    [0] => Array
        (
            [id] => 5
            [name] => hogefuga
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

)

// toList()の中身:
Array
(
    [0] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

    // appendの挙動の期待通りに、後方へ$appendDataが追加される
    [4] => Array
        (
            [id] => 5
            [name] => hogefuga
        )
)

この場合はtoList()を使うのが適切だと分かります。

また、appendItem()で第2引数に適切なキー番号を指定した場合はtoArray()toList()の中身が合致します。

        $data = [
            // キーが数字の連番
            [
                'id' => 1,
                'name' => 'hoge',
            ],
            [
                'id' => 2,
                'name' => 'hogehoge',
            ],
            [
                'id' => 3,
                'name' => 'fuga',
            ],
            [
                'id' => 4,
                'name' => 'fugafuga',
            ],
        ];
        $collection = new Collection($data);
        $appendData = [
            'id' => 5,
            'name' => 'hogefuga'
        ];
        $collection = $collection->appendItem($appendData, 4); // 第2引数で適切なキーを指定
        pr($collection->toArray());
        pr($collection->toList());
        exit;

実行結果:

// toArray()の中身:
Array
(
    [0] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

    [4] => Array
        (
            [id] => 5
            [name] => hogefuga
        )

)

// toList()の中身:
Array
(
    [0] => Array
        (
            [id] => 1
            [name] => hoge
        )

    [1] => Array
        (
            [id] => 2
            [name] => hogehoge
        )

    [2] => Array
        (
            [id] => 3
            [name] => fuga
        )

    [3] => Array
        (
            [id] => 4
            [name] => fugafuga
        )

    [4] => Array
        (
            [id] => 5
            [name] => hogefuga
        )

)

確かにtoArray()toList()の中身が合致しました。

実際の開発においてはappendItem()を使う機会はさほど無いと思いますが、もしappendItem()もしくはprependItem()をCollectionに対して実行する場合、キーの指定と配列変換方法次第で中身が変わることを念頭に入れておくと良いと思います。

参考