Skip to content

Commit

Permalink
Added data provider
Browse files Browse the repository at this point in the history
  • Loading branch information
loevgaard committed Dec 11, 2024
1 parent beb77bd commit 6ca5c4f
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 7 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
"doctrine/dbal": "^3.0",
"doctrine/orm": "^2.10",
"knplabs/knp-menu": "^3.0",
"ocramius/doctrine-batch-utils": "^2.6",
"psr/clock": "^1.0",
"psr/clock-implementation": "*",
"setono/composite-compiler-pass": "^1.2",
"setono/doctrine-orm-trait": "^1.1",
"sylius/channel": "^1.0",
"sylius/channel-bundle": "^1.0",
"sylius/core": "^1.0",
Expand Down
44 changes: 44 additions & 0 deletions src/DataProvider/ProductDataProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusCatalogPromotionPlugin\DataProvider;

use Doctrine\Persistence\ManagerRegistry;
use DoctrineBatchUtils\BatchProcessing\SimpleBatchIteratorAggregate;
use Psr\EventDispatcher\EventDispatcherInterface;
use Setono\Doctrine\ORMTrait;
use Setono\SyliusCatalogPromotionPlugin\Event\DataProviderQueryBuilderCreatedEvent;
use Setono\SyliusCatalogPromotionPlugin\Model\ProductInterface;

final class ProductDataProvider implements ProductDataProviderInterface
{
use ORMTrait;

public function __construct(
ManagerRegistry $managerRegistry,
private readonly EventDispatcherInterface $eventDispatcher,

/** @var class-string<ProductInterface> $productClass */
private readonly string $productClass,
) {
$this->managerRegistry = $managerRegistry;
}

public function getProducts(): \Generator
{
$qb = $this
->getManager($this->productClass)
->createQueryBuilder()
->select('product')
->from($this->productClass, 'product')
;

$this->eventDispatcher->dispatch(new DataProviderQueryBuilderCreatedEvent($qb));

/** @var SimpleBatchIteratorAggregate<array-key, ProductInterface> $iterator */
$iterator = SimpleBatchIteratorAggregate::fromQuery($qb->getQuery(), 100);

yield from $iterator;
}
}
14 changes: 14 additions & 0 deletions src/Event/DataProviderQueryBuilderCreatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusCatalogPromotionPlugin\Event;

use Doctrine\ORM\QueryBuilder;

final class DataProviderQueryBuilderCreatedEvent
{
public function __construct(public readonly QueryBuilder $queryBuilder)
{
}
}
30 changes: 29 additions & 1 deletion src/Message/CommandHandler/ProcessCatalogPromotionsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,46 @@

namespace Setono\SyliusCatalogPromotionPlugin\Message\CommandHandler;

use Doctrine\Persistence\ManagerRegistry;
use Setono\Doctrine\ORMTrait;
use Setono\SyliusCatalogPromotionPlugin\Checker\PreQualification\PreQualificationCheckerInterface;
use Setono\SyliusCatalogPromotionPlugin\DataProvider\ProductDataProviderInterface;
use Setono\SyliusCatalogPromotionPlugin\Message\Command\ProcessCatalogPromotions;
use Setono\SyliusCatalogPromotionPlugin\Repository\PromotionRepositoryInterface;

// todo refactor this by dividing the processing into chunks of x products much like we do in the Meilisearch plugin
final class ProcessCatalogPromotionsHandler
{
use ORMTrait;

public function __construct(
private readonly ProductDataProviderInterface $productDataProvider,
private readonly PromotionRepositoryInterface $promotionRepository,
private readonly PreQualificationCheckerInterface $preQualificationChecker,
ManagerRegistry $managerRegistry,
) {
$this->managerRegistry = $managerRegistry;
}

public function __invoke(ProcessCatalogPromotions $message): void
{
$catalogPromotions = $this->promotionRepository->findForProcessing();
$catalogPromotions = $this->promotionRepository->findForProcessing($message->catalogPromotions);

$manager = null;

$i = 0;
foreach ($this->productDataProvider->getProducts() as $product) {
++$i;

// If we check all catalog promotions, we need to reset the pre-qualified catalog promotions on the product.
// Otherwise, we only need to reset the pre-qualified catalog promotions for the catalog promotions we are processing
$preQualifiedCatalogPromotions = [];
if ([] !== $message->catalogPromotions) {
$preQualifiedCatalogPromotions = array_filter(
$product->getPreQualifiedCatalogPromotions(),
static fn (string $code) => !in_array($code, $message->catalogPromotions, true),
);
}

foreach ($catalogPromotions as $catalogPromotion) {
if ($this->preQualificationChecker->isPreQualified($product, $catalogPromotion)) {
Expand All @@ -32,6 +52,14 @@ public function __invoke(ProcessCatalogPromotions $message): void
}

$product->setPreQualifiedCatalogPromotions($preQualifiedCatalogPromotions);

if ($i % 100 === 0) {
$manager = $this->getManager($product);
$manager->flush();
$manager->clear();
}
}

$manager?->flush();
}
}
2 changes: 1 addition & 1 deletion src/Model/ProductInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface ProductInterface extends BaseProductInterface
public function getPreQualifiedCatalogPromotions(): array;

/**
* @param list<string>|null $preQualifiedCatalogPromotions
* @param array<array-key, string>|null $preQualifiedCatalogPromotions
*/
public function setPreQualifiedCatalogPromotions(?array $preQualifiedCatalogPromotions): void;

Expand Down
4 changes: 2 additions & 2 deletions src/Model/ProductTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function getPreQualifiedCatalogPromotions(): array
}

