Skip to content

Conversation

@mnocon
Copy link
Contributor

@mnocon mnocon commented Dec 16, 2025

PHPStan is failing on main:

  ------ ----------------------------------------------------------------------- 
  Line   back_office/search/src/EventSubscriber/MySuggestionEventSubscriber.ph  
         p                                                                      
 ------ ----------------------------------------------------------------------- 
  40     Arrow function is missing a return type declaration                    
 ------ ----------------------------------------------------------------------- 

 ------ ----------------------------------------------- 
  Line   multisite/siteaccess/AcmeExampleExtension.php  
 ------ ----------------------------------------------- 
  32     Closure is missing a return type declaration   
 ------ ----------------------------------------------- 

I've also fixed small issues from the baseline

@github-actions
Copy link

Preview of modified files: no change to preview.

@github-actions
Copy link

code_samples/ change report

Before (on target branch)After (in current PR)

code_samples/api/public_php_api/src/Command/FindUrlCommand.php

docs/content_management/url_management/url_api.md@20:```php
docs/content_management/url_management/url_api.md@21:// ...
docs/content_management/url_management/url_api.md@22:[[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 5, 6) =]][[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 7, 10) =]]
docs/content_management/url_management/url_api.md@23:// ...
docs/content_management/url_management/url_api.md@24:[[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 34, 49) =]]
docs/content_management/url_management/url_api.md@25:```

001⫶// ...
002⫶use Ibexa\Contracts\Core\Repository\URLService;
003⫶use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion;
004⫶use Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause;
005⫶use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery;
006⫶
007⫶// ...
008⫶ $query = new URLQuery();
009⫶
010⫶ $query->filter = new Criterion\LogicalAnd(
011⫶ [
012⫶ new Criterion\SectionIdentifier(['standard']),
013⫶ new Criterion\Validity(true),
014⫶ ]
015⫶ );
016⫶ $query->sortClauses = [
017⫶ new SortClause\URL(SortClause::SORT_DESC),
018⫶ ];
019⫶ $query->offset = 0;
020⫶ $query->limit = 25;
021⫶
022⫶ $results = $this->urlService->findUrls($query);


code_samples/back_office/search/src/EventSubscriber/MySuggestionEventSubscriber.php

