1
\$\begingroup\$

Let's assume I have 2 objects Product and Volume. 1 Product must have at least 1 Volume which means: Product ----> 1...* Volume.

I want to keep the consistence of Product at its creation, in other words, I want to block the user to create products without any volumes.

I have a ready working code, but nonetheless I want to know your opinions guys.

Here is the running code: https://3v4l.org/t7JqO#v700

Here is my code:

<?php
declare(strict_types = 1);

namespace App
{
    class Product
    {
        private $id;
        private $name;
        private $volumes;

        /**
         * Product constructor.
         * @param int $id
         * @param string $name
         * @param array $volumes
         * @throws \InvalidArgumentException
         */
        public function __construct(int $id, string $name, array $volumes)
        {
            $this->id = $id;
            $this->name = $name;

            if (count($volumes) === 0) {
                throw new \InvalidArgumentException('Your product must have 1 product at least.');
            }

            $this->volumes = $this->prepareVolumes($volumes);
        }

        private function prepareVolumes(array $volumes)
        {
            foreach ($volumes as $volume) {
                $volume->setProduct($this);
            }
            return $volumes;
        }
    }

    class Volume
    {
        protected $id;
        protected $product;
        protected $description;

        public function __construct(string $description)
        {
            $this->description = $description;
        }

        public function setProduct(Product $product)
        {
            $this->product = $product;
        }
    }

    class ProductTest
    {
        public function __construct()
        {
            $volume1 = new Volume('part-1');
            $volume2 = new Volume('part-2');
            $volume3 = new Volume('part-3');

            $product = new Product(1, 'BED', [$volume1, $volume2, $volume3]);
            $invalidProduct = new Product(2, 'Table', []);
        }
    }

    return new ProductTest();
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

At first:

Note: Strict typing is only defined for scalar type declarations, and as such, requires PHP 7.0.0 or later, as scalar type declarations were added in that version.

Secondly:
You are passing in an array of volumes $volumes to the __construct function when instantiating an object of class Product. But there is no confidence that each element of $volumes is an object of Volume class. Someone could put into $volumes array an arbitrary value.
I would recommend applying dependency with Volume class and pass in Product constructor only one object of Volume class (following the initial rule: "Product must have at least 1 Volume"). And then, if necessary, you will be able to add some additional 'volumes' to product object using an auxiliary method addVolume().
See the optimized version:

<?php
declare(strict_types = 1);

namespace App
{
    class Product
    {
        private $id;
        private $name;
        private $volumes = [];

        /**
         * Product constructor.
         * @param int $id
         * @param string $name
         * @param array $volumes
         */
        public function __construct(int $id, string $name, Volume $volume)
        {
            $this->id = $id;
            $this->name = $name;
            $volume->setProduct($this);    
            $this->volumes[] = $volume;
        }

        public function addVolume(Volume $volume)
        {  
            $volume->setProduct($this); 
            $this->volumes[] = $volume;
        }
    }

    class Volume
    {
        protected $id;
        protected $product;
        protected $description;

        public function __construct(string $description)
        {
            $this->description = $description;
        }

        public function setProduct(Product $product)
        {
            $this->product = $product;
        }
    }

    class ProductTest
    {
        public function __construct()
        {
            $volume1 = new Volume('part-1');
            $volume2 = new Volume('part-2');
            $volume3 = new Volume('part-3');

            $product = new Product(1, 'BED', $volume1);
            $product->addVolume($volume2);
            $product->addVolume($volume3);

            $invalidProduct = new Product(2, 'Table', []);
        }
    }

    return new ProductTest();
}
\$\endgroup\$
1
  • \$\begingroup\$ Great solution @romanperekhrest. I had already got the point about confidence tip and in my production code I'm using an collection instead of array. Thank you. I owe you a beer. \$\endgroup\$ Commented Jan 29, 2016 at 11:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.