/**
* @param list<string>|null $preQualifiedCatalogPromotions
* @param list<array-key, string>|null $preQualifiedCatalogPromotions
*/
public function setPreQualifiedCatalogPromotions(?array $preQualifiedCatalogPromotions): void
{
Expand All @@ -44,7 +44,7 @@ public function hasPreQualifiedCatalogPromotions(): bool
}

/**
* @param list<string> $codes
* @param array<array-key, string> $codes
*
* @return list<string>
*/
Expand Down
13 changes: 11 additions & 2 deletions src/Repository/PromotionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@

class PromotionRepository extends EntityRepository implements PromotionRepositoryInterface
{
public function findForProcessing(): array
public function findForProcessing(array $catalogPromotions = []): array
{
$objs = $this->findAll();
$qb = $this->createQueryBuilder('o');
if ([] !== $catalogPromotions) {
$qb->andWhere('o.code IN (:catalogPromotions)')
->setParameter('catalogPromotions', $catalogPromotions)
;
}

$objs = $qb->getQuery()->getResult();
Assert::isArray($objs);
Assert::allIsInstanceOf($objs, PromotionInterface::class);
Assert::isList($objs);

return $objs;
}
Expand Down
4 changes: 3 additions & 1 deletion src/Repository/PromotionRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
interface PromotionRepositoryInterface extends RepositoryInterface

Check failure on line 10 in src/Repository/PromotionRepositoryInterface.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.1 | Deps: lowest | SF~5.4.0)

MissingTemplateParam

src/Repository/PromotionRepositoryInterface.php:10:48: MissingTemplateParam: Setono\SyliusCatalogPromotionPlugin\Repository\PromotionRepositoryInterface has missing template params when extending Sylius\Component\Resource\Repository\RepositoryInterface, expecting 1 (see https://psalm.dev/182)

Check failure on line 10 in src/Repository/PromotionRepositoryInterface.php

View workflow job for this annotation

GitHub Actions / Static Code Analysis (PHP8.1 | Deps: lowest | SF~6.4.0)

MissingTemplateParam

src/Repository/PromotionRepositoryInterface.php:10:48: MissingTemplateParam: Setono\SyliusCatalogPromotionPlugin\Repository\PromotionRepositoryInterface has missing template params when extending Sylius\Component\Resource\Repository\RepositoryInterface, expecting 1 (see https://psalm.dev/182)
{
/**
* @param list<string> $catalogPromotions a list of catalog promotion codes to filter by
*
* @return list<PromotionInterface>
*/
public function findForProcessing(): array;
public function findForProcessing(array $catalogPromotions = []): array;

public function findOneByCode(string $code): ?PromotionInterface;
}
1 change: 1 addition & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<import resource="services/applicator.xml"/>
<import resource="services/calculator.xml"/>
<import resource="services/checker.xml"/>
<import resource="services/data_provider.xml"/>
<import resource="services/factory.xml"/>
<import resource="services/fixture.xml"/>
<import resource="services/form.xml"/>
Expand Down
16 changes: 16 additions & 0 deletions src/Resources/config/services/data_provider.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>

<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Setono\SyliusCatalogPromotionPlugin\DataProvider\ProductDataProviderInterface"
alias="Setono\SyliusCatalogPromotionPlugin\DataProvider\ProductDataProvider"/>

<service id="Setono\SyliusCatalogPromotionPlugin\DataProvider\ProductDataProvider">
<argument type="service" id="doctrine"/>
<argument type="service" id="event_dispatcher"/>
<argument>%sylius.model.product.class%</argument>
</service>
</services>
</container>
16 changes: 16 additions & 0 deletions src/Resources/config/services/message.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>

<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Setono\SyliusCatalogPromotionPlugin\Message\CommandHandler\ProcessCatalogPromotionsHandler">
<argument type="service" id="Setono\SyliusCatalogPromotionPlugin\DataProvider\ProductDataProviderInterface"/>
<argument type="service" id="setono_sylius_catalog_promotion.repository.promotion"/>
<argument type="service" id="Setono\SyliusCatalogPromotionPlugin\Checker\PreQualification\PreQualificationCheckerInterface"/>
<argument type="service" id="doctrine"/>

<tag name="messenger.message_handler"/>
</service>
</services>
</container>

0 comments on commit 6ca5c4f

Please sign in to comment.