概要
業務で以下のようなコードをよく見かけます。
- DBから取得したデータを配列でViewに渡す。
- Viewで色々制御している。
class ProductController
{
public function idAction($id): void {
$this->products = $this->get('product_model')->fetchProductById($id);
}
}
class ProductModel
{
public function fetchProductById($id): array {
// DBからデータを配列で取得する
return $qb->getArrayResult();
}
}
// View
<ul>
<?php foreach ($this->products as $product): ?>
<li>
<?= escape($product['name']) ?><br />
// 税込みであれば税額を足す
<?php if ($product['in_tax'] === true): ?>
<?= escape($product['price'] + $product['tax']) ?><br />
<?php else: ?>
<?= escape($product['price']) ?><br />
<?php endif; ?>
// サムネイルが複数設定されていればループ
<?php if (is_array($product['thumbnail'])): ?>
<?php foreach ($product['thumbnail']) as $thumbnail): ?>
<img src="<?= escape($thumbnail) ?>" /><br />
<?php endforeach; ?>
<?php else: ?>
<img src="<?= escape($product['thumbnail']) ?>" />
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
上記のコードには、いくつかのデメリットがあります。
- Viewが複雑になりがちで、必須のパラメータなどのデータの制約が読み取りづらい。
- PHPの配列はどんな値でも入ってしまうため、プログラムが壊れやすい。
- 配列のキーをIDEが補完してくれないため、毎回キー名を確認しないといけない。
- 一般的にViewはテストしにくいため、テストの負担が増える。
これらのデメリットは、DTOと呼ばれるクラスを作成することで改善できます。
DTOについては、以下の記事の解説がわかりやすいので、ご参照ください。
https://qiita.com/sagaraya/items/96708cd451021fb040b7
DTOを使って書き換えてみる
先ほどのコードをDTOを使ったパターンに書き換えてみます。
class ProductController
{
public function idAction($id): void {
$this->products = $this->get('product_model')->fetchProductById($id);
}
}
class ProductModel
{
public function fetchProductById($id): Product[] {
// DBからデータを配列で取得する
$result = $qb->getArrayResult();
$products = [];
foreach ($result as $row) {
// DTOを生成して返す
$product = new Product();
$product->setName($row['name']);
$product->setPrice($row['price']);
...
$products[] = $product;
}
return $products;
}
}
// これがDTO
class Product
{
private $name;
private $price;
...
public function setName($name) {
$this->name = $name;
}
public function setPrice($price) {
$this->price = $price;
}
...
public function getName(): string {
return $this->name;
}
public function getPrice(): int {
$price = $this->price;
if ($this->in_tax) {
$price += $this->tax;
}
return $price;
}
public function getThumbnails(): string[] {
if (!is_array($this->thumbnail)) {
return [$this->thumbnail];
}
return $this->thumbnail;
}
...
}
// View
<ul>
<?php foreach ($this->products as $product): ?>
<li>
<?= escape($product->getName()) ?><br />
// Viewでの制御が不要になる。
<?= escape($product->getPrice()) ?><br />
// サムネイルは必ず配列になるので、安心してループできる。
<?php foreach ($product->getThumbnails() as $thumbnail): ?>
<img src="<?= escape($thumbnail) ?>" /><br />
<?php endforeach; ?>
</li>
<?php endforeach; ?>
</ul>
まとめ
DTOを使うことにより、以下のようなメリットが生まれました。
- Viewからifが消え読みやすいコードに。データの制約もDTOに集約されるため読みやすい。
- DTOクラスが型を担保してくれるので、プログラムが壊れにくい。
- IDEがgetterやsetterを補完してくれるため、スピーディにコーディングできる。
- DTOはただのクラスなのでテストしやすい。
参考にしていただければと思います。