简体   繁体   中英

PHP Extend class which can only be constructed through static method which calls new static()

I have this class Product with a few properties like Name, Description etc. An object of class Product can only be created through a static method fromArray()

$product = Product::fromArray($arr);


class Product 
{
    // getters and setters
    ...

    public static function fromArray($productArr) {
        $productObj = new static();

        if (isset($productArr['ProductId'])) {
            $productObj->setProductId($productArr['ProductId'];
        } else {
            $productObj->setProductId(null)
        }
        if (isset($productArr['Name'])) {
            $productObj->setName($productArr['Name'];
        } else {
            $productObj->setName(null)
        }
        ...

        return $productObj;
    }
}

I now have a class ProductVariant, (containing extra information like size and/or colour for a product) which should extend this class. If I simply do parent::fromArray($productVariantArr) in this class' fromArray() method I will end up with an object of type Product and not of type ProductVariant, which is obviously not what I want.

So I found a way to work around this, but I am not convinced at all it is the right way to do it. This is what I did:

I changed the fromArray() method of Product to the following, making it possible to pass in an object of the extending class

public static function fromArray($productArr, $object = null) {
    if ($object !== null) {
        $productObj = $object;
    } else {
        $productObj = new static();  
    }
    ....
}

And the ProductVariant class

class ProductVariant extends Product 
{
    // getters and setters 
    ...

    public static function fromArray($productVariantArr, $object = null) {
        $productVariantObj = new static();
        $productVariantObj = parent::fromArray($productVariantArr, $productVariantObj);

        if (isset($productVariantArr['Size'])) {
            $productVariantObj->setSize($productVariantArr['Size']);
        } else {
            $productVariantObj->setSize(null);
        }
        ....

        return $productVariantObj;
    }
}

But as I said, this doesn't seem right. Any help on how to extend the Product class is very welcome

How about making the fromArray method pretty simple and generalized, so that it could be used in all the child classes easily in an abstract way.

 class Product
 {

     ...

     public static function fromArray(array $attrs)
     {
         $instance = new static();

         foreach($attrs as $key => $value) {
             $setter = 'set'.$key;

             // Check if the setter method exists
             if (method_exists($instance, $setter)) {
                 // Invoke that method dynamically
                 call_user_func(array($instance, $setter), $value);
             } else {
                 // You might want to throw an exception here
             }
         }

         return $instance;
     }
 }

 // Child class
 class ProductVariant extends Product
 {
     ...     
 }

Now, just do this:

 // Now create instances
 $product = Product::fromArray([
     'Name' => 'something',
     'ProductId' => 234
 ]);

 $productVariant = ProductVariant::fromArray([
     'Size' => '15px',
     'Zier' => null
 ]);

 print_r($product);
 print_r($productVariant);

Hope, this solves your problem. Not sure, what exactly you are looking for.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM