LoginSignup
9
10

More than 3 years have passed since last update.

DTOのススメ

Last updated at Posted at 2019-12-24

概要

業務で以下のようなコードをよく見かけます。

  • 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はただのクラスなのでテストしやすい。

参考にしていただければと思います。

9
10
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10