Skip to content

Commit

Permalink
Introduce new feed generation system
Browse files Browse the repository at this point in the history
  • Loading branch information
lruozzi9 committed Oct 3, 2024
1 parent 5f39542 commit 92a9a86
Show file tree
Hide file tree
Showing 27 changed files with 945 additions and 8 deletions.
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
],
"license": "MIT",
"require": {
"php": "^8.1",
"php": "^8.2",
"ext-json": "*",
"sylius/sylius": "^1.12",
"symfony/serializer": "^5.0 || ^6.0",
"symfony/lock": "^5.4 || ^6.0",
"symfony/serializer": "^5.0 || ^6.0",
"symfony/webpack-encore-bundle": "^1.15"
},
"require-dev": {
Expand Down Expand Up @@ -49,7 +49,8 @@
"symfony/flex": "^2.2.2",
"symfony/intl": "^5.4 || ^6.0",
"symfony/web-profiler-bundle": "^5.4 || ^6.0",
"vimeo/psalm": "^4.27"
"theofidry/alice-data-fixtures": "^1.7",
"vimeo/psalm": "^5.26"
},
"config": {
"sort-packages": true,
Expand Down
10 changes: 10 additions & 0 deletions config/services/command.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Webgriffe\SyliusClerkPlugin\Command\V2FeedGeneratorCommand;
use Webgriffe\SyliusClerkPlugin\Command\FeedGeneratorCommand;
use Webgriffe\SyliusClerkPlugin\Service\FeedGenerator;

Expand All @@ -19,4 +20,13 @@
])
->tag('console.command')
;

$services->set('webgriffe_sylius_clerk_plugin.command.feed_generator', V2FeedGeneratorCommand::class)
->args([
'$channelRepository' => service('sylius.repository.channel'),
'$productsFeedGenerator' => service('webgriffe_sylius_clerk_plugin.feed_generator.products'),
'$filesystem' => service('filesystem'),
])
->tag('console.command')
;
};
42 changes: 42 additions & 0 deletions config/services/generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Enum\Resource;
use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Generator\ResourceFeedGenerator;
use Webgriffe\SyliusClerkPlugin\QueryBuilder\CustomersQueryBuilderFactory;
use Webgriffe\SyliusClerkPlugin\QueryBuilder\ProductsQueryBuilderFactory;
use Webgriffe\SyliusClerkPlugin\QueryBuilder\TaxonsQueryBuilderFactory;
Expand All @@ -24,4 +26,44 @@
service('serializer'),
])
;

$services->set('webgriffe_sylius_clerk_plugin.feed_generator.products', ResourceFeedGenerator::class)
->args([
service('webgriffe_sylius_clerk_plugin.provider.products'),
service('serializer'),
Resource::PRODUCTS,
])
;

$services->set('webgriffe_sylius_clerk_plugin.feed_generator.categories', ResourceFeedGenerator::class)
->args([
service('webgriffe_sylius_clerk_plugin.provider.categories'),
service('serializer'),
Resource::CATEGORIES,
])
;

$services->set('webgriffe_sylius_clerk_plugin.feed_generator.orders', ResourceFeedGenerator::class)
->args([
service('webgriffe_sylius_clerk_plugin.provider.orders'),
service('serializer'),
Resource::ORDERS,
])
;

$services->set('webgriffe_sylius_clerk_plugin.feed_generator.customers', ResourceFeedGenerator::class)
->args([
service('webgriffe_sylius_clerk_plugin.provider.customers'),
service('serializer'),
Resource::CUSTOMERS,
])
;

$services->set('webgriffe_sylius_clerk_plugin.feed_generator.pages', ResourceFeedGenerator::class)
->args([
service('webgriffe_sylius_clerk_plugin.provider.pages'),
service('serializer'),
Resource::PAGES,
])
;
};
31 changes: 31 additions & 0 deletions config/services/provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Provider\QueryBuilderResourceProvider;
use Webgriffe\SyliusClerkPlugin\Service\PrivateApiKeyProvider;
use Webgriffe\SyliusClerkPlugin\Service\PublicApiKeyProvider;

Expand All @@ -13,4 +14,34 @@
$services->set('webgriffe_sylius_clerk.provider.private_api_key', PrivateApiKeyProvider::class);

$services->set('webgriffe_sylius_clerk.provider.public_api_key', PublicApiKeyProvider::class);

$services->set('webgriffe_sylius_clerk_plugin.provider.products', QueryBuilderResourceProvider::class)
->args([
service('webgriffe_sylius_clerk_plugin.query_builder.products'),
])
;

$services->set('webgriffe_sylius_clerk_plugin.provider.categories', QueryBuilderResourceProvider::class)
->args([
service('webgriffe_sylius_clerk_plugin.query_builder.categories'),
])
;

$services->set('webgriffe_sylius_clerk_plugin.provider.orders', QueryBuilderResourceProvider::class)
->args([
service('webgriffe_sylius_clerk_plugin.query_builder.orders'),
])
;

$services->set('webgriffe_sylius_clerk_plugin.provider.customers', QueryBuilderResourceProvider::class)
->args([
service('webgriffe_sylius_clerk_plugin.query_builder.customers'),
])
;

$services->set('webgriffe_sylius_clerk_plugin.provider.pages', QueryBuilderResourceProvider::class)
->args([
service('webgriffe_sylius_clerk_plugin.query_builder.pages'),
])
;
};
8 changes: 8 additions & 0 deletions config/services/query_builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Doctrine\ORM\ProductsQueryBuilder;
use Webgriffe\SyliusClerkPlugin\QueryBuilder\CustomersQueryBuilderFactory;
use Webgriffe\SyliusClerkPlugin\QueryBuilder\OrdersQueryBuilderFactory;
use Webgriffe\SyliusClerkPlugin\QueryBuilder\ProductsQueryBuilderFactory;
Expand Down Expand Up @@ -35,4 +36,11 @@
service('sylius.repository.customer'),
])
;

$services->set('webgriffe_sylius_clerk_plugin.query_builder.products', ProductsQueryBuilder::class)
->args([
service('sylius.repository.product'),
service('event_dispatcher'),
])
;
};
3 changes: 3 additions & 0 deletions src/Command/FeedGeneratorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
use Webgriffe\SyliusClerkPlugin\Service\FeedGeneratorInterface;
use Webmozart\Assert\Assert;

/**
* @deprecated
*/
final class FeedGeneratorCommand extends Command
{
use LockableTrait;
Expand Down
85 changes: 85 additions & 0 deletions src/Command/V2FeedGeneratorCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace Webgriffe\SyliusClerkPlugin\Command;

use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Generator\FeedGeneratorInterface;
use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\ValueObject\Feed;

/**
* @psalm-suppress PropertyNotSetInConstructor
*/
#[AsCommand(name: 'webgriffe:clerk:feed:generate', description: 'Generate feeds for Clerk.io data sync')]
class V2FeedGeneratorCommand extends Command
{
use LockableTrait;

private SymfonyStyle $io;

/**
* @param ChannelRepositoryInterface<ChannelInterface> $channelRepository
*/
public function __construct(
private readonly ChannelRepositoryInterface $channelRepository,
private readonly FeedGeneratorInterface $productsFeedGenerator,
private readonly Filesystem $filesystem,
private readonly string $feedsStorageDirectory,
) {
parent::__construct();
}

protected function configure(): void
{
}

protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io->writeln('Starting Clerk.io feed generation...');
if (!$this->lock()) {
$this->io->error('The command is already running in another process, quitting.');

return Command::FAILURE;
}

$this->filesystem->mkdir($this->feedsStorageDirectory);
/** @var ChannelInterface[] $channels */
$channels = $this->channelRepository->findAll();
foreach ($channels as $channel) {
foreach ($channel->getLocales() as $locale) {
$productsFeed = $this->productsFeedGenerator->generate($channel, (string) $locale->getCode());
$feedFilePath = $this->getFeedFilePath($productsFeed);

$this->io->writeln(sprintf('Writing feed to file: %s', $feedFilePath));
$this->filesystem->dumpFile($feedFilePath, $productsFeed->getContent());
}
}

$this->io->success('Clerk.io feed generation completed successfully.');

return Command::SUCCESS;
}

private function getFeedFilePath(Feed $productsFeed): string
{
return sprintf(
'%s/%s',
rtrim($this->feedsStorageDirectory, '/'),
ltrim($productsFeed->getFileName(), '/'),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Doctrine\ORM\Event;

use Doctrine\ORM\QueryBuilder;
use Sylius\Component\Core\Model\ChannelInterface;

final readonly class QueryBuilderEvent
{
public function __construct(
private QueryBuilder $queryBuilder,
private ChannelInterface $channel,
private string $localeCode,
private ?\DateTimeInterface $modifiedAfter = null,
private ?int $limit = null,
private ?int $offset = null,
) {
}

public function getQueryBuilder(): QueryBuilder
{
return $this->queryBuilder;
}

public function getChannel(): ChannelInterface
{
return $this->channel;
}

public function getLocaleCode(): string
{
return $this->localeCode;
}

public function getModifiedAfter(): ?\DateTimeInterface
{
return $this->modifiedAfter;
}

public function getLimit(): ?int
{
return $this->limit;
}

public function getOffset(): ?int
{
return $this->offset;
}
}
78 changes: 78 additions & 0 deletions src/DataSyncInfrastructure/Doctrine/ORM/ProductsQueryBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Doctrine\ORM;

use Psr\EventDispatcher\EventDispatcherInterface;
use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductRepository;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Doctrine\ORM\Event\QueryBuilderEvent;
use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Enum\Resource;
use Webgriffe\SyliusClerkPlugin\DataSyncInfrastructure\Model\QueryBuilderInterface;

/**
* @implements QueryBuilderInterface<ProductInterface>
*/
final readonly class ProductsQueryBuilder implements QueryBuilderInterface
{
public function __construct(
private ProductRepository $productRepository,
private EventDispatcherInterface $eventDispatcher,
) {
}

public function getResource(): Resource
{
return Resource::PRODUCTS;
}

public function getResult(
ChannelInterface $channel,
string $localeCode,
?\DateTimeInterface $modifiedAfter = null,
?int $limit = null,
?int $offset = null,
): array {
$queryBuilder = $this->productRepository->createQueryBuilder('p');

$queryBuilder
->andWhere(':channel MEMBER OF p.channels')
->setParameter('channel', $channel)
;
$queryBuilder
->leftJoin('p.translations', 't', 'WITH', 't.locale = :localeCode')
->setParameter('localeCode', $localeCode)
;

if ($modifiedAfter !== null) {
$queryBuilder
->andWhere('p.updatedAt > :modifiedAfter')
->setParameter('modifiedAfter', $modifiedAfter)
;
}

if ($limit !== null) {
$queryBuilder->setMaxResults($limit);
}

if ($offset !== null) {
$queryBuilder->setFirstResult($offset);
}

$this->eventDispatcher->dispatch(new QueryBuilderEvent(
$queryBuilder,
$channel,
$localeCode,
$modifiedAfter,
$limit,
$offset,
));

/** @var ProductInterface[] $result */
$result = $queryBuilder->getQuery()->getResult();

return $result;
}
}
Loading

0 comments on commit 92a9a86

Please sign in to comment.