docs/administration/back_office/customize_search_suggestion.md@48:``` php
docs/administration/back_office/customize_search_suggestion.md@49:[[= include_file('code_samples/back_office/search/src/EventSubscriber/MySuggestionEventSubscriber.php') =]]
docs/administration/back_office/customize_search_suggestion.md@50:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\EventSubscriber;
004⫶
005⫶use App\Search\Model\Suggestion\ProductSuggestion;
006⫶use Ibexa\Contracts\ProductCatalog\ProductServiceInterface;
007⫶use Ibexa\Contracts\ProductCatalog\Values\Product\ProductQuery;
008⫶use Ibexa\Contracts\ProductCatalog\Values\Product\Query\Criterion;
009⫶use Ibexa\Contracts\Search\Event\BuildSuggestionCollectionEvent;
010⫶use Psr\Log\LoggerAwareInterface;
011⫶use Psr\Log\LoggerAwareTrait;
012⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
013⫶
014⫶class MySuggestionEventSubscriber implements EventSubscriberInterface, LoggerAwareInterface
015⫶{
016⫶ use LoggerAwareTrait;
017⫶
018⫶ public function __construct(private ProductServiceInterface $productService)
019⫶ {
020⫶ }
021⫶
022⫶ public static function getSubscribedEvents(): array
023⫶ {
024⫶ return [
025⫶ BuildSuggestionCollectionEvent::class => ['onBuildSuggestionCollectionEvent', -1],
026⫶ ];
027⫶ }
028⫶
029⫶ public function onBuildSuggestionCollectionEvent(BuildSuggestionCollectionEvent $event): BuildSuggestionCollectionEvent
030⫶ {
031⫶ $suggestionQuery = $event->getQuery();
032⫶ $suggestionCollection = $event->getSuggestionCollection();
033⫶
034⫶ $text = $suggestionQuery->getQuery();
035⫶ $words = explode(' ', (string) preg_replace('/\s+/', ' ', $text));
036⫶ $limit = $suggestionQuery->getLimit();
037⫶
038⫶ try {
039⫶ $productQuery = new ProductQuery(null, new Criterion\LogicalOr([

code_samples/api/public_php_api/src/Command/FindUrlCommand.php

docs/content_management/url_management/url_api.md@20:```php
docs/content_management/url_management/url_api.md@21:// ...
docs/content_management/url_management/url_api.md@22:[[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 5, 6) =]][[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 7, 10) =]]
docs/content_management/url_management/url_api.md@23:// ...
docs/content_management/url_management/url_api.md@24:[[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 34, 49) =]]
docs/content_management/url_management/url_api.md@25:```

001⫶// ...
002⫶use Ibexa\Contracts\Core\Repository\URLService;
003⫶use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion;
004⫶use Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause;
005⫶use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery;
006⫶
007⫶// ...
008⫶ $query = new URLQuery();
009⫶
010⫶ $query->filter = new Criterion\LogicalAnd(
011⫶ [
012⫶ new Criterion\SectionIdentifier(['standard']),
013⫶ new Criterion\Validity(true),
014⫶ ]
015⫶ );
016⫶ $query->sortClauses = [
017⫶ new SortClause\URL(SortClause::SORT_DESC),
018⫶ ];
019⫶ $query->offset = 0;
020⫶ $query->limit = 25;
021⫶
022⫶ $results = $this->urlService->findUrls($query);


code_samples/back_office/search/src/EventSubscriber/MySuggestionEventSubscriber.php

docs/administration/back_office/customize_search_suggestion.md@48:``` php
docs/administration/back_office/customize_search_suggestion.md@49:[[= include_file('code_samples/back_office/search/src/EventSubscriber/MySuggestionEventSubscriber.php') =]]
docs/administration/back_office/customize_search_suggestion.md@50:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\EventSubscriber;
004⫶
005⫶use App\Search\Model\Suggestion\ProductSuggestion;
006⫶use Ibexa\Contracts\ProductCatalog\ProductServiceInterface;
007⫶use Ibexa\Contracts\ProductCatalog\Values\Product\ProductQuery;
008⫶use Ibexa\Contracts\ProductCatalog\Values\Product\Query\Criterion;
009⫶use Ibexa\Contracts\Search\Event\BuildSuggestionCollectionEvent;
010⫶use Psr\Log\LoggerAwareInterface;
011⫶use Psr\Log\LoggerAwareTrait;
012⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
013⫶
014⫶class MySuggestionEventSubscriber implements EventSubscriberInterface, LoggerAwareInterface
015⫶{
016⫶ use LoggerAwareTrait;
017⫶
018⫶ public function __construct(private ProductServiceInterface $productService)
019⫶ {
020⫶ }
021⫶
022⫶ public static function getSubscribedEvents(): array
023⫶ {
024⫶ return [
025⫶ BuildSuggestionCollectionEvent::class => ['onBuildSuggestionCollectionEvent', -1],
026⫶ ];
027⫶ }
028⫶
029⫶ public function onBuildSuggestionCollectionEvent(BuildSuggestionCollectionEvent $event): BuildSuggestionCollectionEvent
030⫶ {
031⫶ $suggestionQuery = $event->getQuery();
032⫶ $suggestionCollection = $event->getSuggestionCollection();
033⫶
034⫶ $text = $suggestionQuery->getQuery();
035⫶ $words = explode(' ', (string) preg_replace('/\s+/', ' ', $text));
036⫶ $limit = $suggestionQuery->getLimit();
037⫶
038⫶ try {
039⫶ $productQuery = new ProductQuery(null, new Criterion\LogicalOr([
040⫶                new Criterion\ProductName(implode(' ', array_map(static fn (string $word) => "$word*", $words))),
040⫶                new Criterion\ProductName(implode(' ', array_map(static fn (string $word): string => "$word*", $words))),
041⫶                new Criterion\ProductCode($words),
042⫶ new Criterion\ProductType($words),
043⫶ ]), [], 0, $limit);
044⫶ $searchResult = $this->productService->findProducts($productQuery);
045⫶
046⫶ if ($searchResult->getTotalCount()) {
047⫶ $maxScore = 0.0;
048⫶ $suggestionsByContentIds = [];
049⫶ /** @var \Ibexa\Contracts\Search\Model\Suggestion\ContentSuggestion $suggestion */
050⫶ foreach ($suggestionCollection as $suggestion) {
051⫶ $maxScore = max($suggestion->getScore(), $maxScore);
052⫶ $suggestionsByContentIds[$suggestion->getContent()->id] = $suggestion;
053⫶ }
054⫶
055⫶ /** @var \Ibexa\ProductCatalog\Local\Repository\Values\Product $product */
056⫶ foreach ($searchResult as $product) {
057⫶ $contentId = $product->getContent()->id;
058⫶ if (array_key_exists($contentId, $suggestionsByContentIds)) {
059⫶ $suggestionCollection->remove($suggestionsByContentIds[$contentId]);
060⫶ }
061⫶
062⫶ $productSuggestion = new ProductSuggestion($maxScore + 1, $product);
063⫶ $suggestionCollection->append($productSuggestion);
064⫶ }
065⫶ }
066⫶ } catch (\Throwable $throwable) {
067⫶ $this->logger->error($throwable);
068⫶ }
069⫶
070⫶ return $event;
071⫶ }
072⫶}


