mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
Refactor Template Events to use a standalone ComponentBlock with a bc manner
This commit is contained in:
parent
a1b7cd5c2d
commit
77af62ec7d
47 changed files with 1375 additions and 494 deletions
|
|
@ -233,7 +233,7 @@
|
|||
"symfony/runtime": "^6.3.2",
|
||||
"symfony/web-profiler-bundle": "^6.3.2",
|
||||
"symplify/monorepo-builder": "^11.0",
|
||||
"vimeo/psalm": "5.15.*",
|
||||
"vimeo/psalm": "5.14.*",
|
||||
"weirdan/doctrine-psalm-plugin": "^2.8"
|
||||
},
|
||||
"suggest": {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
<DeprecatedInterface>
|
||||
<errorLevel type="info">
|
||||
<referencedClass name="ApiPlatform\Core\Api\IriConverterInterface" /> <!-- deprecated in ApiPlatform 2.7 -->
|
||||
<referencedClass name="Sylius\Bundle\ShopBundle\Calculator\OrderItemsSubtotalCalculatorInterface" />
|
||||
<referencedClass name="Sylius\Component\Core\Calculator\ProductVariantPriceCalculatorInterface" />
|
||||
<referencedClass name="Sylius\Component\Core\Provider\ProductVariantsPricesProviderInterface" />
|
||||
<referencedClass name="Sylius\Component\User\Security\UserPasswordEncoderInterface" />
|
||||
|
|
@ -135,7 +136,7 @@
|
|||
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<errorLevel type="info">
|
||||
<file name="src/Sylius/Bundle/UiBundle/DataCollector/TemplateBlockRenderingHistory.php" />
|
||||
<file name="src/Sylius/Bundle/UiBundle/DataCollector/BlockRenderingHistory.php" />
|
||||
</errorLevel>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Command;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
|
@ -25,7 +25,7 @@ final class DebugTemplateEventCommand extends Command
|
|||
{
|
||||
protected static $defaultName = 'sylius:debug:template-event';
|
||||
|
||||
public function __construct(private TemplateBlockRegistryInterface $templateBlockRegistry)
|
||||
public function __construct(private BlockRegistryInterface $templateBlockRegistry)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
|
@ -53,6 +53,9 @@ final class DebugTemplateEventCommand extends Command
|
|||
|
||||
$io->title(sprintf('Blocks registered for the template event "%s"', $eventName));
|
||||
|
||||
/** @var array<TemplateBlock> $templateBlocksForEvent */
|
||||
$templateBlocksForEvent = $this->templateBlockRegistry->all()[$eventName] ?? [];
|
||||
|
||||
$io->table(
|
||||
['Block name', 'Template', 'Priority', 'Enabled'],
|
||||
array_map(
|
||||
|
|
@ -62,7 +65,7 @@ final class DebugTemplateEventCommand extends Command
|
|||
$templateBlock->getPriority(),
|
||||
$templateBlock->isEnabled() ? 'TRUE' : 'FALSE',
|
||||
],
|
||||
$this->templateBlockRegistry->all()[$eventName] ?? [],
|
||||
$templateBlocksForEvent,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\ContextProvider;
|
||||
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\Exception\NoSupportedContextProvider;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class CompositeContextProvider implements ContextProviderInterface
|
||||
{
|
||||
/** @var array<ContextProviderInterface> */
|
||||
private array $contextProviders;
|
||||
|
||||
/**
|
||||
* @param iterable<ContextProviderInterface> $contextProviders
|
||||
*/
|
||||
public function __construct(iterable $contextProviders)
|
||||
{
|
||||
Assert::allIsInstanceOf($contextProviders, ContextProviderInterface::class);
|
||||
$this->contextProviders = $contextProviders instanceof \Traversable ? iterator_to_array($contextProviders) : $contextProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $templateContext
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function provide(array $templateContext, Block $templateBlock): array
|
||||
{
|
||||
foreach ($this->contextProviders as $contextProvider) {
|
||||
if (!$contextProvider->supports($templateBlock)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $contextProvider->provide($templateContext, $templateBlock);
|
||||
}
|
||||
|
||||
throw new NoSupportedContextProvider(sprintf('No supported context provider found for block "%s".', $templateBlock->getName()));
|
||||
}
|
||||
|
||||
public function supports(Block $templateBlock): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\ContextProvider;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
|
||||
interface ContextProviderInterface
|
||||
{
|
||||
public function provide(array $templateContext, TemplateBlock $templateBlock): array;
|
||||
public function provide(array $templateContext, Block $templateBlock): array;
|
||||
|
||||
public function supports(TemplateBlock $templateBlock): bool;
|
||||
public function supports(Block $templateBlock): bool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\ContextProvider;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
|
||||
final class DefaultContextProvider implements ContextProviderInterface
|
||||
{
|
||||
public function provide(array $templateContext, TemplateBlock $templateBlock): array
|
||||
public function provide(array $templateContext, Block $templateBlock): array
|
||||
{
|
||||
return array_replace($templateBlock->getContext(), $templateContext);
|
||||
}
|
||||
|
||||
public function supports(TemplateBlock $templateBlock): bool
|
||||
public function supports(Block $templateBlock): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\ContextProvider\Exception;
|
||||
|
||||
class NoSupportedContextProvider extends \RuntimeException
|
||||
{
|
||||
}
|
||||
|
|
@ -13,31 +13,41 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\DataCollector;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
|
||||
/** @internal */
|
||||
final class TemplateBlockRenderingHistory
|
||||
final class BlockRenderingHistory
|
||||
{
|
||||
/** @psalm-var list<array{name: string, start: float, stop: float, time: float, blocks: list<array{definition: TemplateBlock, start: float, stop: float, time: float}>}> */
|
||||
/** @psalm-var list<array{name: string, start: float, stop: float, time: float, blocks: list<array{definition: Block, start: float, stop: float, time: float}>}> */
|
||||
private $renderedEvents = [];
|
||||
|
||||
/** @psalm-var list<array{name: string, start: float, stop?: float, time?: float, blocks: list<array{definition: TemplateBlock, start: float, stop: float, time: float}>}> */
|
||||
/** @psalm-var list<array{name: string, start: float, stop?: float, time?: float, blocks: list<array{definition: Block, start: float, stop: float, time: float}>}> */
|
||||
private array $currentlyRenderedEvents = [];
|
||||
|
||||
/** @psalm-var list<array{definition: TemplateBlock, start: float, stop?: float, time?: float}> */
|
||||
/** @psalm-var list<array{definition: Block, start: float, stop?: float, time?: float}> */
|
||||
private array $currentlyRenderedBlocks = [];
|
||||
|
||||
/**
|
||||
* @param array<string> $eventNames
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function startRenderingEvent(array $eventNames, array $context): void
|
||||
{
|
||||
$this->currentlyRenderedEvents[] = ['names' => $eventNames, 'start' => microtime(true), 'blocks' => []];
|
||||
}
|
||||
|
||||
public function startRenderingBlock(TemplateBlock $templateBlock, array $context): void
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function startRenderingBlock(Block $templateBlock, array $context): void
|
||||
{
|
||||
$this->currentlyRenderedBlocks[] = ['definition' => $templateBlock, 'start' => microtime(true)];
|
||||
}
|
||||
|
||||
public function stopRenderingBlock(TemplateBlock $templateBlock, array $context): void
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function stopRenderingBlock(Block $templateBlock, array $context): void
|
||||
{
|
||||
$currentlyRenderedBlock = array_pop($this->currentlyRenderedBlocks);
|
||||
$currentlyRenderedBlock['stop'] = microtime(true);
|
||||
|
|
@ -48,6 +58,10 @@ final class TemplateBlockRenderingHistory
|
|||
$this->currentlyRenderedEvents[] = $currentlyRenderedEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $eventNames
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function stopRenderingEvent(array $eventNames, array $context): void
|
||||
{
|
||||
$currentlyRenderedEvent = array_pop($this->currentlyRenderedEvents);
|
||||
|
|
@ -56,6 +70,9 @@ final class TemplateBlockRenderingHistory
|
|||
$this->renderedEvents[] = $currentlyRenderedEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array{name: string, start: float, stop: float, time: float, blocks: list<array{definition: Block, start: float, stop: float, time: float}>}>
|
||||
*/
|
||||
public function getRenderedEvents(): array
|
||||
{
|
||||
return $this->renderedEvents;
|
||||
|
|
@ -20,7 +20,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
|||
/** @internal */
|
||||
final class TemplateBlockDataCollector extends DataCollector
|
||||
{
|
||||
public function __construct(private TemplateBlockRenderingHistory $templateBlockRenderingHistory)
|
||||
public function __construct(private BlockRenderingHistory $templateBlockRenderingHistory)
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,19 +13,19 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\DataCollector;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface;
|
||||
|
||||
/** @internal */
|
||||
final class TraceableTemplateBlockRenderer implements TemplateBlockRendererInterface
|
||||
final class TraceableBlockRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TemplateBlockRendererInterface $templateBlockRenderer,
|
||||
private TemplateBlockRenderingHistory $templateBlockRenderingHistory,
|
||||
private BlockRendererInterface $templateBlockRenderer,
|
||||
private BlockRenderingHistory $templateBlockRenderingHistory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function render(TemplateBlock $templateBlock, array $context = []): string
|
||||
public function render(Block $templateBlock, array $context = []): string
|
||||
{
|
||||
$this->templateBlockRenderingHistory->startRenderingBlock($templateBlock, $context);
|
||||
|
||||
|
|
@ -13,14 +13,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\DataCollector;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface;
|
||||
|
||||
/** @internal */
|
||||
final class TraceableTemplateEventRenderer implements TemplateEventRendererInterface
|
||||
final class TraceableTwigEventRenderer implements TwigEventRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TemplateEventRendererInterface $templateEventRenderer,
|
||||
private TemplateBlockRenderingHistory $templateBlockRenderingHistory,
|
||||
private TwigEventRendererInterface $templateEventRenderer,
|
||||
private BlockRenderingHistory $templateBlockRenderingHistory,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -42,11 +42,32 @@ final class Configuration implements ConfigurationInterface
|
|||
->ifString()
|
||||
->then(static fn (?string $template): array => ['template' => $template])
|
||||
->end()
|
||||
->validate()
|
||||
->ifTrue(static function (array $block): bool {
|
||||
if (!array_key_exists('template', $block) || !array_key_exists('component', $block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null !== $block['template'] && [] !== $block['component'];
|
||||
})
|
||||
->thenInvalid('You cannot use both "template" and "component" for a block.')
|
||||
->end()
|
||||
->children()
|
||||
->booleanNode('enabled')->defaultNull()->end()
|
||||
->arrayNode('context')->addDefaultsIfNotSet()->ignoreExtraKeys(false)->end()
|
||||
->scalarNode('template')->defaultNull()->end()
|
||||
->scalarNode('component')->defaultNull()->end()
|
||||
->arrayNode('component')
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(static fn (?string $component): array => ['name' => $component])
|
||||
->end()
|
||||
->treatNullLike([])
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('name')->isRequired()->end()
|
||||
->arrayNode('inputs')->ignoreExtraKeys(false)->end()
|
||||
->end()
|
||||
->end()
|
||||
->integerNode('priority')->defaultNull()->end()
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\UiBundle\DependencyInjection;
|
||||
|
||||
use Laminas\Stdlib\SplPriorityQueue;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
|
@ -44,7 +45,7 @@ final class SyliusUiExtension extends Extension
|
|||
*/
|
||||
private function loadEvents(array $eventsConfig, ContainerBuilder $container): void
|
||||
{
|
||||
$templateBlockRegistryDefinition = $container->findDefinition(TemplateBlockRegistryInterface::class);
|
||||
$templateBlockRegistryDefinition = $container->findDefinition(BlockRegistryInterface::class);
|
||||
|
||||
$blocksForEvents = [];
|
||||
foreach ($eventsConfig as $eventName => $eventConfiguration) {
|
||||
|
|
@ -57,20 +58,53 @@ final class SyliusUiExtension extends Extension
|
|||
$blocksPriorityQueue->insert($details, $details['priority'] ?? 0);
|
||||
}
|
||||
|
||||
/** @psalm-var array{name: string, eventName: string, template: string, component: string|null, context: array, priority: int, enabled: bool} $details */
|
||||
/** @psalm-var array{name: string, eventName: string, template: string, component: array{name?: string, inputs?: array<string, mixed>}, context: array, priority: int, enabled: bool} $details */
|
||||
foreach ($blocksPriorityQueue->toArray() as $details) {
|
||||
$blocksForEvents[$eventName][$details['name']] = new Definition(TemplateBlock::class, [
|
||||
$details['name'],
|
||||
$details['eventName'],
|
||||
$details['template'],
|
||||
$details['context'],
|
||||
$details['priority'],
|
||||
$details['enabled'],
|
||||
$details['component'],
|
||||
]);
|
||||
$blockType = [] !== $details['component'] ? 'component' : 'template';
|
||||
$blocksForEvents[$eventName][$details['name']] = match ($blockType) {
|
||||
'template' => $this->createTemplateBlockDefinition($details),
|
||||
'component' => $this->createComponentBlockDefinition($details),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
$templateBlockRegistryDefinition->setArgument(0, $blocksForEvents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $details
|
||||
*/
|
||||
private function createTemplateBlockDefinition(array $details): Definition
|
||||
{
|
||||
return new Definition(
|
||||
TemplateBlock::class,
|
||||
[
|
||||
$details['name'],
|
||||
$details['eventName'],
|
||||
$details['template'],
|
||||
$details['context'],
|
||||
$details['priority'],
|
||||
$details['enabled'],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $details
|
||||
*/
|
||||
private function createComponentBlockDefinition(array $details): Definition
|
||||
{
|
||||
return new Definition(
|
||||
ComponentBlock::class,
|
||||
[
|
||||
$details['name'],
|
||||
$details['eventName'],
|
||||
$details['component']['name'],
|
||||
$details['component']['inputs'] ?? [],
|
||||
$details['context'],
|
||||
$details['priority'],
|
||||
$details['enabled'],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
src/Sylius/Bundle/UiBundle/Registry/Block.php
Normal file
59
src/Sylius/Bundle/UiBundle/Registry/Block.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
abstract class Block
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed>|null $context
|
||||
*/
|
||||
public function __construct(
|
||||
protected string $name,
|
||||
protected string $eventName,
|
||||
protected ?array $context,
|
||||
protected ?int $priority,
|
||||
protected ?bool $enabled,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getEventName(): string
|
||||
{
|
||||
return $this->eventName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getContext(): array
|
||||
{
|
||||
return $this->context ?? [];
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return $this->priority ?? 0;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled ?? true;
|
||||
}
|
||||
|
||||
abstract public function overwriteWith(self $block): self;
|
||||
}
|
||||
90
src/Sylius/Bundle/UiBundle/Registry/BlockRegistry.php
Normal file
90
src/Sylius/Bundle/UiBundle/Registry/BlockRegistry.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
use Laminas\Stdlib\SplPriorityQueue;
|
||||
|
||||
final class BlockRegistry implements BlockRegistryInterface
|
||||
{
|
||||
/**
|
||||
* Blocks within an event should be sorted by their priority descending.
|
||||
*
|
||||
* @var array<string, array<string, Block>>
|
||||
*/
|
||||
private array $eventsToBlocks;
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, Block>> $eventsToTemplateBlocks
|
||||
*/
|
||||
public function __construct(array $eventsToTemplateBlocks)
|
||||
{
|
||||
$this->eventsToBlocks = $eventsToTemplateBlocks;
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->eventsToBlocks;
|
||||
}
|
||||
|
||||
public function findEnabledForEvents(array $eventNames): array
|
||||
{
|
||||
// No need to sort blocks again if there's only one event because we have them sorted already
|
||||
if (count($eventNames) === 1) {
|
||||
$eventName = reset($eventNames);
|
||||
|
||||
return array_values(array_filter(
|
||||
$this->eventsToBlocks[$eventName] ?? [],
|
||||
static fn (Block $block): bool => $block->isEnabled(),
|
||||
));
|
||||
}
|
||||
|
||||
$enabledFinalizedBlocks = array_filter(
|
||||
$this->findFinalizedForEvents($eventNames),
|
||||
static fn (Block $block): bool => $block->isEnabled(),
|
||||
);
|
||||
|
||||
$blocksPriorityQueue = new SplPriorityQueue();
|
||||
foreach ($enabledFinalizedBlocks as $block) {
|
||||
$blocksPriorityQueue->insert($block, $block->getPriority());
|
||||
}
|
||||
|
||||
return $blocksPriorityQueue->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $eventNames
|
||||
*
|
||||
* @return Block[]
|
||||
*/
|
||||
private function findFinalizedForEvents(array $eventNames): array
|
||||
{
|
||||
/** @var array<string, Block> $finalizedBlocks */
|
||||
$finalizedBlocks = [];
|
||||
$reversedEventNames = array_reverse($eventNames);
|
||||
foreach ($reversedEventNames as $eventName) {
|
||||
$blocks = $this->eventsToBlocks[$eventName] ?? [];
|
||||
foreach ($blocks as $blockName => $block) {
|
||||
if (array_key_exists($blockName, $finalizedBlocks)) {
|
||||
$block = $finalizedBlocks[$blockName]->overwriteWith($block);
|
||||
}
|
||||
|
||||
$finalizedBlocks[$blockName] = $block;
|
||||
}
|
||||
}
|
||||
|
||||
return $finalizedBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias(BlockRegistry::class, '\Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistry');
|
||||
|
|
@ -13,23 +13,19 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
interface TemplateBlockRegistryInterface
|
||||
interface BlockRegistryInterface
|
||||
{
|
||||
/**
|
||||
* @return TemplateBlock[][]
|
||||
*
|
||||
* @psalm-return array<string, array<string, TemplateBlock>>
|
||||
* @return array<string, array<string, Block>>
|
||||
*/
|
||||
public function all(): array;
|
||||
|
||||
/**
|
||||
* @param string[] $eventNames
|
||||
*
|
||||
* @psalm-param non-empty-list<string> $eventNames
|
||||
*
|
||||
* @return TemplateBlock[]
|
||||
*
|
||||
* @psalm-return list<TemplateBlock>
|
||||
* @return Block[]
|
||||
*/
|
||||
public function findEnabledForEvents(array $eventNames): array;
|
||||
}
|
||||
|
||||
class_alias(BlockRegistryInterface::class, '\Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface');
|
||||
73
src/Sylius/Bundle/UiBundle/Registry/ComponentBlock.php
Normal file
73
src/Sylius/Bundle/UiBundle/Registry/ComponentBlock.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
final class ComponentBlock extends Block
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $componentInputs
|
||||
* @param array<string, mixed>|null $context
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $eventName,
|
||||
private string $componentName,
|
||||
private array $componentInputs,
|
||||
?array $context,
|
||||
?int $priority,
|
||||
?bool $enabled,
|
||||
) {
|
||||
parent::__construct($name, $eventName, $context, $priority, $enabled);
|
||||
}
|
||||
|
||||
public function getComponentName(): string
|
||||
{
|
||||
return $this->componentName;
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
public function getComponentInputs(): array
|
||||
{
|
||||
return $this->componentInputs;
|
||||
}
|
||||
|
||||
public function overwriteWith(Block $block): self
|
||||
{
|
||||
if (!$block instanceof ComponentBlock) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Trying to overwrite component block "%s" with block of different type "%s".',
|
||||
$this->name,
|
||||
get_class($block),
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->name !== $block->name) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Trying to overwrite block "%s" with block "%s".',
|
||||
$this->name,
|
||||
$block->name,
|
||||
));
|
||||
}
|
||||
|
||||
return new self(
|
||||
$this->name,
|
||||
$block->eventName,
|
||||
$block->getComponentName(),
|
||||
$block->getComponentInputs(),
|
||||
$block->context ?? $this->context,
|
||||
$block->priority ?? $this->priority,
|
||||
$block->enabled ?? $this->enabled,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,27 +13,17 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
final class TemplateBlock
|
||||
final class TemplateBlock extends Block
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $eventName,
|
||||
string $name,
|
||||
string $eventName,
|
||||
private ?string $template,
|
||||
private ?array $context,
|
||||
private ?int $priority,
|
||||
private ?bool $enabled,
|
||||
private ?string $component = null,
|
||||
?array $context,
|
||||
?int $priority,
|
||||
?bool $enabled,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getEventName(): string
|
||||
{
|
||||
return $this->eventName;
|
||||
parent::__construct($name, $eventName, $context, $priority, $enabled);
|
||||
}
|
||||
|
||||
public function getTemplate(): string
|
||||
|
|
@ -49,28 +39,16 @@ final class TemplateBlock
|
|||
return $this->template;
|
||||
}
|
||||
|
||||
public function getComponent(): ?string
|
||||
public function overwriteWith(Block $block): self
|
||||
{
|
||||
return $this->component;
|
||||
}
|
||||
if (!$block instanceof self) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Trying to overwrite template block "%s" with block of different type "%s".',
|
||||
$this->name,
|
||||
get_class($block),
|
||||
));
|
||||
}
|
||||
|
||||
public function getContext(): array
|
||||
{
|
||||
return $this->context ?? [];
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return $this->priority ?? 0;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled ?? true;
|
||||
}
|
||||
|
||||
public function overwriteWith(self $block): self
|
||||
{
|
||||
if ($this->name !== $block->name) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Trying to overwrite block "%s" with block "%s".',
|
||||
|
|
@ -86,7 +64,6 @@ final class TemplateBlock
|
|||
$block->context ?? $this->context,
|
||||
$block->priority ?? $this->priority,
|
||||
$block->enabled ?? $this->enabled,
|
||||
$block->component ?? $this->component,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
use Laminas\Stdlib\SplPriorityQueue;
|
||||
|
||||
final class TemplateBlockRegistry implements TemplateBlockRegistryInterface
|
||||
{
|
||||
/**
|
||||
* Blocks within an event should be sorted by their priority descending.
|
||||
*
|
||||
* @var TemplateBlock[][]
|
||||
*
|
||||
* @psalm-var array<string, array<string, TemplateBlock>>
|
||||
*/
|
||||
private $eventsToTemplateBlocks;
|
||||
|
||||
public function __construct(array $eventsToTemplateBlocks)
|
||||
{
|
||||
$this->eventsToTemplateBlocks = $eventsToTemplateBlocks;
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->eventsToTemplateBlocks;
|
||||
}
|
||||
|
||||
public function findEnabledForEvents(array $eventNames): array
|
||||
{
|
||||
// No need to sort blocks again if there's only one event because we have them sorted already
|
||||
if (count($eventNames) === 1) {
|
||||
$eventName = reset($eventNames);
|
||||
|
||||
return array_values(array_filter(
|
||||
$this->eventsToTemplateBlocks[$eventName] ?? [],
|
||||
static fn (TemplateBlock $templateBlock): bool => $templateBlock->isEnabled(),
|
||||
));
|
||||
}
|
||||
|
||||
$enabledFinalizedTemplateBlocks = array_filter(
|
||||
$this->findFinalizedForEvents($eventNames),
|
||||
static fn (TemplateBlock $templateBlock): bool => $templateBlock->isEnabled(),
|
||||
);
|
||||
|
||||
$templateBlocksPriorityQueue = new SplPriorityQueue();
|
||||
foreach ($enabledFinalizedTemplateBlocks as $templateBlock) {
|
||||
$templateBlocksPriorityQueue->insert($templateBlock, $templateBlock->getPriority());
|
||||
}
|
||||
|
||||
return $templateBlocksPriorityQueue->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $eventNames
|
||||
*
|
||||
* @psalm-param non-empty-list<string> $eventNames
|
||||
*
|
||||
* @return TemplateBlock[]
|
||||
*/
|
||||
private function findFinalizedForEvents(array $eventNames): array
|
||||
{
|
||||
/**
|
||||
* @var TemplateBlock[] $finalizedTemplateBlocks
|
||||
*
|
||||
* @psalm-var array<string, TemplateBlock> $finalizedTemplateBlocks
|
||||
*/
|
||||
$finalizedTemplateBlocks = [];
|
||||
$reversedEventNames = array_reverse($eventNames);
|
||||
foreach ($reversedEventNames as $eventName) {
|
||||
$templateBlocks = $this->eventsToTemplateBlocks[$eventName] ?? [];
|
||||
foreach ($templateBlocks as $blockName => $templateBlock) {
|
||||
if (array_key_exists($blockName, $finalizedTemplateBlocks)) {
|
||||
$templateBlock = $finalizedTemplateBlocks[$blockName]->overwriteWith($templateBlock);
|
||||
}
|
||||
|
||||
$finalizedTemplateBlocks[$blockName] = $templateBlock;
|
||||
}
|
||||
}
|
||||
|
||||
return $finalizedTemplateBlocks;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
|
||||
interface BlockRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function render(Block $templateBlock, array $context = []): string;
|
||||
}
|
||||
|
||||
class_alias(BlockRendererInterface::class, '\Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface');
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Renderer\Exception\NoSupportedBlockRenderer;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/** @internal */
|
||||
final class CompositeBlockRenderer implements BlockRendererInterface
|
||||
{
|
||||
/** @var array<SupportableBlockRendererInterface> */
|
||||
private array $blockRenderers;
|
||||
|
||||
/**
|
||||
* @param iterable<SupportableBlockRendererInterface> $blockRenderers
|
||||
*/
|
||||
public function __construct(iterable $blockRenderers)
|
||||
{
|
||||
Assert::allIsInstanceOf($blockRenderers, SupportableBlockRendererInterface::class);
|
||||
$this->blockRenderers = $blockRenderers instanceof \Traversable ? iterator_to_array($blockRenderers) : $blockRenderers;
|
||||
}
|
||||
|
||||
public function render(Block $templateBlock, array $context = []): string
|
||||
{
|
||||
foreach ($this->blockRenderers as $blockRenderer) {
|
||||
if (!$blockRenderer->supports($templateBlock)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $blockRenderer->render($templateBlock, $context);
|
||||
}
|
||||
|
||||
throw new NoSupportedBlockRenderer(sprintf('No supported block renderer found for "%s" block.', $templateBlock->getName()));
|
||||
}
|
||||
}
|
||||
|
|
@ -13,14 +13,18 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
|
||||
final class DelegatingTemplateEventRenderer implements TemplateEventRendererInterface
|
||||
final class DelegatingTwigEventRenderer implements TwigEventRendererInterface
|
||||
{
|
||||
public function __construct(private TemplateBlockRegistryInterface $templateBlockRegistry, private TemplateBlockRendererInterface $templateBlockRenderer)
|
||||
public function __construct(private BlockRegistryInterface $templateBlockRegistry, private BlockRendererInterface $templateBlockRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $eventNames
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function render(array $eventNames, array $context = []): string
|
||||
{
|
||||
$templateBlocks = $this->templateBlockRegistry->findEnabledForEvents($eventNames);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer\Exception;
|
||||
|
||||
class NoSupportedBlockRenderer extends \RuntimeException
|
||||
{
|
||||
}
|
||||
100
src/Sylius/Bundle/UiBundle/Renderer/HtmlDebugBlockRenderer.php
Normal file
100
src/Sylius/Bundle/UiBundle/Renderer/HtmlDebugBlockRenderer.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
|
||||
final class HtmlDebugBlockRenderer implements BlockRendererInterface
|
||||
{
|
||||
public function __construct(private BlockRendererInterface $blockRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
public function render(Block $templateBlock, array $context = []): string
|
||||
{
|
||||
if (!$this->shouldRenderHtmlDebug($templateBlock)) {
|
||||
return $this->blockRenderer->render($templateBlock, $context);
|
||||
}
|
||||
|
||||
$renderedParts = [];
|
||||
|
||||
switch (get_class($templateBlock)) {
|
||||
case TemplateBlock::class:
|
||||
$renderedParts[] = $this->getBeginBlockForTemplate($templateBlock);
|
||||
|
||||
break;
|
||||
case ComponentBlock::class:
|
||||
$renderedParts[] = $this->getBeginBlockForComponent($templateBlock);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$renderedParts[] = $this->blockRenderer->render($templateBlock, $context);
|
||||
$renderedParts[] = $this->getEndBlock($templateBlock);
|
||||
|
||||
return implode("\n", $renderedParts);
|
||||
}
|
||||
|
||||
private function shouldRenderHtmlDebug(Block $block): bool
|
||||
{
|
||||
return match (get_class($block)) {
|
||||
TemplateBlock::class => strrpos($block->getTemplate(), '.html.twig') !== false,
|
||||
ComponentBlock::class => true,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
||||
private function getBeginBlockForTemplate(Block $block): string
|
||||
{
|
||||
if (!$block instanceof TemplateBlock) {
|
||||
throw new \InvalidArgumentException(sprintf('Block must be instance of "%s"', TemplateBlock::class));
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<!-- BEGIN BLOCK | event name: "%s", block name: "%s", template: "%s", priority: %d -->',
|
||||
$block->getEventName(),
|
||||
$block->getName(),
|
||||
$block->getTemplate(),
|
||||
$block->getPriority(),
|
||||
);
|
||||
}
|
||||
|
||||
private function getBeginBlockForComponent(Block $block): string
|
||||
{
|
||||
if (!$block instanceof ComponentBlock) {
|
||||
throw new \InvalidArgumentException(sprintf('Block must be instance of "%s"', ComponentBlock::class));
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<!-- BEGIN BLOCK | event name: "%s", block name: "%s", component: "%s", priority: %d -->',
|
||||
$block->getEventName(),
|
||||
$block->getName(),
|
||||
$block->getComponentName(),
|
||||
$block->getPriority(),
|
||||
);
|
||||
}
|
||||
|
||||
private function getEndBlock(Block $templateBlock): string
|
||||
{
|
||||
return sprintf(
|
||||
'<!-- END BLOCK | event name: "%s", block name: "%s" -->',
|
||||
$templateBlock->getEventName(),
|
||||
$templateBlock->getName(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias(HtmlDebugBlockRenderer::class, '\Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateBlockRenderer');
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
|
||||
final class HtmlDebugTemplateBlockRenderer implements TemplateBlockRendererInterface
|
||||
{
|
||||
public function __construct(private TemplateBlockRendererInterface $templateBlockRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
public function render(TemplateBlock $templateBlock, array $context = []): string
|
||||
{
|
||||
$isTemplateBlockComponent = null !== $templateBlock->getComponent();
|
||||
$shouldRenderHtmlDebug = $isTemplateBlockComponent || strrpos($templateBlock->getTemplate(), '.html.twig') !== false;
|
||||
|
||||
if (!$shouldRenderHtmlDebug) {
|
||||
return $this->templateBlockRenderer->render($templateBlock, $context);
|
||||
}
|
||||
|
||||
$renderedParts = [];
|
||||
|
||||
if ($isTemplateBlockComponent) {
|
||||
$renderedParts[] = $this->getBeginBlockForComponent($templateBlock);
|
||||
} else {
|
||||
$renderedParts[] = $this->getBeginBlockForTemplate($templateBlock);
|
||||
}
|
||||
|
||||
$renderedParts[] = $this->templateBlockRenderer->render($templateBlock, $context);
|
||||
$renderedParts[] = $this->getEndBlock($templateBlock);
|
||||
|
||||
return implode("\n", $renderedParts);
|
||||
}
|
||||
|
||||
private function getBeginBlockForTemplate(TemplateBlock $templateBlock): string
|
||||
{
|
||||
return sprintf(
|
||||
'<!-- BEGIN BLOCK | event name: "%s", block name: "%s", template: "%s", priority: %d -->',
|
||||
$templateBlock->getEventName(),
|
||||
$templateBlock->getName(),
|
||||
$templateBlock->getTemplate(),
|
||||
$templateBlock->getPriority(),
|
||||
);
|
||||
}
|
||||
|
||||
private function getBeginBlockForComponent(TemplateBlock $templateBlock): string
|
||||
{
|
||||
return sprintf(
|
||||
'<!-- BEGIN BLOCK | event name: "%s", block name: "%s", component: "%s", priority: %d -->',
|
||||
$templateBlock->getEventName(),
|
||||
$templateBlock->getName(),
|
||||
$templateBlock->getComponent(),
|
||||
$templateBlock->getPriority(),
|
||||
);
|
||||
}
|
||||
|
||||
private function getEndBlock(TemplateBlock $templateBlock): string
|
||||
{
|
||||
return sprintf(
|
||||
'<!-- END BLOCK | event name: "%s", block name: "%s" -->',
|
||||
$templateBlock->getEventName(),
|
||||
$templateBlock->getName(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,20 +13,26 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
|
||||
final class HtmlDebugTemplateEventRenderer implements TemplateEventRendererInterface
|
||||
final class HtmlDebugTwigEventRenderer implements TwigEventRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TemplateEventRendererInterface $templateEventRenderer,
|
||||
private TemplateBlockRegistryInterface $templateBlockRegistry,
|
||||
private TwigEventRendererInterface $templateEventRenderer,
|
||||
private BlockRegistryInterface $blockRegistry,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $eventNames
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function render(array $eventNames, array $context = []): string
|
||||
{
|
||||
$shouldRenderHtmlDebug = $this->shouldRenderHtmlDebug($this->templateBlockRegistry->findEnabledForEvents($eventNames));
|
||||
$shouldRenderHtmlDebug = $this->shouldRenderHtmlDebug($this->blockRegistry->findEnabledForEvents($eventNames));
|
||||
|
||||
$renderedParts = [];
|
||||
|
||||
|
|
@ -50,24 +56,24 @@ final class HtmlDebugTemplateEventRenderer implements TemplateEventRendererInter
|
|||
}
|
||||
|
||||
/**
|
||||
* @param TemplateBlock[] $templateBlocks
|
||||
* @param Block[] $blocks
|
||||
*/
|
||||
private function shouldRenderHtmlDebug(array $templateBlocks): bool
|
||||
private function shouldRenderHtmlDebug(array $blocks): bool
|
||||
{
|
||||
return count($templateBlocks) === 0 || $this->hasAnyBlockWithComponentOrTemplate($templateBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<TemplateBlock> $templateBlocks
|
||||
*/
|
||||
private function hasAnyBlockWithComponentOrTemplate(array $templateBlocks): bool
|
||||
{
|
||||
foreach ($templateBlocks as $templateBlock) {
|
||||
if (null !== $templateBlock->getComponent() || strrpos($templateBlock->getTemplate(), '.html.twig') !== false) {
|
||||
return true;
|
||||
}
|
||||
if (0 === count($blocks)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return 0 !== count(
|
||||
array_filter($blocks, function (Block $block): bool {
|
||||
return match (get_class($block)) {
|
||||
TemplateBlock::class => strrpos($block->getTemplate(), '.html.twig') !== false,
|
||||
ComponentBlock::class => true,
|
||||
default => false,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias(HtmlDebugTwigEventRenderer::class, '\Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateEventRenderer');
|
||||
|
|
@ -13,9 +13,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
|
||||
interface TemplateBlockRendererInterface
|
||||
interface SupportableBlockRendererInterface extends BlockRendererInterface
|
||||
{
|
||||
public function render(TemplateBlock $templateBlock, array $context = []): string;
|
||||
public function supports(Block $block): bool;
|
||||
}
|
||||
|
|
@ -13,27 +13,71 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\UX\TwigComponent\ComponentRendererInterface;
|
||||
|
||||
/** @internal */
|
||||
final class TwigComponentBlockRenderer implements TemplateBlockRendererInterface
|
||||
final class TwigComponentBlockRenderer implements SupportableBlockRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TemplateBlockRendererInterface $decoratedRenderer,
|
||||
private ComponentRendererInterface $componentRenderer,
|
||||
private ContextProviderInterface $contextProvider,
|
||||
private ExpressionLanguage $expressionLanguage,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ComponentBlock $templateBlock
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function render(TemplateBlock $templateBlock, array $context = []): string
|
||||
public function render(Block $templateBlock, array $context = []): string
|
||||
{
|
||||
if (null === $templateBlock->getComponent()) {
|
||||
return $this->decoratedRenderer->render($templateBlock, $context);
|
||||
if (!$this->supports($templateBlock)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Block "%s" is not supported by "%s".',
|
||||
$templateBlock->getName(),
|
||||
self::class,
|
||||
));
|
||||
}
|
||||
|
||||
return $this->componentRenderer->createAndRender($templateBlock->getComponent(), ['context' => $templateBlock->getContext() + $context]);
|
||||
$context = $this->contextProvider->provide($context, $templateBlock);
|
||||
|
||||
$inputs = $this->mapArrayRecursively(function (mixed $value) use ($context) {
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!str_starts_with($value, 'expr:')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->expressionLanguage->evaluate(substr($value, 5), $context);
|
||||
}, $templateBlock->getComponentInputs());
|
||||
|
||||
return $this->componentRenderer->createAndRender($templateBlock->getComponentName(), $inputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $array
|
||||
* @return array<array-key, mixed>
|
||||
*/
|
||||
private function mapArrayRecursively(callable $callback, array $array): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($array as $key => $value) {
|
||||
$result[$key] = is_array($value)
|
||||
? $this->mapArrayRecursively($callback, $value)
|
||||
: $callback($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function supports(Block $block): bool
|
||||
{
|
||||
return is_a($block, ComponentBlock::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
interface TemplateEventRendererInterface
|
||||
interface TwigEventRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param string[] $eventNames
|
||||
*
|
||||
* @psalm-param non-empty-list<string> $eventNames
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function render(array $eventNames, array $context = []): string;
|
||||
}
|
||||
|
||||
class_alias(TwigEventRendererInterface::class, '\Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface');
|
||||
|
|
@ -14,17 +14,32 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Twig\Environment;
|
||||
|
||||
final class TwigTemplateBlockRenderer implements TemplateBlockRendererInterface
|
||||
final class TwigTemplateBlockRenderer implements SupportableBlockRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param iterable<ContextProviderInterface> $contextProviders
|
||||
*/
|
||||
public function __construct(private Environment $twig, private iterable $contextProviders)
|
||||
{
|
||||
}
|
||||
|
||||
public function render(TemplateBlock $templateBlock, array $context = []): string
|
||||
/**
|
||||
* @param TemplateBlock $templateBlock
|
||||
*/
|
||||
public function render(Block $templateBlock, array $context = []): string
|
||||
{
|
||||
if (!$this->supports($templateBlock)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Block "%s" is not supported by "%s".',
|
||||
$templateBlock->getName(),
|
||||
self::class,
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($this->contextProviders as $contextProvider) {
|
||||
if (!$contextProvider instanceof ContextProviderInterface || !$contextProvider->supports($templateBlock)) {
|
||||
continue;
|
||||
|
|
@ -35,4 +50,9 @@ final class TwigTemplateBlockRenderer implements TemplateBlockRendererInterface
|
|||
|
||||
return $this->twig->render($templateBlock->getTemplate(), $context);
|
||||
}
|
||||
|
||||
public function supports(Block $block): bool
|
||||
{
|
||||
return is_a($block, TemplateBlock::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,30 +13,33 @@
|
|||
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TemplateBlockRenderingHistory" />
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\BlockRenderingHistory" />
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TemplateBlockDataCollector">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\TemplateBlockRenderingHistory" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\BlockRenderingHistory" />
|
||||
<tag name="data_collector" template="@SyliusUi/DataCollector/templateBlock.html.twig" id="sylius_ui.template_block" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TraceableTemplateBlockRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\TraceableTemplateBlockRenderer.inner" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\TemplateBlockRenderingHistory" />
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TraceableBlockRenderer" decorates="sylius.ui.block_renderer.composite">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\TraceableBlockRenderer.inner" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\BlockRenderingHistory" />
|
||||
</service>
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TraceableTemplateBlockRenderer" alias="Sylius\Bundle\UiBundle\DataCollector\TraceableBlockRenderer" />
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TraceableTwigEventRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface">
|
||||
<argument type="service" id=".inner" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\BlockRenderingHistory" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\DataCollector\TraceableTemplateEventRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\TraceableTemplateEventRenderer.inner" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\DataCollector\TemplateBlockRenderingHistory" />
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugBlockRenderer" decorates="sylius.ui.block_renderer.composite">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugBlockRenderer.inner" />
|
||||
</service>
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateBlockRenderer" alias="Sylius\Bundle\UiBundle\Renderer\HtmlDebugBlockRenderer" />
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateBlockRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateBlockRenderer.inner" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateEventRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateEventRenderer.inner" />
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTwigEventRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface">
|
||||
<argument type="service" id=".inner" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface" />
|
||||
</service>
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTemplateEventRenderer" alias="Sylius\Bundle\UiBundle\Renderer\HtmlDebugTwigEventRenderer" />
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -13,24 +13,34 @@
|
|||
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface" class="Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistry">
|
||||
<service id="sylius.ui.block_renderer.composite" class="Sylius\Bundle\UiBundle\Renderer\CompositeBlockRenderer">
|
||||
<argument type="tagged_iterator" tag="sylius.ui.block_renderer" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface" class="Sylius\Bundle\UiBundle\Registry\BlockRegistry">
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
<service id="Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface" alias="Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface" />
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface" class="Sylius\Bundle\UiBundle\Renderer\TwigTemplateBlockRenderer">
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface" class="Sylius\Bundle\UiBundle\Renderer\TwigTemplateBlockRenderer">
|
||||
<argument type="service" id="twig" />
|
||||
<argument type="tagged_iterator" tag="sylius.ui.template_event.context_provider" />
|
||||
<tag name="sylius.ui.block_renderer" />
|
||||
</service>
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface" alias="Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface" />
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TwigComponentBlockRenderer" decorates="Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface" decoration-priority="1024">
|
||||
<argument type="service" id=".inner" />
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TwigComponentBlockRenderer">
|
||||
<argument type="service" id="ux.twig_component.component_renderer" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface" />
|
||||
<argument type="service" id="sylius.expression_language" />
|
||||
<tag name="sylius.ui.block_renderer" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface" class="Sylius\Bundle\UiBundle\Renderer\DelegatingTemplateEventRenderer">
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface" class="Sylius\Bundle\UiBundle\Renderer\DelegatingTwigEventRenderer">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface" />
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface" />
|
||||
<argument type="service" id="sylius.ui.block_renderer.composite" />
|
||||
</service>
|
||||
<service id="Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface" alias="Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface" />
|
||||
|
||||
<service id="Sylius\Bundle\UiBundle\Command\DebugTemplateEventCommand">
|
||||
<argument type="service" id="Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface" />
|
||||
|
|
|
|||
|
|
@ -213,6 +213,108 @@ final class ConfigurationTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function component_block_configuration_can_be_shortened_to_template_string_only(): void
|
||||
{
|
||||
$this->assertProcessedConfigurationEquals(
|
||||
[
|
||||
[
|
||||
'events' => [
|
||||
'event_name' => [
|
||||
'blocks' => [
|
||||
'block_name' => [
|
||||
'component' => 'component_name',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'events' => [
|
||||
'event_name' => [
|
||||
'blocks' => [
|
||||
'block_name' => [
|
||||
'component' => [
|
||||
'name' => 'component_name',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'events.*.blocks.*.component',
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function component_block_configuration_can_be_defined_with_inputs(): void
|
||||
{
|
||||
$this->assertProcessedConfigurationEquals(
|
||||
[
|
||||
[
|
||||
'events' => [
|
||||
'event_name' => [
|
||||
'blocks' => [
|
||||
'block_name' => [
|
||||
'component' => [
|
||||
'name' => 'component_name',
|
||||
'inputs' => [
|
||||
'foo' => 'bar',
|
||||
'bar' => 'baz',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'events' => [
|
||||
'event_name' => [
|
||||
'blocks' => [
|
||||
'block_name' => [
|
||||
'component' => [
|
||||
'name' => 'component_name',
|
||||
'inputs' => [
|
||||
'foo' => 'bar',
|
||||
'bar' => 'baz',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'events.*.blocks.*.component',
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function template_and_component_cannot_be_defined_at_the_same_time(): void
|
||||
{
|
||||
$this->assertPartialConfigurationIsInvalid(
|
||||
[
|
||||
[
|
||||
'events' => [
|
||||
'event_name' => [
|
||||
'blocks' => [
|
||||
'block_name' => [
|
||||
'template' => 'template.html.twig',
|
||||
'component' => [
|
||||
'name' => 'component_name',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'events.*.blocks.*.template',
|
||||
);
|
||||
}
|
||||
|
||||
protected function getConfiguration(): ConfigurationInterface
|
||||
{
|
||||
return new Configuration();
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ namespace Sylius\Bundle\UiBundle\Tests\DependencyInjection;
|
|||
|
||||
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
|
||||
use Sylius\Bundle\UiBundle\DependencyInjection\SyliusUiExtension;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
final class SyliusUiExtensionTest extends AbstractExtensionTestCase
|
||||
|
|
@ -30,6 +31,7 @@ final class SyliusUiExtensionTest extends AbstractExtensionTestCase
|
|||
'first_event' => ['blocks' => [
|
||||
'first_block' => ['template' => 'first.html.twig', 'context' => [], 'enabled' => true, 'priority' => 0],
|
||||
'second_block' => ['template' => 'second.html.twig', 'context' => ['foo' => 'bar'], 'enabled' => true, 'priority' => 0],
|
||||
'third_block' => ['component' => 'component_name', 'context' => [], 'enabled' => true, 'priority' => 0],
|
||||
]],
|
||||
'second_event' => ['blocks' => [
|
||||
'another_block' => ['template' => 'another.html.twig', 'context' => [], 'enabled' => true, 'priority' => 0],
|
||||
|
|
@ -37,15 +39,16 @@ final class SyliusUiExtensionTest extends AbstractExtensionTestCase
|
|||
]]);
|
||||
|
||||
$this->assertContainerBuilderHasServiceDefinitionWithArgument(
|
||||
TemplateBlockRegistryInterface::class,
|
||||
BlockRegistryInterface::class,
|
||||
0,
|
||||
[
|
||||
'first_event' => [
|
||||
'first_block' => new Definition(TemplateBlock::class, ['first_block', 'first_event', 'first.html.twig', [], 0, true, null]),
|
||||
'second_block' => new Definition(TemplateBlock::class, ['second_block', 'first_event', 'second.html.twig', ['foo' => 'bar'], 0, true, null]),
|
||||
'first_block' => new Definition(TemplateBlock::class, ['first_block', 'first_event', 'first.html.twig', [], 0, true]),
|
||||
'second_block' => new Definition(TemplateBlock::class, ['second_block', 'first_event', 'second.html.twig', ['foo' => 'bar'], 0, true]),
|
||||
'third_block' => new Definition(ComponentBlock::class, ['third_block', 'first_event', 'component_name', [], [], 0, true]),
|
||||
],
|
||||
'second_event' => [
|
||||
'another_block' => new Definition(TemplateBlock::class, ['another_block', 'second_event', 'another.html.twig', [], 0, true, null]),
|
||||
'another_block' => new Definition(TemplateBlock::class, ['another_block', 'second_event', 'another.html.twig', [], 0, true]),
|
||||
],
|
||||
],
|
||||
);
|
||||
|
|
@ -66,13 +69,13 @@ final class SyliusUiExtensionTest extends AbstractExtensionTestCase
|
|||
]]);
|
||||
|
||||
$this->assertContainerBuilderHasServiceDefinitionWithArgument(
|
||||
TemplateBlockRegistryInterface::class,
|
||||
BlockRegistryInterface::class,
|
||||
0,
|
||||
['event_name' => [
|
||||
'first_block' => new Definition(TemplateBlock::class, ['first_block', 'event_name', 'first.html.twig', [], 5, true, null]),
|
||||
'second_block' => new Definition(TemplateBlock::class, ['second_block', 'event_name', 'second.html.twig', [], 0, true, null]),
|
||||
'third_block' => new Definition(TemplateBlock::class, ['third_block', 'event_name', 'third.html.twig', [], 0, true, null]),
|
||||
'fourth_block' => new Definition(TemplateBlock::class, ['fourth_block', 'event_name', 'fourth.html.twig', [], -5, true, null]),
|
||||
'first_block' => new Definition(TemplateBlock::class, ['first_block', 'event_name', 'first.html.twig', [], 5, true]),
|
||||
'second_block' => new Definition(TemplateBlock::class, ['second_block', 'event_name', 'second.html.twig', [], 0, true]),
|
||||
'third_block' => new Definition(TemplateBlock::class, ['third_block', 'event_name', 'third.html.twig', [], 0, true]),
|
||||
'fourth_block' => new Definition(TemplateBlock::class, ['fourth_block', 'event_name', 'fourth.html.twig', [], -5, true]),
|
||||
]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\UiBundle\Tests\Functional\src;
|
||||
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
|
||||
final class CustomContextProvider implements ContextProviderInterface
|
||||
{
|
||||
public function provide(array $templateContext, TemplateBlock $templateBlock): array
|
||||
public function provide(array $templateContext, Block $templateBlock): array
|
||||
{
|
||||
return $templateContext + ['custom' => 'yolo'];
|
||||
}
|
||||
|
||||
public function supports(TemplateBlock $templateBlock): bool
|
||||
public function supports(Block $templateBlock): bool
|
||||
{
|
||||
return 'custom_context_provider' === $templateBlock->getEventName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\UiBundle\Twig;
|
||||
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class TemplateEventExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(private TemplateEventRendererInterface $templateEventRenderer)
|
||||
public function __construct(private TwigEventRendererInterface $templateEventRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\UiBundle\ContextProvider;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface;
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\Exception\NoSupportedContextProvider;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
final class CompositeContextProviderSpec extends ObjectBehavior
|
||||
{
|
||||
function it_is_a_context_provider(): void
|
||||
{
|
||||
$this->beConstructedWith([]);
|
||||
|
||||
$this->shouldImplement(ContextProviderInterface::class);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_any_of_passed_context_providers_is_not_a_context_provider(): void
|
||||
{
|
||||
$this->beConstructedWith([new \stdClass()]);
|
||||
|
||||
$this->shouldThrow(InvalidArgumentException::class)->duringInstantiation();
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_no_context_provider_supports_a_given_block(
|
||||
ContextProviderInterface $someContextProvider,
|
||||
Block $someBlock,
|
||||
): void {
|
||||
$someBlock->getName()->willReturn('some_block');
|
||||
|
||||
$someContextProvider->supports($someBlock)->willReturn(false);
|
||||
|
||||
$this->beConstructedWith([$someContextProvider]);
|
||||
|
||||
$this->shouldThrow(new NoSupportedContextProvider('No supported context provider found for block "some_block".'))->during('provide', [[], $someBlock]);
|
||||
}
|
||||
|
||||
function it_returns_always_true_for_supports(Block $someBlock): void
|
||||
{
|
||||
$this->beConstructedWith([]);
|
||||
|
||||
$this->supports($someBlock)->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_returns_a_context_for_a_first_supported_context_provider(
|
||||
ContextProviderInterface $someContextProvider,
|
||||
ContextProviderInterface $anotherContextProvider,
|
||||
ContextProviderInterface $yetAnotherContextProvider,
|
||||
Block $someBlock,
|
||||
): void {
|
||||
$someBlock->getName()->willReturn('some_block');
|
||||
|
||||
$someContextProvider->supports($someBlock)->willReturn(false);
|
||||
|
||||
$anotherContextProvider->supports($someBlock)->willReturn(true);
|
||||
$anotherContextProvider->provide(['foo' => 'bar'], $someBlock)->willReturn(['foo' => 'qux']);
|
||||
|
||||
$yetAnotherContextProvider->supports($someBlock)->shouldNotBeCalled();
|
||||
|
||||
$this->beConstructedWith([$someContextProvider, $anotherContextProvider]);
|
||||
|
||||
$this->provide(['foo' => 'bar'], $someBlock)->shouldReturn(['foo' => 'qux']);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,19 +14,20 @@ declare(strict_types=1);
|
|||
namespace spec\Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
|
||||
final class TemplateBlockRegistrySpec extends ObjectBehavior
|
||||
final class BlockRegistrySpec extends ObjectBehavior
|
||||
{
|
||||
function let(): void
|
||||
{
|
||||
$this->beConstructedWith([]);
|
||||
}
|
||||
|
||||
function it_is_a_template_block_registry(): void
|
||||
function it_is_a_block_registry(): void
|
||||
{
|
||||
$this->shouldImplement(TemplateBlockRegistryInterface::class);
|
||||
$this->shouldImplement(BlockRegistryInterface::class);
|
||||
}
|
||||
|
||||
function it_returns_all_template_blocks(): void
|
||||
|
|
@ -38,26 +39,28 @@ final class TemplateBlockRegistrySpec extends ObjectBehavior
|
|||
$this->all()->shouldReturn(['event' => ['block_name' => $templateBlock]]);
|
||||
}
|
||||
|
||||
function it_returns_enabled_template_blocks_for_a_given_event(): void
|
||||
function it_returns_enabled_blocks_for_a_given_event(): void
|
||||
{
|
||||
$firstTemplateBlock = new TemplateBlock('first_block', 'event', 'first.html.twig', [], 0, true, null);
|
||||
$secondTemplateBlock = new TemplateBlock('second_block', 'event', 'second.html.twig', [], 10, false, null);
|
||||
$thirdTemplateBlock = new TemplateBlock('third_block', 'event', 'third.html.twig', [], 50, true, null);
|
||||
$firstBlock = new TemplateBlock('first_block', 'event', 'first.html.twig', [], 0, true, null);
|
||||
$secondBlock = new TemplateBlock('second_block', 'event', 'second.html.twig', [], 10, false, null);
|
||||
$thirdBlock = new TemplateBlock('third_block', 'event', 'third.html.twig', [], 50, true, null);
|
||||
$fourthBlock = new ComponentBlock('fourth_block', 'event', 'fourth.html.twig', [], [], 0, null);
|
||||
|
||||
$this->beConstructedWith([
|
||||
'event' => [
|
||||
'first_block' => $firstTemplateBlock,
|
||||
'second_block' => $secondTemplateBlock,
|
||||
'first_block' => $firstBlock,
|
||||
'second_block' => $secondBlock,
|
||||
],
|
||||
'another_event' => [
|
||||
'third_block' => $thirdTemplateBlock,
|
||||
'third_block' => $thirdBlock,
|
||||
'fourth_block' => $fourthBlock,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->findEnabledForEvents(['event'])->shouldReturn([$firstTemplateBlock]);
|
||||
$this->findEnabledForEvents(['event'])->shouldReturn([$firstBlock]);
|
||||
}
|
||||
|
||||
function it_returns_enabled_template_blocks_for_multiple_events(): void
|
||||
function it_returns_enabled_blocks_for_multiple_events(): void
|
||||
{
|
||||
$this->beConstructedWith([
|
||||
'generic_event' => ['block' => new TemplateBlock('block', 'generic_event', 'generic.html.twig', ['foo' => 'bar'], 0, true)],
|
||||
|
|
@ -82,7 +85,7 @@ final class TemplateBlockRegistrySpec extends ObjectBehavior
|
|||
]);
|
||||
}
|
||||
|
||||
function it_returns_enabled_template_blocks_sorted_by_priority_for_multiple_events(): void
|
||||
function it_returns_enabled_blocks_sorted_by_priority_for_multiple_events(): void
|
||||
{
|
||||
$this->beConstructedWith([
|
||||
'generic_event' => [
|
||||
100
src/Sylius/Bundle/UiBundle/spec/Registry/ComponentBlockSpec.php
Normal file
100
src/Sylius/Bundle/UiBundle/spec/Registry/ComponentBlockSpec.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
|
||||
final class ComponentBlockSpec extends ObjectBehavior
|
||||
{
|
||||
function it_represents_a_component_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'MyComponent', ['my' => 'input'], ['my' => 'context'], 10, false);
|
||||
|
||||
$this->getName()->shouldReturn('block_name');
|
||||
$this->getEventName()->shouldReturn('event_name');
|
||||
$this->getComponentName()->shouldReturn('MyComponent');
|
||||
$this->getComponentInputs()->shouldReturn(['my' => 'input']);
|
||||
$this->getContext()->shouldReturn(['my' => 'context']);
|
||||
$this->getPriority()->shouldReturn(10);
|
||||
$this->isEnabled()->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_overwrites_a_component_block_with_an_another_component_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'MyComponent', ['my' => 'input'], ['my' => 'context'], 10, false);
|
||||
|
||||
$this
|
||||
->overwriteWith(
|
||||
new ComponentBlock(
|
||||
'block_name',
|
||||
'specific_event_name',
|
||||
'MyAnotherComponent',
|
||||
['my' => 'another_input'],
|
||||
['my' => 'another_context'],
|
||||
20,
|
||||
true,
|
||||
),
|
||||
)
|
||||
->shouldBeLike(
|
||||
new ComponentBlock(
|
||||
'block_name',
|
||||
'specific_event_name',
|
||||
'MyAnotherComponent',
|
||||
['my' => 'another_input'],
|
||||
['my' => 'another_context'],
|
||||
20,
|
||||
true,
|
||||
),
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_trying_to_overwrite_with_a_differently_named_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'MyComponent', ['my' => 'input'], ['my' => 'context'], 10, false);
|
||||
|
||||
$this->shouldThrow(\DomainException::class)->during(
|
||||
'overwriteWith',
|
||||
[
|
||||
new ComponentBlock(
|
||||
'another_block_name',
|
||||
'specific_event_name',
|
||||
'MyComponent',
|
||||
[],
|
||||
[],
|
||||
0,
|
||||
false,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_trying_to_override_template_block_with_template_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'MyComponent', ['my' => 'input'], ['my' => 'context'], 10, false);
|
||||
|
||||
$exceptionMessage = sprintf(
|
||||
'Trying to overwrite component block "%s" with block of different type "%s".',
|
||||
'block_name',
|
||||
'Sylius\Bundle\UiBundle\Registry\TemplateBlock',
|
||||
);
|
||||
|
||||
$this
|
||||
->shouldThrow(new \DomainException($exceptionMessage))
|
||||
->during('overwriteWith', [new TemplateBlock('block_name', 'specific_event_name', null, null, null, true)])
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,18 +14,18 @@ declare(strict_types=1);
|
|||
namespace spec\Sylius\Bundle\UiBundle\Registry;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
|
||||
final class TemplateBlockSpec extends ObjectBehavior
|
||||
{
|
||||
function it_represents_a_template_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false, null);
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false);
|
||||
|
||||
$this->getName()->shouldReturn('block_name');
|
||||
$this->getEventName()->shouldReturn('event_name');
|
||||
$this->getTemplate()->shouldReturn('block.html.twig');
|
||||
$this->getComponent()->shouldReturn(null);
|
||||
$this->getContext()->shouldReturn(['foo' => 'bar']);
|
||||
$this->getPriority()->shouldReturn(10);
|
||||
$this->isEnabled()->shouldReturn(false);
|
||||
|
|
@ -33,43 +33,59 @@ final class TemplateBlockSpec extends ObjectBehavior
|
|||
|
||||
function it_overwrites_a_template_block_with_an_another_template_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false, null);
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false);
|
||||
|
||||
$this
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', 'another.html.twig', null, null, null, 'some_component'))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'another.html.twig', ['foo' => 'bar'], 10, false, 'some_component'))
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', 'another.html.twig', null, null, null))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'another.html.twig', ['foo' => 'bar'], 10, false))
|
||||
;
|
||||
|
||||
$this
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', null, [], null, null, null))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'block.html.twig', [], 10, false, null))
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', null, [], null, null))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'block.html.twig', [], 10, false))
|
||||
;
|
||||
|
||||
$this
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', null, null, -5, null, null))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'block.html.twig', ['foo' => 'bar'], -5, false, null))
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', null, null, -5, null))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'block.html.twig', ['foo' => 'bar'], -5, false))
|
||||
;
|
||||
|
||||
$this
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', null, null, null, true, null))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'block.html.twig', ['foo' => 'bar'], 10, true, null))
|
||||
->overwriteWith(new TemplateBlock('block_name', 'specific_event_name', null, null, null, true))
|
||||
->shouldBeLike(new TemplateBlock('block_name', 'specific_event_name', 'block.html.twig', ['foo' => 'bar'], 10, true))
|
||||
;
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_trying_to_overwrite_with_a_differently_named_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false, null);
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false);
|
||||
|
||||
$this->shouldThrow(\DomainException::class)->during('overwriteWith', [new TemplateBlock('different_name', 'specific_event_name', null, null, null, null, null)]);
|
||||
$this->shouldThrow(\DomainException::class)->during('overwriteWith', [new TemplateBlock('different_name', 'specific_event_name', null, null, null, null)]);
|
||||
}
|
||||
|
||||
function it_has_sensible_defaults(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', null, null, null, null, null);
|
||||
$this->beConstructedWith('block_name', 'event_name', null, null, null, null);
|
||||
|
||||
$this->shouldThrow(\DomainException::class)->during('getTemplate');
|
||||
$this->getContext()->shouldReturn([]);
|
||||
$this->getPriority()->shouldReturn(0);
|
||||
$this->isEnabled()->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_trying_to_override_template_block_with_component_block(): void
|
||||
{
|
||||
$this->beConstructedWith('block_name', 'event_name', 'block.html.twig', ['foo' => 'bar'], 10, false);
|
||||
|
||||
$exceptionMessage = sprintf(
|
||||
'Trying to overwrite template block "%s" with block of different type "%s".',
|
||||
'block_name',
|
||||
'Sylius\Bundle\UiBundle\Registry\ComponentBlock',
|
||||
);
|
||||
|
||||
$this
|
||||
->shouldThrow(new \DomainException($exceptionMessage))
|
||||
->during('overwriteWith', [new ComponentBlock('block_name', 'specific_event_name', 'MyComponent', [], [], 0, false)])
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\Block;
|
||||
use Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\Exception\NoSupportedBlockRenderer;
|
||||
use Sylius\Bundle\UiBundle\Renderer\SupportableBlockRendererInterface;
|
||||
|
||||
final class CompositeBlockRendererSpec extends ObjectBehavior
|
||||
{
|
||||
function it_is_a_block_renderer(): void
|
||||
{
|
||||
$this->beConstructedWith([]);
|
||||
|
||||
$this->shouldImplement(BlockRendererInterface::class);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_any_of_passed_block_renderers_is_not_a_block_renderer(): void
|
||||
{
|
||||
$this->beConstructedWith([new \stdClass()]);
|
||||
|
||||
$this->shouldThrow(InvalidArgumentException::class)->duringInstantiation();
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_no_block_renderers_supports_a_given_block(
|
||||
SupportableBlockRendererInterface $someBlockRenderer,
|
||||
Block $someBlock,
|
||||
): void {
|
||||
$someBlock->getName()->willReturn('some_block');
|
||||
|
||||
$someBlockRenderer->supports($someBlock)->willReturn(false);
|
||||
|
||||
$this->beConstructedWith([$someBlockRenderer]);
|
||||
|
||||
$this
|
||||
->shouldThrow(new NoSupportedBlockRenderer('No supported block renderer found for "some_block" block.'))
|
||||
->during('render', [$someBlock, []])
|
||||
;
|
||||
}
|
||||
|
||||
function it_renders_a_block_using_a_first_supported_block_renderer(
|
||||
SupportableBlockRendererInterface $someBlockRenderer,
|
||||
SupportableBlockRendererInterface $anotherBlockRenderer,
|
||||
SupportableBlockRendererInterface $yetAnotherBlockRenderer,
|
||||
Block $someBlock,
|
||||
): void {
|
||||
$someBlock->getName()->willReturn('some_block');
|
||||
|
||||
$someBlockRenderer->supports($someBlock)->willReturn(false);
|
||||
|
||||
$anotherBlockRenderer->supports($someBlock)->willReturn(true);
|
||||
$anotherBlockRenderer->render($someBlock, [])->willReturn('Rendered block');
|
||||
|
||||
$yetAnotherBlockRenderer->supports($someBlock)->shouldNotBeCalled();
|
||||
|
||||
$this->beConstructedWith([$someBlockRenderer, $anotherBlockRenderer, $yetAnotherBlockRenderer]);
|
||||
|
||||
$this->render($someBlock, [])->shouldReturn('Rendered block');
|
||||
}
|
||||
}
|
||||
|
|
@ -15,26 +15,26 @@ namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
|||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface;
|
||||
|
||||
final class DelegatingTemplateEventRendererSpec extends ObjectBehavior
|
||||
final class DelegatingTwigEventRendererSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TemplateBlockRegistryInterface $templateBlockRegistry, TemplateBlockRendererInterface $templateBlockRenderer): void
|
||||
function let(BlockRegistryInterface $templateBlockRegistry, BlockRendererInterface $templateBlockRenderer): void
|
||||
{
|
||||
$this->beConstructedWith($templateBlockRegistry, $templateBlockRenderer);
|
||||
}
|
||||
|
||||
function it_is_a_template_event_renderer(): void
|
||||
function it_is_a_twig_event_renderer(): void
|
||||
{
|
||||
$this->shouldImplement(TemplateEventRendererInterface::class);
|
||||
$this->shouldImplement(TwigEventRendererInterface::class);
|
||||
}
|
||||
|
||||
function it_renders_template_events(
|
||||
TemplateBlockRegistryInterface $templateBlockRegistry,
|
||||
TemplateBlockRendererInterface $templateBlockRenderer,
|
||||
function it_renders_blocks_for_a_given_template(
|
||||
BlockRegistryInterface $templateBlockRegistry,
|
||||
BlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$firstTemplateBlock = new TemplateBlock('first_block', 'best_event_ever', 'firstBlock.txt.twig', [], 0, true, null);
|
||||
$secondTemplateBlock = new TemplateBlock('second_block', 'best_event_ever', 'secondBlock.txt.twig', [], 0, true, null);
|
||||
|
|
@ -48,8 +48,8 @@ final class DelegatingTemplateEventRendererSpec extends ObjectBehavior
|
|||
}
|
||||
|
||||
function it_returns_an_empty_string_if_no_blocks_are_found_for_an_event(
|
||||
TemplateBlockRegistryInterface $templateBlockRegistry,
|
||||
TemplateBlockRendererInterface $templateBlockRenderer,
|
||||
BlockRegistryInterface $templateBlockRegistry,
|
||||
BlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$templateBlockRegistry->findEnabledForEvents(['best_event_ever'])->willReturn([]);
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface;
|
||||
|
||||
final class HtmlDebugBlockRendererSpec extends ObjectBehavior
|
||||
{
|
||||
function let(BlockRendererInterface $templateBlockRenderer): void
|
||||
{
|
||||
$this->beConstructedWith($templateBlockRenderer);
|
||||
}
|
||||
|
||||
function it_is_a_block_renderer(): void
|
||||
{
|
||||
$this->shouldImplement(BlockRendererInterface::class);
|
||||
}
|
||||
|
||||
function it_does_not_render_html_debug_comment_if_template_block_is_not_an_html_twig_file_path(
|
||||
BlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'template', [], 0, true);
|
||||
|
||||
$templateBlockRenderer->render($templateBlock, [])->willReturn('template');
|
||||
|
||||
$this->render($templateBlock)->shouldReturn('template');
|
||||
}
|
||||
|
||||
function it_renders_html_debug_comment_if_template_block_is_an_html_twig_file_path(
|
||||
BlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'template.html.twig', [], 0, true);
|
||||
|
||||
$templateBlockRenderer->render($templateBlock, [])->willReturn('template');
|
||||
|
||||
$this->render($templateBlock)->shouldReturn(
|
||||
'<!-- BEGIN BLOCK | event name: "event_name", block name: "block_name", template: "template.html.twig", priority: 0 -->' . "\n" .
|
||||
'template' . "\n" .
|
||||
'<!-- END BLOCK | event name: "event_name", block name: "block_name" -->',
|
||||
);
|
||||
}
|
||||
|
||||
function it_renders_html_debug_comment_if_the_block_is_a_component(
|
||||
BlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$componentBlock = new ComponentBlock('block_name', 'event_name', 'Component', [], [], 0, true);
|
||||
|
||||
$templateBlockRenderer->render($componentBlock, ['some' => 'context'])->willReturn('template');
|
||||
|
||||
$this->render($componentBlock, ['some' => 'context'])->shouldReturn(
|
||||
'<!-- BEGIN BLOCK | event name: "event_name", block name: "block_name", component: "Component", priority: 0 -->' . "\n" .
|
||||
'template' . "\n" .
|
||||
'<!-- END BLOCK | event name: "event_name", block name: "block_name" -->',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface;
|
||||
|
||||
final class HtmlDebugTemplateBlockRendererSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TemplateBlockRendererInterface $templateBlockRenderer): void
|
||||
{
|
||||
$this->beConstructedWith($templateBlockRenderer);
|
||||
}
|
||||
|
||||
function it_is_a_template_block_renderer(): void
|
||||
{
|
||||
$this->shouldImplement(TemplateBlockRendererInterface::class);
|
||||
}
|
||||
|
||||
function it_does_not_render_a_debug_html_comment_if_the_template_block_is_not_a_component_nor_a_twig_template(
|
||||
TemplateBlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'some_content', null, null, true, null);
|
||||
|
||||
$templateBlockRenderer->render($templateBlock, [])->willReturn('Rendered template');
|
||||
|
||||
$this->render($templateBlock, [])->shouldReturn('Rendered template');
|
||||
}
|
||||
|
||||
function it_renders_a_debug_html_comment_if_the_template_block_has_a_configured_component(
|
||||
TemplateBlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'some_content', null, null, true, 'component_name');
|
||||
|
||||
$templateBlockRenderer->render($templateBlock, [])->willReturn('Rendered template');
|
||||
|
||||
$this->render($templateBlock, [])->shouldReturn(
|
||||
'<!-- BEGIN BLOCK | event name: "event_name", block name: "block_name", component: "component_name", priority: 0 -->' . "\n" .
|
||||
'Rendered template' . "\n" .
|
||||
'<!-- END BLOCK | event name: "event_name", block name: "block_name" -->'
|
||||
);
|
||||
}
|
||||
|
||||
function it_renders_a_debug_html_comment_if_the_template_block_has_a_configured_twig_template(
|
||||
TemplateBlockRendererInterface $templateBlockRenderer,
|
||||
): void {
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'template.html.twig', [], null, true, null);
|
||||
|
||||
$templateBlockRenderer->render($templateBlock, [])->willReturn('Rendered template');
|
||||
|
||||
$this->render($templateBlock, [])->shouldReturn(
|
||||
'<!-- BEGIN BLOCK | event name: "event_name", block name: "block_name", template: "template.html.twig", priority: 0 -->' . "\n" .
|
||||
'Rendered template' . "\n" .
|
||||
'<!-- END BLOCK | event name: "event_name", block name: "block_name" -->'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,25 +14,26 @@ declare(strict_types=1);
|
|||
namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\Registry\BlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlockRegistryInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateEventRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TwigEventRendererInterface;
|
||||
|
||||
final class HtmlDebugTemplateEventRendererSpec extends ObjectBehavior
|
||||
final class HtmlDebugTwigEventRendererSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TemplateEventRendererInterface $templateEventRenderer, TemplateBlockRegistryInterface $templateBlockRegistry): void
|
||||
function let(TwigEventRendererInterface $templateEventRenderer, BlockRegistryInterface $templateBlockRegistry): void
|
||||
{
|
||||
$this->beConstructedWith($templateEventRenderer, $templateBlockRegistry);
|
||||
}
|
||||
|
||||
function it_is_a_template_event_renderer(): void
|
||||
{
|
||||
$this->shouldImplement(TemplateEventRendererInterface::class);
|
||||
$this->shouldImplement(TwigEventRendererInterface::class);
|
||||
}
|
||||
|
||||
function it_does_not_render_html_debug_comments_when_there_are_no_template_blocks_with_a_defined_component_nor_template(
|
||||
TemplateEventRendererInterface $templateEventRenderer,
|
||||
TemplateBlockRegistryInterface $templateBlockRegistry
|
||||
TwigEventRendererInterface $templateEventRenderer,
|
||||
BlockRegistryInterface $templateBlockRegistry,
|
||||
): void {
|
||||
$templateBlockRegistry->findEnabledForEvents(['event_name'])->willReturn([
|
||||
new TemplateBlock('some_block_one', 'some_event', 'some content', null, null, true, null),
|
||||
|
|
@ -44,8 +45,8 @@ final class HtmlDebugTemplateEventRendererSpec extends ObjectBehavior
|
|||
}
|
||||
|
||||
function it_renders_html_debug_comment_when_no_template_block_passed(
|
||||
TemplateEventRendererInterface $templateEventRenderer,
|
||||
TemplateBlockRegistryInterface $templateBlockRegistry
|
||||
TwigEventRendererInterface $templateEventRenderer,
|
||||
BlockRegistryInterface $templateBlockRegistry,
|
||||
): void {
|
||||
$templateBlockRegistry->findEnabledForEvents(['event_name'])->willReturn([]);
|
||||
|
||||
|
|
@ -54,16 +55,16 @@ final class HtmlDebugTemplateEventRendererSpec extends ObjectBehavior
|
|||
$this->render(['event_name'])->shouldReturn(
|
||||
'<!-- BEGIN EVENT | event name: "event_name" -->' . "\n" .
|
||||
'rendered_content' . "\n" .
|
||||
'<!-- END EVENT | event name: "event_name" -->'
|
||||
'<!-- END EVENT | event name: "event_name" -->',
|
||||
);
|
||||
}
|
||||
|
||||
function it_renders_html_debug_comment_when_at_least_one_block_has_a_configured_component(
|
||||
TemplateEventRendererInterface $templateEventRenderer,
|
||||
TemplateBlockRegistryInterface $templateBlockRegistry
|
||||
TwigEventRendererInterface $templateEventRenderer,
|
||||
BlockRegistryInterface $templateBlockRegistry,
|
||||
): void {
|
||||
$templateBlockRegistry->findEnabledForEvents(['event_name'])->willReturn([
|
||||
new TemplateBlock('some_block_one', 'some_event', 'some content', null, null, true, 'SomeComponent'),
|
||||
new ComponentBlock('some_block_one', 'some_event', 'Component', [], [], 0, true),
|
||||
]);
|
||||
|
||||
$templateEventRenderer->render(['event_name'], [])->willReturn('rendered_content');
|
||||
|
|
@ -71,16 +72,16 @@ final class HtmlDebugTemplateEventRendererSpec extends ObjectBehavior
|
|||
$this->render(['event_name'])->shouldReturn(
|
||||
'<!-- BEGIN EVENT | event name: "event_name" -->' . "\n" .
|
||||
'rendered_content' . "\n" .
|
||||
'<!-- END EVENT | event name: "event_name" -->'
|
||||
'<!-- END EVENT | event name: "event_name" -->',
|
||||
);
|
||||
}
|
||||
|
||||
function it_renders_html_debug_comment_when_at_least_one_block_has_a_configured_twig_template(
|
||||
TemplateEventRendererInterface $templateEventRenderer,
|
||||
TemplateBlockRegistryInterface $templateBlockRegistry
|
||||
TwigEventRendererInterface $templateEventRenderer,
|
||||
BlockRegistryInterface $templateBlockRegistry,
|
||||
): void {
|
||||
$templateBlockRegistry->findEnabledForEvents(['event_name'])->willReturn([
|
||||
new TemplateBlock('some_block_one', 'some_event', 'some_template.html.twig', null, null, true, null),
|
||||
new TemplateBlock('some_block_one', 'some_event', 'template.html.twig', null, null, true),
|
||||
]);
|
||||
|
||||
$templateEventRenderer->render(['event_name'], [])->willReturn('rendered_content');
|
||||
|
|
@ -88,7 +89,7 @@ final class HtmlDebugTemplateEventRendererSpec extends ObjectBehavior
|
|||
$this->render(['event_name'])->shouldReturn(
|
||||
'<!-- BEGIN EVENT | event name: "event_name" -->' . "\n" .
|
||||
'rendered_content' . "\n" .
|
||||
'<!-- END EVENT | event name: "event_name" -->'
|
||||
'<!-- END EVENT | event name: "event_name" -->',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,51 +14,91 @@ declare(strict_types=1);
|
|||
namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\UX\TwigComponent\ComponentRendererInterface;
|
||||
|
||||
final class TwigComponentBlockRendererSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TemplateBlockRendererInterface $decoratedRenderer, ComponentRendererInterface $componentRenderer): void
|
||||
{
|
||||
$this->beConstructedWith($decoratedRenderer, $componentRenderer);
|
||||
}
|
||||
|
||||
function it_invokes_decorated_renderer_when_no_component_is_set(
|
||||
TemplateBlockRendererInterface $decoratedRenderer,
|
||||
): void {
|
||||
$someTemplateBlock = new TemplateBlock(
|
||||
'some_name',
|
||||
'some_event_name',
|
||||
'some_template',
|
||||
['some' => 'context'],
|
||||
0,
|
||||
true,
|
||||
null,
|
||||
);
|
||||
$decoratedRenderer->render($someTemplateBlock, ['some' => 'context'])->shouldBeCalled();
|
||||
|
||||
$this->render($someTemplateBlock, ['some' => 'context']);
|
||||
}
|
||||
|
||||
function it_renders_a_component_when_component_is_set(
|
||||
function let(
|
||||
ComponentRendererInterface $componentRenderer,
|
||||
ContextProviderInterface $contextProvider,
|
||||
ExpressionLanguage $expressionLanguage,
|
||||
): void {
|
||||
$someTemplateBlock = new TemplateBlock(
|
||||
'some_name',
|
||||
'some_event_name',
|
||||
'some_template',
|
||||
['another' => 'value'],
|
||||
$this->beConstructedWith(
|
||||
$componentRenderer,
|
||||
$contextProvider,
|
||||
$expressionLanguage,
|
||||
);
|
||||
}
|
||||
|
||||
function it_returns_true_if_block_is_supported(): void
|
||||
{
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'template.html.twig', [], 0, true);
|
||||
$componentBlock = new ComponentBlock('block_name', 'event_name', 'Component', [], [], 0, true);
|
||||
|
||||
$this->supports($templateBlock)->shouldReturn(false);
|
||||
$this->supports($componentBlock)->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_trying_to_render_unsupported_block(): void
|
||||
{
|
||||
$templateBlock = new TemplateBlock('block_name', 'event_name', 'template.html.twig', [], 0, true);
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->during('render', [$templateBlock])
|
||||
;
|
||||
}
|
||||
|
||||
function it_renders_component_block(
|
||||
ComponentRendererInterface $componentRenderer,
|
||||
ContextProviderInterface $contextProvider,
|
||||
ExpressionLanguage $expressionLanguage,
|
||||
): void {
|
||||
$componentBlock = new ComponentBlock(
|
||||
'block_name',
|
||||
'event_name',
|
||||
'Component',
|
||||
[
|
||||
'foo' => 'bar',
|
||||
'bar' => 'expr:foo',
|
||||
'nested' => [
|
||||
'foo' => 'expr:bar',
|
||||
'bar' => 'expr:baz',
|
||||
],
|
||||
],
|
||||
[],
|
||||
0,
|
||||
true,
|
||||
'some_component',
|
||||
);
|
||||
|
||||
$context = [
|
||||
'foo' => 'bar',
|
||||
'bar' => 'baz',
|
||||
'baz' => 'qux',
|
||||
];
|
||||
|
||||
$contextProvider->provide([], $componentBlock)->willReturn($context);
|
||||
|
||||
$expressionLanguage->evaluate('foo', $context)->willReturn('bar');
|
||||
$expressionLanguage->evaluate('bar', $context)->willReturn('baz');
|
||||
$expressionLanguage->evaluate('baz', $context)->willReturn('qux');
|
||||
|
||||
$componentRenderer
|
||||
->createAndRender('some_component', ['context' => ['another' => 'value', 'some' => 'value']])
|
||||
->willReturn('some_rendered_component')
|
||||
->createAndRender('Component', [
|
||||
'foo' => 'bar',
|
||||
'bar' => 'bar',
|
||||
'nested' => [
|
||||
'foo' => 'baz',
|
||||
'bar' => 'qux',
|
||||
],
|
||||
])
|
||||
->willReturn('rendered_component')
|
||||
;
|
||||
|
||||
$this->render($someTemplateBlock, ['some' => 'value'])->shouldReturn('some_rendered_component');
|
||||
$this->render($componentBlock)->shouldReturn('rendered_component');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ namespace spec\Sylius\Bundle\UiBundle\Renderer;
|
|||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface;
|
||||
use Sylius\Bundle\UiBundle\Registry\ComponentBlock;
|
||||
use Sylius\Bundle\UiBundle\Registry\TemplateBlock;
|
||||
use Sylius\Bundle\UiBundle\Renderer\TemplateBlockRendererInterface;
|
||||
use Sylius\Bundle\UiBundle\Renderer\BlockRendererInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
final class TwigTemplateBlockRendererSpec extends ObjectBehavior
|
||||
|
|
@ -35,7 +36,17 @@ final class TwigTemplateBlockRendererSpec extends ObjectBehavior
|
|||
|
||||
function it_is_a_template_block_renderer(): void
|
||||
{
|
||||
$this->shouldImplement(TemplateBlockRendererInterface::class);
|
||||
$this->shouldImplement(BlockRendererInterface::class);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_trying_to_render_unsupported_block(): void
|
||||
{
|
||||
$templateBlock = new ComponentBlock('block_name', 'event_name', 'Component', [], [], 0, true);
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->during('render', [$templateBlock])
|
||||
;
|
||||
}
|
||||
|
||||
function it_renders_a_template_block(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue