scheduled-tasksshopwareshopware6

Check products by categoryId using scheduled task


I want to use Scheduled Task that checks if a specific category has products or not. I have used product.repository to get products by categoryId, but this method does not work. For example if the category "Clothes" shows 2 products in storefront, I get null when using product.repository.

I would like to use product_listing, but I cannot use SalesChannelContext in Scheduled Task. Do you have Idea, how can I get total products displayed in storefront for a specific category?

#[AsMessageHandler(handles: CategoriesTask::class)]
class CategoriesTaskHandler extends ScheduledTaskHandler
{
    private EntityRepository $productRepository;

    private EntityRepository $categoryRepository;

    public function __construct(
        EntityRepository $productRepository,
        EntityRepository $categoryRepository,
    {
        $this->productRepository = $productRepository;
        $this->categoryRepository = $categoryRepository;
    }

    public function run(): void
    {
        $data = [];
        $productsTotal = 0;

        $context = Context::createDefaultContext();

        $categories = $this->categoryRepository->search(new Criteria(), $context);
        foreach ($categories as $category) {
            $criteria = new Criteria();
            $criteria->addAssociation('categories');
            $criteria->addFilter(new EqualsFilter('categories.id', $category->getId()));

            $products = $this->productRepository->search($criteria, $context);

            $data[] = [
                'categoryName' => $category->getName(),
                'products' => $products->getTotal(),
            ];
        }

        dump($data);
    }
}

Solution

  • If you access a normal repository you don't need a salesChannel context, for example:

    $defaultContext = Context::createDefaultContext();
    $criteria = new Criteria();
    $criteria->addFilter(new EqualsFilter('product.categories.id', $categoryId)); // or something similar
    $this->productRepository->search($criteria, $defaultContext);
    

    But if need to access a saleschannel repository you can create a saleschannel context. You need the corresponding salesChannel Id, the 3rd parameter (with the language ID) is option but helps getting the correct data if necessary and afterwards reload the cartRuleLoader to apply all necessary rules (like dynamic access, etc.). And then you can load the saleschannel depended products.

    $token = Uuid::fromStringToHex($id);
    $salesChannelContext = $this->salesChannelContextFactory->create(
                $token,
                $salesChannelId,
                [
                    SalesChannelContextService::LANGUAGE_ID => $languageId
                ]
            );
    $this->cartRuleLoader->loadByToken($salesChannelContext, $token);
    $criteria = new Criteria();
    $criteria->addFilter(new EqualsFilter('product.categories.id', $categoryId)); // or something similar
    $salesChannelProduct = $this->salesChannelProductRepository->search(
                        $criteria, $salesChannelContext
                    );