code_samples/multisite/siteaccess/AcmeExampleExtension.php

docs/multisite/siteaccess/siteaccess_aware_configuration.md@70:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@71:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 0, 42) =]][[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 53, 62) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@72:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace Acme\ExampleBundle\DependencyInjection;
004⫶
005⫶use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor;
006⫶use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface;
007⫶use Symfony\Component\Config\FileLocator;
008⫶use Symfony\Component\DependencyInjection\ContainerBuilder;
009⫶use Symfony\Component\DependencyInjection\Extension\Extension;
010⫶use Symfony\Component\DependencyInjection\Loader;
011⫶
012⫶final class AcmeExampleExtension extends Extension
013⫶{
014⫶ public const string ACME_CONFIG_DIR = __DIR__ . '/../../../config/acme';
015⫶
016⫶ /**
017⫶ * @throws \Exception
018⫶ */
019⫶ public function load(array $configs, ContainerBuilder $container): void
020⫶ {
021⫶ $configuration = $this->getConfiguration($configs, $container);
022⫶ $config = $this->processConfiguration($configuration, $configs);
023⫶
024⫶ $loader = new Loader\YamlFileLoader($container, new FileLocator(self::ACME_CONFIG_DIR));
025⫶ $loader->load('default_settings.yaml');
026⫶
027⫶ $processor = new ConfigurationProcessor($container, 'acme_example');
028⫶ $processor->mapConfig(
029⫶ $config,
030⫶ // Any kind of callable can be used here.
031⫶ // It is called for each declared scope/SiteAccess.
041⫶                new Criterion\ProductCode($words),
042⫶ new Criterion\ProductType($words),
043⫶ ]), [], 0, $limit);
044⫶ $searchResult = $this->productService->findProducts($productQuery);
045⫶
046⫶ if ($searchResult->getTotalCount()) {
047⫶ $maxScore = 0.0;
048⫶ $suggestionsByContentIds = [];
049⫶ /** @var \Ibexa\Contracts\Search\Model\Suggestion\ContentSuggestion $suggestion */
050⫶ foreach ($suggestionCollection as $suggestion) {
051⫶ $maxScore = max($suggestion->getScore(), $maxScore);
052⫶ $suggestionsByContentIds[$suggestion->getContent()->id] = $suggestion;
053⫶ }
054⫶
055⫶ /** @var \Ibexa\ProductCatalog\Local\Repository\Values\Product $product */
056⫶ foreach ($searchResult as $product) {
057⫶ $contentId = $product->getContent()->id;
058⫶ if (array_key_exists($contentId, $suggestionsByContentIds)) {
059⫶ $suggestionCollection->remove($suggestionsByContentIds[$contentId]);
060⫶ }
061⫶
062⫶ $productSuggestion = new ProductSuggestion($maxScore + 1, $product);
063⫶ $suggestionCollection->append($productSuggestion);
064⫶ }
065⫶ }
066⫶ } catch (\Throwable $throwable) {
067⫶ $this->logger->error($throwable);
068⫶ }
069⫶
070⫶ return $event;
071⫶ }
072⫶}


code_samples/multisite/siteaccess/AcmeExampleExtension.php

