配列とオブジェクト、データをどちらで持つか
配列とオブジェクトの使い分けとは、構造が固定で複数箇所を流れるデータはDTOなどのクラスに、その場限りの一時データやキーが動的なものは連想配列に、と持ち方を選ぶ設計の勘所です。
連想配列は手軽だが構造が見えない
ちょっとしたデータを持ち回るとき、連想配列はとにかく手軽です。ただ、規模が育ってくると弱点が出てきます。どんなキーがあるのか、値の型は何なのかがコードから読み取れず、タイプミスしたキーもエラーにならず null がすり抜ける。「暗黙の構造」が頭の中にしかない状態になりがちなんですね。
$user = ['name' => '太郎', 'age' => 30];
echo $user['nmae']; // タイプミスでも警告止まり、null が返る
// age が数値である保証もどこにもない
DTOにすると構造が型で見える
形が決まっているデータなら、素直にクラス(DTO)にしてしまうのが後々ラクだと思います。プロパティに型を付けておけば、キーの綴りはIDEが補完してくれるし、存在しないプロパティに触ればすぐ気づけます。何より、そのデータが何を持つのかがクラス定義を見れば一目で分かるのが大きいですね。
final class User
{
public function __construct(
public readonly string $name,
public readonly int $age,
) {}
}
$user = new User('太郎', 30);
echo $user->name; // 補完が効き、綴り間違いは即エラー
使い分けの目安
私の感覚では、構造が固定で複数箇所を流れていくデータはオブジェクト、その場限りの一時的なまとめや、キーが動的に決まるものは配列、という切り分けです。APIのレスポンスを受けてドメインに渡すような境界では、配列で受けてすぐDTOに詰め替えると、以降は型に守られた世界で作業できて安心ですね。
// 外部から来た配列を、境界でDTOへ詰め替える
function fromArray(array $row): User
{
return new User(
name: (string) $row['name'],
age: (int) $row['age'],
);
}
まとめ
配列が悪いわけではなく、寿命の短い一時データには配列がちょうどいい場面も多いと思います。ただ、あちこちを流れていく構造の決まったデータは、早めにDTOへ移すとタイプミスや型の不整合から解放されます。「このデータ、どんなキーがあったっけ」と何度も見返している自分に気づいたら、型にする合図だと受け取るようにしています。
よくある質問
Q. 連想配列の何が問題になるのですか?
A. どんなキーや型があるかがコードから読み取れず、キーのタイプミスもエラーにならず null がすり抜けます。構造が頭の中にしかない状態になりがちです。
Q. 外部APIのレスポンスはどう扱うといいですか?
A. 配列で受けて、境界ですぐDTOへ詰め替えるのがおすすめです。以降は型に守られた世界で作業でき、綴り間違いや型の不整合から解放されます。