LoginSignup
26
6

More than 1 year has passed since last update.

MVCモデルのViewにDTOを導入するとどうなるか

Last updated at Posted at 2021-12-08

はじめに

株式会社じげんのアレクセイです
プロパンガス会社の情報・見積もり比較サービス エネピの開発を担当しています。

今回は、Viewでの作業を楽にするためにDTOの導入について書いていきます。
コントローラーで取得したモデルをそのままViewへ渡すことが多いと思いますが、Viewでモデルのリレーションを呼び出したり、モデルのプロパティによって分岐処理を入れたりします。分岐が複雑になり、同じ分岐が増え、だんだんViewのソースが読みづらくなっていきます。ビズネスロジックが変わったら何箇所を直さないといけないことになります。定期的にこういう依頼が来るとViewでの作業が大変です。そしてリファクタリングの時にモデルのリレーションやプロパティをなくすか変更する場合はエラーが起こりやすくなります。

始める前に

この記事のソースコードは実際に使われているものではないので、以下の画像をHTMLページとしてメージしています。
Screen Shot 2021-11-30 at 18.17.05.png

DTOとは?

【Data Transfer Object】オブジェクト指向プログラミングでよく用いられる典型的なオブジェクトの設計パターン(デザインパターン)の一つで、関連するデータを一つにまとめ、データの格納・読み出しのためのメソッドを定義したオブジェクトのことです。異なるプログラム間やコンピュータ間でひとまとまりのデータを受け渡す際に用いられます。
引用:https://e-words.jp/w/DTO.html

DTOを導入する前のView

view.html
@if ($book->author)
  @if ($book->author->firstName && $book->author->lastName)
  <p>{{ $book->author->firstName }} {{ $book->author->lastName }}</p>
  @else
    <p>Unknown Author</p>
  @endif
<p>{{ $book->author->registeredDate->format('Y-m-d') }}</p>
@endif

<p>Price: {{ $book->price ? number_format().'円' : 'Sold out' }}</p>
<a href="/buy-me">{{ session('ab.test') ? 'Buy now' : 'Buy' }}</a>

@if ($book->images)
  @foreach ($book->images as $image)
  <div><img src="{{ $image->url }}"></div>
  @endforeach
@elseif
  <div><img src="no-image.jpg"></div>
@endif

DTOオブジェクトを用意する

dto.php

class BookShowDto extends AbstractDto
{
    /**
     * @var bool
     */
    public $showAuthorSection;

    /**
     * @var string
     */
    public $authorFullName;

    /**
     * @var mixed|string|null
     */
    public $authorRegisteredDate;

    /**
     * @var array
     */
    public $bookImages = [];

    /**
     * @var string
     */
    public $bookPrice;

    /**
     * @var string
     */
    public $buyButtonText = 'Buy'; // or HTML
}

DTOオブジェクトのプロパティを設定する

logic.php
$dto = new BookShowDto;

if ($book->author) {
  $dto->showAuthorSection = true;
  $dto->authorFullName = $book->author->firstName.$book->author->lastName;

  if (!$dto->authorFullName) {
    $dto->authorFullName = 'Unknown Author';
  }
  $dto->authorRegisteredDate = $book->author->registeredDate->format('Y-m-d');
}

$dto->bookPrice ? $book->price ? number_format().'円' : 'Sold out';

@foreach ($book->images as $image) {
  $dto->bookImages[] = $image->url;
}

// Default image
empty($dto->bookImages) and $dto->bookImages[] = '/images/no-image.jpg';

// AB testing
session('ab.test') and $dto->buyButtonText = 'Buy now';

DTOを導入する後のView

view.html
@if ($dto->showAuthorSection)
  <p>{{ $dto->authorFullName }}</p>
  <p>{{ $dto->authorRegisteredDate }}</p>
@endif

<p>Price: {{ $dto->bookPrice }}</p>
<a href="/buy-me">{{ $dto->buyButtonText }}</a>

@foreach ($dto->bookImages as $url)
<div><img src="{{ $url }}"></div>
@endforeach

まとめ

単純なページであればメリットをあまり感じませんが、リレーションが多い、大量な分岐が入っているページならViewとビジネスロジックの間にDTOを置けば作業が楽になるかと思います。実際のプロジェクトに導入したら以下のメリットを感じました。
① Viewが読みやすい。
② 多くの分岐が1箇所にまとまっている、HTMLと混ざっていないので、HTMLだけでなくphpのロジックも読みやすい。
③ DTOのデフォルト値を設定できるので、リファクタリングの際に値の設定を忘れたり、リレーションが変わる際にもエラーが起きない。
④ ビジネスロジックが重かったらDTOをそのまま簡単にキャッシュに乗せられる。
⑤ ボタンや文言などのABテストの実装がやりやすい。

みなさんも是非試してみてください。

26
6
0

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
26
6