Value Object en php

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

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 :

php
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 :

php
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 :

php
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 :

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

Vous pouvez écrire :

php
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.

PHP

Damien Flandrin Développeur web depuis 2010, créateur de Gekkode et d’Email Impact. Chaque article est testé sur un projet réel avant publication. Contact
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.