Shopware 6, abstract classes and dependency injection in Symfony
P粉680087550
P粉680087550 2024-01-09 08:44:57
0
1
507

Currently I am developing a Shopware 6 extension based on Symfony. What I don't understand is how to implement abstract classes and dependency injection.

So I want to be able to refactor the code and use these methods often, but in another context (using another repository)

<?php

declare(strict_types=1);

namespace WShopService;

use ShopwareCoreFrameworkContext;
use ShopwareCoreFrameworkDataAbstractionLayerSearchCriteria;
use ShopwareCoreFrameworkDataAbstractionLayerEntityRepository;
use ShopwareCoreFrameworkDataAbstractionLayerSearchFilterEqualsFilter;
use ShopwareCoreFrameworkUuidUuid;

/**
 * Service for writing Products
 */
class ProductService
{
    private EntityRepository  $productRepository;
    private MediaImageService $mediaImageService;
    private EntityRepository  $productMediaRepository;

    public function __construct(
        EntityRepository  $productRepository,
        MediaImageService $mediaImageService,
        EntityRepository  $productMediaRepository
    )
    {
        $this->productRepository = $productRepository;
        $this->mediaImageService = $mediaImageService;
        $this->productMediaRepository = $productMediaRepository;
    }

private function createProduct(array $data, Context $context = null): void
{
    $context = $context ?? Context::createDefaultContext();

    $this->productRepository->create([
                                         $data
                                     ], $context);
}

public function updateProduct(array $data): void
{
    $this->productRepository->update([$data], Context::createDefaultContext());
}

public function getExistingProductId(string $productNumber): ?string
{
    $criteria = new Criteria();
    $criteria->addFilter(new EqualsFilter('productNumber', $productNumber));

    return $this->productRepository->searchIds($criteria, 
     Context::createDefaultContext())->firstId();
 }
}

As you can see, there is dependency injection inside the construct (product repository). Now my question is, how can I create an abstract class, i.e. store these methods, but the child class will "override" the parent construct with the required repository? For example, I want to use the getDataId (now called getExistingProductId, but will be refactored and renamed in the abstract class) method on ProductRepository, but for the next class I want to Use the same method on Category Repository?

Service.xml also known as dependency injector

<service id="wshop_product_service" class="WShopServiceProductService">
            <argument type="service" id="product.repository"/>
            <argument id="wshop_media_image_service" type="service"/>
            <argument type="service" id="product_media.repository"/>
</service>

I am new to OOP. Please provide good examples and code explanations. Thanks!

P粉680087550
P粉680087550

reply all(1)
P粉248602298

If I understand correctly, you only want the first parameter to be interchangeable, and the 3 methods in your example should be implemented in the abstraction. Here's an idea.

Summary:

abstract class AbstractEntityService
{
    protected EntityRepository $repository;

    public function __construct(EntityRepository  $repository)
    {
        $this->repository = $repository;
    }

    public function create(array $data, ?Context $context = null): void
    {
        $context = $context ?? Context::createDefaultContext();

        $this->repository->create([
            $data
        ], $context);
    }

    public function update(array $data): void
    {
        $this->repository->update([$data], Context::createDefaultContext());
    }
    
    abstract public function getDataId(array $params): ?string;

    protected function searchId(Criteria $criteria): ?string
    {
        return $this->repository->searchIds(
            $criteria,
            Context::createDefaultContext()
        )->firstId();
    }
}

You get the repository in the constructor and implement all the common methods about the common repository in the abstraction. You want to implement the getDataId methods in your extended class because you are using specific conditions for each method (presumably). So you just force the implementation in the extended class by defining an abstract signature.

Your service level:

class ProductService extends AbstractEntityService
{
    private MediaImageService $mediaImageService;

    private EntityRepository $productMediaRepository;

    public function __construct(
        EntityRepository $productRepository,
        MediaImageService $mediaImageService,
        EntityRepository $productMediaRepository
    ) {
        parent::__construct($productRepository);
        $this->mediaImageService = $mediaImageService;
        $this->productMediaRepository = $productMediaRepository;
    }

    public function getDataId(array $params): ?string
    {
        if (!isset($params['productNumber'])) {
            return null;
        }

        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('productNumber', $params['productNumber']));

        return $this->searchId($criteria);
    }

    // your other methods using the injected services
}

In the extension class you only pass the repository to the parent constructor because other injected services are only used in this specific instance. You can implement getDataId where you create a specific condition and use that condition to call the protected (because it can only be used by extensions) searchId method.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template