13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

運動通信社 AdventCalendar 2021Advent Calendar 2021

Day 1

Laravel PJに値オブジェクトを導入してちょっと幸せになった話

Last updated at Posted at 2021-11-30

はじめに

  • 最近、開発チームでテックブログを書こうという取り組みをはじめたもので折角ならとアドベントカレンダーもやってみることにしました!
  • テーマをしぼれるだけの下地がないのでノーテーマでお送りしますこちらのアドベントカレンダー記念すべき一発目よろしくお願い致します。
  • Laravelプロジェクトに値オブジェクトを導入したら、ちょっと幸せになったというお話です。

結論

  • まずはバリデーションに値オブジェクトを導入してみるだけでも幸せになれました。

たとえばのお話

ある日

  • たとえばユーザーという概念のあるWebサービスを開発しているとして、最大10文字の名前をつけることができるということにします。
  • まずはユーザーの登録機能を作っていきましょう。
UsersController.php
public function register (UserRegisterRequest $request)
{
    // ユーザー登録処理
}
UserRegisterRequest.php
public function rules()
{
    return [
        'name' => [
            'required',
            'max:10',
        ],
     'email' => [
            'required',
            // 略
        ],
        // 上記以外の項目のルール
    ];
}

※ 簡略化の為、Controllerクラス、Requestクラスだけにしてます。

  • Laravelでバリデーションを実施しようと思った際、便利なのがRequestクラスでして上記のように書くだけで名前における制約「最大10文字」を満たすことができます。
  • では、つぎに登録したユーザーの名前を変更する機能を追加する場合はどうでしょうか?
UsersController.php
public function register (UserRegisterRequest $request)
{
    // ユーザー登録処理
}

public function updateName (UserUpdateNameRequest $request)
{
    // ユーザー名変更処理
}
UserUpdateNameRequest.php
public function rules()
{
    return [
        'name' => [
            'required',
            'max:10',
        ]
    ];
}
  • 名前だけを変更する機能ということですのでControllerクラスへのメソッド追加に加え、新しいRequestクラス「UserUpdateNameRequest」クラスを作ります。
  • これで登録後に名前を変更できるようになりました。

それから時が経ったある日

  • ユーザー名を15文字許容してほしいという要望があったとしましょう。
  • 恐らく西武ライオンズの「タイシンガーブランドン大河」選手でも登録できるようするという意図の仕様変更でしょう。
  • 以前に作成したユーザー登録処理と名前変更処理の修正をする場合、それぞれのRequestクラスの max:10 の部分を max:15 にすれば良いのですが、これだと変更が発生するたびに漏らさず修正する必要がありますし、名前を扱う処理が増えるたび修正箇所が増えてしまいます。
  • 私は2箇所でも共通化したいタイプなのでもうこの時点でリファクタリングしたくてたまりません。

値オブジェクトの導入により解決

Name.php
class Name
{
    private const MAX_LENGTH = 15;
    private $value;

    public function __construct(string $value)
    {
        if (!$value) {
            throw new \Exception('値が存在しない');
        }

        if (self::MAX_LENGTH < mb_strlen($value)) {
            throw new \Exception('文字数オーバー');
        }
            
        $this->value = $value;
    }

    public function getValue(): string
    {
        return $this->value;
    }
}
UserNameRule.php
<?php
namespace App\Rules\User;

use App\Domain\Object\ValueObjects\User\Name;
use Illuminate\Contracts\Validation\Rule;

class UserNameRule implements Rule
{
    // 略

    public function passes($attribute, $value): bool
    {
        try {
            new Name($value);

            return true;
        } catch (\Exception $e) {
            $this->message = $e->getMessage();
            return false;
        }
    }

    // 略
}
UserRegisterRequest.php
public function rules()
{
    return [
        'name' => [
            'required',
            new UserNameRule(),
        ],
      'email' => [
            'required',
            // 略
        ],
        // 上記以外の項目のルール
    ];
}
  • だいぶ雑ですが上記のようなクラスを導入することで修正箇所をまとめることができました。
  • 値オブジェクトを用いたカスタムバリデーションを実装する為にRuleクラスを用意しています。
  • これで、メジャーリーガーの「ダルビッシュ・セファット・ファリード・有」(ダルビッシュ有)投手に対応する為20文字まで可とする場合にも1行の修正で事足ります。(ころころ名前の文字数上限変えられても困りますが)
Name.php
class Name
{
    private const MAX_LENGTH = 20;

    // 略
}

おわりに

  • 私個人として昨今、興味のあるドメイン駆動設計についてのお話でした。
  • この記事ではバリデーション実装にフォーカスした話でしたが、そこに限らず値オブジェクト・エンティティを実際に導入してみて気持ちの部分含め変更が容易になったと感じると共に表現の幅が広がっていることを感じています。
13
9
1

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
13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?