Value Object en php

Un Value object en PHP est un type qui englobe des données et qui se distingue uniquement par ses propriétés. Contrairement à une Entité, il ne possède pas d’identifiant unique. Ainsi, deux Value objects en php ayant les mêmes valeurs de propriété doivent être considérés comme égaux.

Un bon exemple de candidats pour les value objects sont :

  • prix
  • numéro de téléphone
  • adresse
  • un hash d’engagement
  • un identifiant d’entité
  • et ainsi de suite.
 

Lors de la conception d’un value object en php, vous devez prêter attention à ses trois principales caractéristiques : immuabilité, égalité structurelle et auto-validation.

Exemple value object en php

final class Price
{
    const EU = 'EU';
    const CAD = 'CAD';

    /** @var float */
    private $amount;

    /** @var string */
    private $currency;

    public function __construct(float $amount, string $currency = 'EU')
    {
        if ($amount < 0) {
            throw new \InvalidArgumentException("Le montant doit être une valeur positive: {$amount}.");
        }

        if (!in_array($currency, $this->getAvailableCurrencies())) {
            throw new \InvalidArgumentException("La devise doit être valide: {$currency}.");
        }

        $this->amount = $amount;
        $this->currency = $currency;
    }

    private function getAvailableCurrencies(): array
    {
        return [self::EU, self::CAD];
    }

    public function getAmount(): float
    {
        return $this->amount;
    }

    public function getCurrency(): string
    {
        return $this->currency;
    }
}

Immutabilité

Une fois que vous avez instancié un value object en php, il doit rester le même pour le reste du cycle de vie de l’application. Si vous devez modifier sa valeur, vous devez remplacer entièrement cet objet.

L’utilisation d’un value object mutables est acceptable si vous les utilisez entièrement dans une portée locale, avec une seule référence à l’objet. Sinon, vous risquez de rencontrer des problèmes.

En reprenant l’exemple précédent, voici comment vous pouvez mettre à jour le montant d’un type de prix :

final class Price
{
    // ...

    private function hasSameCurrency(Price $price): bool
    {
        return $this->currency === $price->currency;
    }

    public function sum(Price $price): self
    {
        if (!$this->hasSameCurrency($price)) {
            throw \InvalidArgumentException(
                "Vous ne pouvez additionner que les valeurs ayant la même devise: {$this->currency} !== {$price->currency}."
            );
        }

        return new self($this->amount + $price->amount, $this->currency);
    }
}

Égalité structurelle

Les value objects en php n’ont pas d’identifiant. En d’autres termes, si deux value objects ont les mêmes valeurs internes, ils doivent être considérés comme égaux. Comme PHP n’a pas la possibilité de surcharger l’opérateur d’égalité, vous devez l’implémenter vous-même.

Vous pouvez créer une méthode spécialisée pour le faire :

final class Price
{
    // ...

    public function isEqualsTo(Price $price): bool
    {
        return $this->amount === $price->amount && 
        $this->currency === $price->currency;
    }
}

Auto-validation

La validation d’un value object doit avoir lieu lors de sa création. Si l’une de ses propriétés n’est pas valide, une exception doit être levée. Si l’on ajoute à cela l’immuabilité, une fois que vous avez créé un value object, vous pouvez être sûr qu’il sera toujours valide.

Si l’on reprend l’exemple du type Prix, il n’est pas logique d’avoir un montant négatif pour le domaine de l’application :

final class Price
{
    // ...

    public function __construct(float $amount, string $currency = 'USD')
    {
        if ($amount < 0) {
            throw new \InvalidArgumentException("Le montant doit être une valeur positive: {$amount}.");
        }

        if (!in_array($currency, $this->getAvailableCurrencies())) {
            throw new \InvalidArgumentException("La devise doit être valide: {$currency}.");
        }

        $this->amount = $amount;
        $this->currency = $currency;
    }
}

Conclusion

Les value objects sont utiles pour écrire du code propre. Au lieu d’écrire :

public function addPhoneNumber(string $phone) : void {}

Vous pouvez écrire :

public function addPhoneNumber(string $phone) : void {}

Cela facilite la lecture et le raisonnement, et vous n’avez pas besoin de déterminer quel format de téléphone vous devez utiliser. Puisque leurs attributs les définissent et que vous pouvez les partager avec d’autres entités différentes, ils peuvent être mis en cache pour toujours.

Ils peuvent vous aider à réduire les doublons. Au lieu d’avoir de multiples champs de montant et de devise, vous pouvez utiliser une classe de prix unique.

Bien sûr, comme tout dans la vie, il ne faut pas abuser des value objects en php. Imaginez que vous convertissiez des tonnes de value objects pour les stocker dans la base de données, ou que vous les reconvertissiez en value objects en php lorsque vous les récupérez dans la base de données : vous risquez d’avoir des problèmes de performances. De plus, le fait d’avoir des tonnes de value objects peut alourdir votre base de code.

Utilisez-les pour représenter un champ ou un groupe de champs de votre domaine qui nécessitent une validation ou pour éviter toute ambiguïté comme par exemples les formats de téléphone.

Nouveau Tutoriel

Newsletter

Ne manquez jamais les nouveaux conseils, tutoriels et autres.

Pas de spam, jamais. Nous ne partagerons jamais votre adresse électronique et vous pouvez vous désabonner à tout moment.