docs/multisite/siteaccess/siteaccess_aware_configuration.md@70:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@71:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 0, 42) =]][[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 53, 62) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@72:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace Acme\ExampleBundle\DependencyInjection;
004⫶
005⫶use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor;
006⫶use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface;
007⫶use Symfony\Component\Config\FileLocator;
008⫶use Symfony\Component\DependencyInjection\ContainerBuilder;
009⫶use Symfony\Component\DependencyInjection\Extension\Extension;
010⫶use Symfony\Component\DependencyInjection\Loader;
011⫶
012⫶final class AcmeExampleExtension extends Extension
013⫶{
014⫶ public const string ACME_CONFIG_DIR = __DIR__ . '/../../../config/acme';
015⫶
016⫶ /**
017⫶ * @throws \Exception
018⫶ */
019⫶ public function load(array $configs, ContainerBuilder $container): void
020⫶ {
021⫶ $configuration = $this->getConfiguration($configs, $container);
022⫶ $config = $this->processConfiguration($configuration, $configs);
023⫶
024⫶ $loader = new Loader\YamlFileLoader($container, new FileLocator(self::ACME_CONFIG_DIR));
025⫶ $loader->load('default_settings.yaml');
026⫶
027⫶ $processor = new ConfigurationProcessor($container, 'acme_example');
028⫶ $processor->mapConfig(
029⫶ $config,
030⫶ // Any kind of callable can be used here.
031⫶ // It is called for each declared scope/SiteAccess.
032⫶            static function ($scopeSettings, $currentScope, ContextualizerInterface $contextualizer) {
032⫶            static function ($scopeSettings, $currentScope, ContextualizerInterface $contextualizer): void {
033⫶                // Maps the "name" setting to "acme_example.<$currentScope>.name" container parameter
034⫶ // It is then possible to retrieve this parameter through ConfigResolver in the application code:
035⫶ // $helloSetting = $configResolver->getParameter( 'name', 'acme_example' );
036⫶ $contextualizer->setContextualParameter('name', $currentScope, $scopeSettings['name']);
037⫶ }
038⫶ );
039⫶
040⫶ // Now map "custom_setting" and ensure the key defined for "my_siteaccess" overrides the one for "my_siteaccess_group"
041⫶ // It is done outside the closure as it's needed only once.
042⫶ $processor->mapConfigArray('custom_setting', $config);
043⫶ }
044⫶
045⫶ /** @param array<mixed> $config */
046⫶ #[\Override]
047⫶ public function getConfiguration(array $config, ContainerBuilder $container): Configuration
048⫶ {
049⫶ return new Configuration();
050⫶ }
051⫶}

docs/multisite/siteaccess/siteaccess_aware_configuration.md@76:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@77:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 44, 46) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@78:```

001⫶ $processor = new ConfigurationProcessor($container, 'acme_example');
002⫶ $processor->mapSetting('name', $config);

docs/multisite/siteaccess/siteaccess_aware_configuration.md@101:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@102:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 48, 49) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@103:```

001⫶ $processor->mapConfigArray('custom_setting', $config);

docs/multisite/siteaccess/siteaccess_aware_configuration.md@155:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@156:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 51, 53) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@157:```

001⫶ $contextualizer = $processor->getContextualizer();
002⫶ $contextualizer->mapConfigArray('custom_setting', $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL);


code_samples/page/custom_page_block/src/Event/Subscriber/BlockEmbedEventEventSubscriber.php

docs/content_management/pages/create_custom_page_block.md@139:``` php
docs/content_management/pages/create_custom_page_block.md@140:[[= include_file('code_samples/page/custom_page_block/src/Event/Subscriber/BlockEmbedEventEventSubscriber.php') =]]
docs/content_management/pages/create_custom_page_block.md@141:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Event\Subscriber;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\BlockRenderEvents;
007⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Event\PreRenderEvent;
008⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
009⫶
010⫶class BlockEmbedEventEventSubscriber implements EventSubscriberInterface
011⫶{
012⫶ public function __construct(private readonly ContentService $contentService)
013⫶ {
014⫶ }
015⫶
016⫶ public static function getSubscribedEvents(): array
017⫶ {
018⫶ return [
019⫶ BlockRenderEvents::getBlockPreRenderEventName('event') => 'onBlockPreRender',
020⫶ ];
021⫶ }
022⫶
023⫶ public function onBlockPreRender(PreRenderEvent $event): void
024⫶ {
033⫶                // Maps the "name" setting to "acme_example.<$currentScope>.name" container parameter
034⫶ // It is then possible to retrieve this parameter through ConfigResolver in the application code:
035⫶ // $helloSetting = $configResolver->getParameter( 'name', 'acme_example' );
036⫶ $contextualizer->setContextualParameter('name', $currentScope, $scopeSettings['name']);
037⫶ }
038⫶ );
039⫶
040⫶ // Now map "custom_setting" and ensure the key defined for "my_siteaccess" overrides the one for "my_siteaccess_group"
041⫶ // It is done outside the closure as it's needed only once.
042⫶ $processor->mapConfigArray('custom_setting', $config);
043⫶ }
044⫶
045⫶ /** @param array<mixed> $config */
046⫶ #[\Override]
047⫶ public function getConfiguration(array $config, ContainerBuilder $container): Configuration
048⫶ {
049⫶ return new Configuration();
050⫶ }
051⫶}

