Validation of Entities

Doctrine ORM does not ship with any internal validators, the reason being that we think all the frameworks out there already ship with quite decent ones that can be integrated into your Domain easily. What we offer are hooks to execute any kind of validation.

Doctrine ORM には内部バリデーターが同梱されていません。その理由は、そこにあるすべてのフレームワークには、ドメインに簡単に統合できる非常に適切なフレームワークが既に同梱されていると考えているからです。私たちが提供するのは、あらゆる種類の検証を実行するフックです。

Note

ノート

You don’t need to validate your entities in the lifecycle events. Its only one of many options. Of course you can also perform validations in value setters or any other method of your entities that are used in your code.

ライフサイクル イベントでエンティティを検証する必要はありません。多くのオプションの 1 つにすぎません。もちろん、コードで使用されるエンティティの値セッターまたはその他のメソッドで検証を実行することもできます。

Entities can register lifecycle event methods with Doctrine that are called on different occasions. For validation we would need to hook into the events called before persisting and updating. Even though we don’t support validation out of the box, the implementation is even simpler than in Doctrine 1 and you will get the additional benefit of being able to re-use your validation in any other part of your domain.

エンティティは、さまざまな機会に呼び出されるライフサイクル イベント メソッドを Doctrine に登録できます。検証のために、永続化と更新の前に呼び出されたイベントにフックする必要があります。すぐに使用できる検証はサポートしていませんが、実装は Doctrine 1 よりもさらに簡単であり、ドメインの他の部分で検証を再利用できるという追加の利点が得られます。

Say we have an Order with several OrderLine instances. We never want to allow any customer to order for a larger sum than they are allowed to:

複数の OrderLine インスタンスを持つ Order があるとします。顧客が許可されているよりも多くの金額を注文できるようにしたいとは考えていません。

<?php
class Order
{
    public function assertCustomerAllowedBuying()
    {
        $orderLimit = $this->customer->getOrderLimit();

        $amount = 0;
        foreach ($this->orderLines as $line) {
            $amount += $line->getAmount();
        }

        if ($amount > $orderLimit) {
            throw new CustomerOrderLimitExceededException();
        }
    }
}

Now this is some pretty important piece of business logic in your code, enforcing it at any time is important so that customers with a unknown reputation don’t owe your business too much money.

これは、コード内のビジネス ロジックのかなり重要な部分です。評判が不明な顧客がビジネスに多額の借金を負わないように、いつでも適用することが重要です。

We can enforce this constraint in any of the metadata drivers. First Attributes:

この制約は、任意のメタデータ ドライバーで適用できます。最初の属性:

<?php

#[Entity]
#[HasLifecycleCallbacks]
class Order
{
    #[PrePersist, PreUpdate]
    public function assertCustomerAllowedBuying() {}
}

As Annotations:

注釈として:

<?php
/**
 * @Entity
 * @HasLifecycleCallbacks
 */
class Order
{
    /**
     * @PrePersist @PreUpdate
     */
    public function assertCustomerAllowedBuying() {}
}

In XML Mappings:

XML マッピング:

<doctrine-mapping>
    <entity name="Order">
        <lifecycle-callbacks>
            <lifecycle-callback type="prePersist" method="assertCustomerallowedBuying" />
            <lifecycle-callback type="preUpdate" method="assertCustomerallowedBuying" />
        </lifecycle-callbacks>
    </entity>
</doctrine-mapping>

Now validation is performed whenever you call EntityManager#persist($order) or when you call EntityManager#flush() and an order is about to be updated. Any Exception that happens in the lifecycle callbacks will be caught by the EntityManager and the current transaction is rolled back.

EntityManager#persist($order) を呼び出すたびに、または EntityManager#flush() を呼び出して注文が更新されようとしているときに、検証が実行されるようになりました。ライフサイクル コールバックで発生する例外は、EntityManager によって捕捉され、現在のトランザクションはロールバックされます。

Of course you can do any type of primitive checks, not null, email-validation, string size, integer and date ranges in your validation callbacks.

もちろん、検証コールバックでは、null、電子メール検証、文字列サイズ、整数、および日付範囲ではなく、任意のタイプのプリミティブ チェックを実行できます。

<?php
class Order
{
    #[PrePersist, PreUpdate]
    public function validate()
    {
        if (!($this->plannedShipDate instanceof DateTime)) {
            throw new ValidateException();
        }

        if ($this->plannedShipDate->format('U') < time()) {
            throw new ValidateException();
        }

        if ($this->customer == null) {
            throw new OrderRequiresCustomerException();
        }
    }
}

What is nice about lifecycle events is, you can also re-use the methods at other places in your domain, for example in combination with your form library. Additionally there is no limitation in the number of methods you register on one particular event, i.e. you can register multiple methods for validation in “PrePersist” or “PreUpdate” or mix and share them in any combinations between those two events.

ライフサイクル イベントの優れている点は、たとえばフォーム ライブラリと組み合わせて、ドメイン内の他の場所でメソッドを再利用できることです。さらに、1 つの特定のイベントに登録するメソッドの数に制限はありません。つまり、「PrePersist」または「PreUpdate」で検証用に複数のメソッドを登録したり、これら 2 つのイベント間で任意の組み合わせでそれらを組み合わせて共有したりできます。

There is no limit to what you can and can’t validate in “PrePersist” and “PreUpdate” as long as you don’t create new entity instances. This was already discussed in the previous blog post on the Versionable extension, which requires another type of event called “onFlush”.

新しいエンティティ インスタンスを作成しない限り、「PrePersist」と「PreUpdate」で検証できるものとできないものに制限はありません。これは、「onFlush」と呼ばれる別のタイプのイベントを必要とするバージョン管理可能な拡張機能に関する以前のブログ記事で既に説明されています。

Further readings: Events Overview

詳細情報: イベントの概要

Previous topic

Strategy-Pattern

戦略パターン

Next topic

Working with DateTime Instances

DateTime インスタンスの操作

This Page

Fork me on GitHub