2
\$\begingroup\$

I am trying to make a small e-commerce backend in php. I centralized all the static factory methods in a class called EntityFactory. The main reason for that was to centralize the dependency on dependency injector and to make it easy for the client to initialize an entity.

class EntityFactory
{
    private static $employeeRepository;
    private static $productRepository;

    public static function employee($name, $designation, $salary, $contactNo)
    {
        if (!self::$employeeRepository)
            self::$employeeRepository = resolve(EmployeeRepository::class);

        return new Employee(self::$employeeRepository, $name, $designation, $salary, $contactNo);
    }

    public static function product($itemCode, $name, Brand $brand, Category $category, $costPrice, $salePrice)
    {
        if (!self::$productRepository)
            self::$productRepository = resolve(ProductRepository::class);

        return new Product($itemCode, $name, $brand, $category, $costPrice, $salePrice, self::$productRepository);
    }

    public static function brand($name, $id = null)
    {
        $brand = new Brand($name);
        if ($id) $brand->setId($id);
        return $brand;
    }

    public static function category($name, $id = null)
    {
        $category = new Category($name);
        if ($id) $category->setId($id);
        return $category;
    }

    public static function purchaseOrder($customer, $user)
    {
        return new PurchaseOrder($customer, $user);
    }

    public static function orderItem($item, $qty)
    {
        return new PurchaseOrderItem($item, $qty);
    }
}

The main problem I feel here is that this breaks SOLID, especially SRP, ISP and OCP. I may also face problems regarding unit testing in the future(though I've not yet faced one). The code for product purchasing:

//controller: create a new purchase order
public function store(Request $request)
    {
        $this->validateRequest($request);

    $user = $this->productRepository->findById(Auth::id());
    $customer = $this->supplierRepository->findById($request->input('id'));
    $products = $request->input('products');
    $purchaseOrder = EntityFactory::purchaseOrder($customer, $user);

    foreach ($products as $product) {
        $item = $this->productRepository->findById($product['id']);
        $purchaseOrder->addItem(EntityFactory::orderItem($item, $product['qty']));
    }

    $this->purchaseOrderRepository->beginTransaction();
    $this->purchaseOrderRepository->save($purchaseOrder);
    $purchaseOrder->finalize();
    $this->purchaseOrderRepository->endTransaction();
}

Here I can easily stub out the repositories in the controller

// class PurchaseOrder

public function addItem(PurchaseOrderItem $item)
{
    $this->items[] = $item;
}

public function finalize()
{
    foreach ($this->items as $item) {
        $product = $item->getProduct();
        $product->purchase($item->getQuantity());
    }
}


//class Product
public function purchase($qty)
{
    if ($this->stock - $qty < 0) {
        throw new \RuntimeException("Product not available");
    } else {
        $this->stock += $qty;
        $this->productRepository->update($this);
    }
}

Here also the ProductOrderItem and Product can be stubbed.

I didn't want the factory to need instantiation. Otherwise I would have a dependency graph like factory -> repository -> mapper -> factory where mapper maps data from ORM classes to entity classes.

What are your thoughts?

\$\endgroup\$
  • \$\begingroup\$ I think the question is fairly language agnostic as it's mainly about design. However, I'll add the tag if it helps. \$\endgroup\$ – sayeed Mar 27 '18 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.