Value Object in Symfony - An update - Blog | createIT

Get a free advice now!

    Pick the topic

    Developer OutsourcingWeb developingApp developingDigital MarketingeCommerce systemseEntertainment systems

    Thank you for your message. It has been sent.

    Tags

    Value Object in Symfony – an update

    Value Object in Symfony – an update

    A problem with null

    This entry is a follow-up to an earlier one about Value Object – “All about value object in Symfony”.

    In the previous article on the use of value object in Symfony I showed how to use VOs along with the Embeddable mechanism in Doctrine. However, this method has a one, clear problem. The mapping value as Embedd cannot assume the null value. This time, I will try to show two different ways that can be used when we need our VO to be able to assume the null value as an entity field.

    Setter/getter mapping

    The first way is to resign from specialized mapping mechanisms with Doctrine. Instead, we personally define the columns required to store our VO, and we perform the appropriate transformation using the right methods:

    /** 
     * @ORM\Entity(repositoryClass=Product1Repository::class) 
     */ 
     class Product1 
     { 
       /** 
        * @ORM\Id 
        * @ORM\GeneratedValue 
        * @ORM\Column(type="integer") 
        */ private $id; 
    
       /** 
        * @ORM\Column(type="string", length=255) 
        */ 
       private $name; 
    
       /** 
        * @ORM\Column(type="integer") 
        */ 
       private $priceValue; 
    
       /** 
        * @ORM\Column(type="string") 
        */ 
       private $priceCurrency; 
    
       public function __construct(string $name, Money $price) 
       { 
           $this->name = $name;
           $this->priceValue = $price->value();
           $this->priceCurrency = $price->currency(); 
       } 
    
       ... 
    
       public function setPrice(Money $price) 
       { 
           $this->priceValue = $price->value(); 
           $this->priceCurrency = $price->currency(); 
       } 
    
       public function getPrice() 
       { 
           return new Money($this->priceValue, $this->priceCurrency); 
       } 
     }

    As you can see, $priceValue and $priceCurrency are defined, they will serve to store the value and currency from VO Money. The constructor and setter assume the Money object as an argument, and then they extract the necessary information from it and save it in the $priceValue and $priceCurrency fields. The getter constructs the Money object on the basis of the information from the database and returns it.

    Mapping using Custom Mapping Type

    The second method uses the Custom Mapping Type from Doctrine. It allows to define our own mapping types for Doctrtine. They work as standard column types so they can assume the “nullable” parameter. The downside is that we must map our object to a single column in the database.

    First, we define our mapping:

    namespace App\Type;
    
    use App\ValueObject\Money; 
    use Doctrine\DBAL\Platforms\AbstractPlatform; 
    use Doctrine\DBAL\Types\Type; 
    
    class MoneyType extends Type 
     { 
         const MONEY = 'money'; 
    
         public function getSQLDeclaration(array $column, AbstractPlatform $platform) 
         { 
             return 'varchar(100)'; 
         }
    
         public function convertToPHPValue($value, AbstractPlatform $platform) 
         { 
             [$value, $currency] = explode(' ', $value); 
             return new Money($value, $currency); 
         } 
    
         public function convertToDatabaseValue($value, AbstractPlatform $platform) 
         { 
            if ($value === null) { 
               return null; 
            } 
            return $value->value().' '.$value->currency(); 
         }
     
         public function getName() 
         { 
            return self::MONEY; 
         }
     }

    getSQLDeclaration indicates the type of column we will use to store our value in the database.

    convertToPHPValue transforms the value from the database into our VO. In this case, we store information as a string type , where the value and currency are separated with the space sign, so we divide it into a table using explode and transfer the appropriate elements to the Money constructor.

    convertToDatabaseValue is used to transform our VO to a format that we will be able to store in the database.

    The last function, getName, defines the name of our mapping, which we will be using in Doctrine annotations.

    Next, we register our mapping in Symfony:

    # doctrine.yaml 
    
    doctrine: 
        dbal: url:'%env(resolve:DATABASE_URL)%'
    
        # IMPORTANT: You MUST configure your server version, 
        # either here or in the DATABASE_URL env var (see .env file)
        #server_version: '13'
    
        types: 
          money: App\Type\MoneyType 
    
        mapping_types: 
          money: string

    We can now use our mapping to determine the type of column in the entity:

    /** 
     * @ORM\Entity(repositoryClass=ProductCustomMappingRepository::class) 
     */ 
    class ProductCustomMapping 
      { 
        /**
         * @ORM\Id 
         * @ORM\GeneratedValue 
         * @ORM\Column(type="integer") 
         */ 
         private $id; 
    
        /** 
         * @ORM\Column(type="string", length=255) 
         */ private $name; 
    
        /** 
         * @ORM\Column(type="money", nullable=true) 
         */ 
        private $price; 
    
        public function __construct(string $name, ?Money $price) 
        { 
            $this->name = $name; 
            $this->price = $price; 
        } 
    
        ... 
    
        /** 
         * @return null|Money 
         */ 
         public function getPrice(): ?Money 
        { 
             return $this->price; 
        } 
    
        /** 
         * @param ?Money $price 
         */ 
        public function setPrice(?Money $price): void 
        { 
            $this->price = $price; 
        } 
      }

    https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/cookbook/custom-mapping-types.html

    https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/cookbook/advanced-field-value-conversion-using-custom-mapping-types.html

    About the author:

    Adam Zając is a Backend Developer with many years of experience, but he is also skilled in Frontend. Among his many interests, there are architecture, clean code and broadly understood quality in programming.

    References:

    Polish version: http://adamzajac.info/2021/09/30/value-object-w-symfony-update/

    Comments
    0 response

    Add comment

    Your email address will not be published. Required fields are marked *

    Popular news

    How to Get and Use the ChatGPT API
    • Dev Tips and Tricks

    How to Get and Use the ChatGPT API

    April 25, 2024 by createIT
    eCommerce growth – is your business ready?
    • Services
    • Trends

    eCommerce growth – is your business ready?

    April 8, 2024 by createIT
    Digital marketing without third-party cookies – new rules
    • Technology
    • Trends

    Digital marketing without third-party cookies – new rules

    February 21, 2024 by createIT
    eCommerce healthcheck
    • Services
    • Trends

    eCommerce healthcheck

    January 24, 2024 by createIT
    Live Visitor Count in WooCommerce with SSE
    • Dev Tips and Tricks

    Live Visitor Count in WooCommerce with SSE

    December 12, 2023 by createIT
    Calculate shipping costs programmatically in WooCommerce
    • Dev Tips and Tricks

    Calculate shipping costs programmatically in WooCommerce

    December 11, 2023 by createIT
    Designing a cookie consent modal certified by TCF IAB
    • Dev Tips and Tricks

    Designing a cookie consent modal certified by TCF IAB

    December 7, 2023 by createIT

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us