docs/multisite/siteaccess/siteaccess_aware_configuration.md@76:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@77:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 44, 46) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@78:```

001⫶ $processor = new ConfigurationProcessor($container, 'acme_example');
002⫶ $processor->mapSetting('name', $config);

docs/multisite/siteaccess/siteaccess_aware_configuration.md@101:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@102:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 48, 49) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@103:```

001⫶ $processor->mapConfigArray('custom_setting', $config);

docs/multisite/siteaccess/siteaccess_aware_configuration.md@155:``` php
docs/multisite/siteaccess/siteaccess_aware_configuration.md@156:[[= include_file('code_samples/multisite/siteaccess/AcmeExampleExtension.php', 51, 53) =]]
docs/multisite/siteaccess/siteaccess_aware_configuration.md@157:```

001⫶ $contextualizer = $processor->getContextualizer();
002⫶ $contextualizer->mapConfigArray('custom_setting', $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL);


code_samples/page/custom_page_block/src/Event/Subscriber/BlockEmbedEventEventSubscriber.php

docs/content_management/pages/create_custom_page_block.md@139:``` php
docs/content_management/pages/create_custom_page_block.md@140:[[= include_file('code_samples/page/custom_page_block/src/Event/Subscriber/BlockEmbedEventEventSubscriber.php') =]]
docs/content_management/pages/create_custom_page_block.md@141:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Event\Subscriber;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\BlockRenderEvents;
007⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Event\PreRenderEvent;
008⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
009⫶
010⫶class BlockEmbedEventEventSubscriber implements EventSubscriberInterface
011⫶{
012⫶ public function __construct(private readonly ContentService $contentService)
013⫶ {
014⫶ }
015⫶
016⫶ public static function getSubscribedEvents(): array
017⫶ {
018⫶ return [
019⫶ BlockRenderEvents::getBlockPreRenderEventName('event') => 'onBlockPreRender',
020⫶ ];
021⫶ }
022⫶
023⫶ public function onBlockPreRender(PreRenderEvent $event): void
024⫶ {
025⫶        $renderRequest = $event->getRenderRequest();
026⫶ $parameters = $event->getRenderRequest()->getParameters();
027⫶ $parameters['event_content'] = $this->contentService->loadContent($parameters['event']);
028⫶ $renderRequest->setParameters($parameters);
029⫶ }
030⫶}
025⫶        /** @var \Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Twig\TwigRenderRequest $renderRequest */
026⫶ $renderRequest = $event->getRenderRequest();
027⫶ $parameters = $event->getRenderRequest()->getParameters();
028⫶ $parameters['event_content'] = $this->contentService->loadContent($parameters['event']);
029⫶ $renderRequest->setParameters($parameters);
030⫶ }
031⫶}


code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php

docs/content_management/pages/page_blocks.md@103:``` php
docs/content_management/pages/page_blocks.md@104:[[= include_file('code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php') =]]
docs/content_management/pages/page_blocks.md@105:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Block\Listener;
004⫶
005⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\BlockRenderEvents;
006⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Event\PreRenderEvent;
007⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
008⫶
009⫶class MyBlockListener implements EventSubscriberInterface
010⫶{
011⫶ public static function getSubscribedEvents(): array
012⫶ {
013⫶ return [
014⫶ BlockRenderEvents::getBlockPreRenderEventName('event') => 'onBlockPreRender',
015⫶ ];
016⫶ }
017⫶
018⫶ public function onBlockPreRender(PreRenderEvent $event): void
019⫶ {


code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php

docs/content_management/pages/page_blocks.md@103:``` php
docs/content_management/pages/page_blocks.md@104:[[= include_file('code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php') =]]
docs/content_management/pages/page_blocks.md@105:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Block\Listener;
004⫶
005⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\BlockRenderEvents;
006⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Event\PreRenderEvent;
007⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
008⫶
009⫶class MyBlockListener implements EventSubscriberInterface
010⫶{
011⫶ public static function getSubscribedEvents(): array
012⫶ {
013⫶ return [
014⫶ BlockRenderEvents::getBlockPreRenderEventName('event') => 'onBlockPreRender',
015⫶ ];
016⫶ }
017⫶
018⫶ public function onBlockPreRender(PreRenderEvent $event): void
019⫶ {
020⫶        $renderRequest = $event->getRenderRequest();
021⫶
022⫶ $parameters = $event->getRenderRequest()->getParameters();
023⫶
024⫶ $parameters['my_parameter'] = 'parameter_value';
025⫶
026⫶ $renderRequest->setParameters($parameters);
027⫶ }
028⫶}
020⫶        /** @var \Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Twig\TwigRenderRequest $renderRequest */
021⫶ $renderRequest = $event->getRenderRequest();
022⫶
023⫶ $parameters = $event->getRenderRequest()->getParameters();
024⫶
025⫶ $parameters['my_parameter'] = 'parameter_value';
026⫶
027⫶ $renderRequest->setParameters($parameters);
028⫶ }
029⫶}


code_samples/tutorials/page_tutorial/src/Event/RandomBlockListener.php

docs/tutorials/page_and_form_tutorial/4_create_a_custom_block.md@42:``` php
docs/tutorials/page_and_form_tutorial/4_create_a_custom_block.md@43:[[= include_file('code_samples/tutorials/page_tutorial/src/Event/RandomBlockListener.php') =]]
docs/tutorials/page_and_form_tutorial/4_create_a_custom_block.md@44:```

001⫶<?php
002⫶
003⫶declare(strict_types=1);
004⫶
005⫶namespace App\Event;
006⫶
007⫶use Ibexa\Contracts\Core\Repository\ContentService;
008⫶use Ibexa\Contracts\Core\Repository\LocationService;
009⫶use Ibexa\Contracts\Core\Repository\SearchService;
010⫶use Ibexa\Contracts\Core\Repository\Values\Content\Location;
011⫶use Ibexa\Contracts\Core\Repository\Values\Content\Query;
012⫶use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
013⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\BlockRenderEvents;
014⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Event\PreRenderEvent;
015⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
016⫶
017⫶class RandomBlockListener implements EventSubscriberInterface
018⫶{
019⫶ public function __construct(
020⫶ private readonly ContentService $contentService,
021⫶ private readonly LocationService $locationService,
022⫶ private readonly SearchService $searchService
023⫶ ) {
024⫶ }
025⫶
026⫶ public static function getSubscribedEvents(): array
027⫶ {
028⫶ return [
029⫶ BlockRenderEvents::getBlockPreRenderEventName('random') => 'onBlockPreRender',
030⫶ ];
031⫶ }
032⫶
033⫶ public function onBlockPreRender(PreRenderEvent $event): void
034⫶ {
035⫶ $blockValue = $event->getBlockValue();


code_samples/tutorials/page_tutorial/src/Event/RandomBlockListener.php

docs/tutorials/page_and_form_tutorial/4_create_a_custom_block.md@42:``` php
docs/tutorials/page_and_form_tutorial/4_create_a_custom_block.md@43:[[= include_file('code_samples/tutorials/page_tutorial/src/Event/RandomBlockListener.php') =]]
docs/tutorials/page_and_form_tutorial/4_create_a_custom_block.md@44:```

001⫶<?php
002⫶
003⫶declare(strict_types=1);
004⫶
005⫶namespace App\Event;
006⫶
007⫶use Ibexa\Contracts\Core\Repository\ContentService;
008⫶use Ibexa\Contracts\Core\Repository\LocationService;
009⫶use Ibexa\Contracts\Core\Repository\SearchService;
010⫶use Ibexa\Contracts\Core\Repository\Values\Content\Location;
011⫶use Ibexa\Contracts\Core\Repository\Values\Content\Query;
012⫶use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
013⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\BlockRenderEvents;
014⫶use Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Event\PreRenderEvent;
015⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
016⫶
017⫶class RandomBlockListener implements EventSubscriberInterface
018⫶{
019⫶ public function __construct(
020⫶ private readonly ContentService $contentService,
021⫶ private readonly LocationService $locationService,
022⫶ private readonly SearchService $searchService
023⫶ ) {
024⫶ }
025⫶
026⫶ public static function getSubscribedEvents(): array
027⫶ {
028⫶ return [
029⫶ BlockRenderEvents::getBlockPreRenderEventName('random') => 'onBlockPreRender',
030⫶ ];
031⫶ }
032⫶
033⫶ public function onBlockPreRender(PreRenderEvent $event): void
034⫶ {
035⫶ $blockValue = $event->getBlockValue();
036⫶        $renderRequest = $event->getRenderRequest();
037⫶
038⫶ $parameters = $renderRequest->getParameters();
039⫶
040⫶ $contentIdAttribute = $blockValue->getAttribute('parent');
041⫶ $location = $this->loadLocationByContentId((int) $contentIdAttribute->getValue());
042⫶ $contents = $this->findContentItems($location);
043⫶ shuffle($contents);
044⫶
045⫶ $parameters['randomContent'] = reset($contents);
046⫶
047⫶ $renderRequest->setParameters($parameters);
048⫶ }
049⫶
050⫶ private function findContentItems(Location $location): array
051⫶ {
052⫶ $query = new Query();
053⫶ $query->query = new Criterion\LogicalAnd(
054⫶ [
055⫶ new Criterion\ParentLocationId($location->id),
056⫶ new Criterion\Visibility(Criterion\Visibility::VISIBLE),
057⫶ ]
058⫶ );
059⫶
060⫶ $searchHits = $this->searchService->findContent($query)->searchHits;
061⫶
062⫶ $contentArray = [];
063⫶ foreach ($searchHits as $searchHit) {
064⫶ $contentArray[] = $searchHit->valueObject;
065⫶ }
066⫶
067⫶ return $contentArray;
068⫶ }
069⫶
070⫶ private function loadLocationByContentId(int $contentId): Location
071⫶ {
072⫶ $contentInfo = $this->contentService->loadContentInfo($contentId);
073⫶
074⫶ return $this->locationService->loadLocation($contentInfo->mainLocationId);
075⫶ }
076⫶}
036⫶        /** @var \Ibexa\FieldTypePage\FieldType\Page\Block\Renderer\Twig\TwigRenderRequest $renderRequest */
037⫶ $renderRequest = $event->getRenderRequest();
038⫶
039⫶ $parameters = $renderRequest->getParameters();
040⫶
041⫶ $contentIdAttribute = $blockValue->getAttribute('parent');
042⫶ $location = $this->loadLocationByContentId((int) $contentIdAttribute->getValue());
043⫶ $contents = $this->findContentItems($location);
044⫶ shuffle($contents);
045⫶
046⫶ $parameters['randomContent'] = reset($contents);
047⫶
048⫶ $renderRequest->setParameters($parameters);
049⫶ }
050⫶
051⫶ private function findContentItems(Location $location): array
052⫶ {
053⫶ $query = new Query();
054⫶ $query->query = new Criterion\LogicalAnd(
055⫶ [
056⫶ new Criterion\ParentLocationId($location->id),
057⫶ new Criterion\Visibility(Criterion\Visibility::VISIBLE),
058⫶ ]
059⫶ );
060⫶
061⫶ $searchHits = $this->searchService->findContent($query)->searchHits;
062⫶
063⫶ $contentArray = [];
064⫶ foreach ($searchHits as $searchHit) {
065⫶ $contentArray[] = $searchHit->valueObject;
066⫶ }
067⫶
068⫶ return $contentArray;
069⫶ }
070⫶
071⫶ private function loadLocationByContentId(int $contentId): Location
072⫶ {
073⫶ $contentInfo = $this->contentService->loadContentInfo($contentId);
074⫶
075⫶ return $this->locationService->loadLocation($contentInfo->mainLocationId);
076⫶ }
077⫶}


Download colorized diff

@mnocon mnocon marked this pull request as ready for review December 17, 2025 08:37
@mnocon mnocon requested a review from a team December 17, 2025 08:37
@mnocon mnocon merged commit e238d3b into 5.0 Dec 17, 2025
18 checks passed
@mnocon mnocon deleted the phpstan branch December 17, 2025 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants