Skip to content

Commit

Permalink
IBX-6906: [DX] Introduced identifier-based view matchers (#322)
Browse files Browse the repository at this point in the history
For more details see https://issues.ibexa.co/browse/IBX-6906 and #322

Key changes:

* Dropped ViewMatcherRegistryPass in favor of tagged iterator Symfony DIC

* Refactored ViewMatcherRegistry to rely on an injectable list of matchers

* Refactored ServiceAwareMatcherFactory for identifier-based view matchers

* Extracted ViewMatcherRegistryInterface

* [Tests] Added coverage for ServiceAwareMatcherFactory

* [PHPStan] Aligned baseline with the changes
  • Loading branch information
alongosz authored Feb 5, 2024
1 parent 7f24942 commit 2c8459d
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 151 deletions.
20 changes: 0 additions & 20 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3825,16 +3825,6 @@ parameters:
count: 1
path: src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php

-
message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Matcher\\\\ServiceAwareMatcherFactory\\:\\:__construct\\(\\) has parameter \\$relativeNamespace with no type specified\\.$#"
count: 1
path: src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php

-
message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Matcher\\\\ServiceAwareMatcherFactory\\:\\:getMatcher\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MatcherInterface but returns Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ViewMatcherInterface\\.$#"
count: 2
path: src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php

-
message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$name with no type specified\\.$#"
count: 1
Expand Down Expand Up @@ -11890,11 +11880,6 @@ parameters:
count: 1
path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php

-
message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:__construct\\(\\) has parameter \\$relativeNamespace with no type specified\\.$#"
count: 1
path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php

-
message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:getMatcher\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ViewMatcherInterface but returns object\\.$#"
count: 1
Expand Down Expand Up @@ -26255,11 +26240,6 @@ parameters:
count: 1
path: tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php

-
message: "#^Parameter \\#1 \\$matchers of class Ibexa\\\\Bundle\\\\Core\\\\Matcher\\\\ViewMatcherRegistry constructor expects array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ViewMatcherInterface\\>, array\\<int, Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MatcherInterface\\|string\\> given\\.$#"
count: 1
path: tests/bundle/Core/Matcher/ViewMatcherRegistryTest.php

-
message: "#^Cannot access offset 'path' on array\\{scheme\\?\\: string, host\\?\\: string, port\\?\\: int\\<0, 65535\\>, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\|false\\.$#"
count: 2
Expand Down

This file was deleted.

2 changes: 0 additions & 2 deletions src/bundle/Core/IbexaCoreBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
use Ibexa\Bundle\Core\DependencyInjection\Compiler\StorageConnectionPass;
use Ibexa\Bundle\Core\DependencyInjection\Compiler\TranslationCollectorPass;
use Ibexa\Bundle\Core\DependencyInjection\Compiler\URLHandlerPass;
use Ibexa\Bundle\Core\DependencyInjection\Compiler\ViewMatcherRegistryPass;
use Ibexa\Bundle\Core\DependencyInjection\Compiler\ViewProvidersPass;
use Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser;
use Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser as ConfigParser;
Expand Down Expand Up @@ -78,7 +77,6 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new PlaceholderProviderPass());
$container->addCompilerPass(new NotificationRendererPass());
$container->addCompilerPass(new ConsoleCacheWarmupPass());
$container->addCompilerPass(new ViewMatcherRegistryPass());
$container->addCompilerPass(new SiteAccessMatcherRegistryPass());
$container->addCompilerPass(new ConsoleCommandPass());
$container->addCompilerPass(new LazyDoctrineRepositoriesPass(), PassConfig::TYPE_BEFORE_REMOVING);
Expand Down
24 changes: 12 additions & 12 deletions src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,26 @@
*/
namespace Ibexa\Bundle\Core\Matcher;

use Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface;
use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Core\MVC\Symfony\Matcher\ClassNameMatcherFactory;
use Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface;

/**
* A view matcher factory that also accepts services as matchers.
*
* If a service id is passed as the MatcherIdentifier, this service will be used for the matching.
* Otherwise, it will fallback to the class name based matcher factory.
* If a view matcher service is registered with `identifier` attribute, that service will be used for matching. *
* Otherwise, it will fall back to the class name-based matcher factory.
*/
final class ServiceAwareMatcherFactory extends ClassNameMatcherFactory
{
/** @var \Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry */
private $viewMatcherRegistry;
private ViewMatcherRegistryInterface $viewMatcherRegistry;

public function __construct(
ViewMatcherRegistry $viewMatcherRegistry,
ViewMatcherRegistryInterface $viewMatcherRegistry,
Repository $repository,
$relativeNamespace = null,
?string $relativeNamespace = null,
array $matchConfig = []
) {
$this->viewMatcherRegistry = $viewMatcherRegistry;
Expand All @@ -33,18 +35,16 @@ public function __construct(

/**
* @param string $matcherIdentifier
*
* @return \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MatcherInterface
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
*/
protected function getMatcher($matcherIdentifier)
protected function getMatcher($matcherIdentifier): ViewMatcherInterface
{
if (strpos($matcherIdentifier, '@') === 0) {
return $this->viewMatcherRegistry->getMatcher(substr($matcherIdentifier, 1));
$matcherIdentifier = substr($matcherIdentifier, 1);
}

return parent::getMatcher($matcherIdentifier);
return $this->viewMatcherRegistry->hasMatcher($matcherIdentifier)
? $this->viewMatcherRegistry->getMatcher($matcherIdentifier)
: parent::getMatcher($matcherIdentifier);
}
}

Expand Down
20 changes: 16 additions & 4 deletions src/bundle/Core/Matcher/ViewMatcherRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,27 @@

namespace Ibexa\Bundle\Core\Matcher;

use Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface;
use Ibexa\Core\Base\Exceptions\NotFoundException;
use Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface;

final class ViewMatcherRegistry
/**
* @internal
*/
final class ViewMatcherRegistry implements ViewMatcherRegistryInterface
{
/** @var \Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface[] */
private $matchers;

/**
* @param \Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface[] $matchers
* @param iterable<\Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface> $matchers
*/
public function __construct(array $matchers = [])
public function __construct(iterable $matchers = [])
{
$this->matchers = $matchers;
$this->matchers = [];
foreach ($matchers as $identifier => $matcher) {
$this->matchers[$identifier] = $matcher;
}
}

public function setMatcher(string $matcherIdentifier, ViewMatcherInterface $matcher): void
Expand All @@ -44,6 +51,11 @@ public function getMatcher(string $matcherIdentifier): ViewMatcherInterface

return $this->matchers[$matcherIdentifier];
}

public function hasMatcher(string $matcherIdentifier): bool
{
return isset($this->matchers[$matcherIdentifier]);
}
}

class_alias(ViewMatcherRegistry::class, 'eZ\Bundle\EzPublishCoreBundle\Matcher\ViewMatcherRegistry');
17 changes: 11 additions & 6 deletions src/bundle/Core/Resources/config/templating.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ services:
- '@Ibexa\Core\MVC\Symfony\View\Configurator\ViewProvider'
- "@?logger"

Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry: ~
Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface:
alias: Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry

Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry:
arguments:
$matchers: !tagged_iterator { tag: 'ibexa.view.matcher', index_by: identifier }

ibexa.content_view_provider.configured:
class: Ibexa\Bundle\Core\View\Provider\Configured
Expand All @@ -40,7 +45,7 @@ services:
ibexa.content_view.matcher_factory:
class: Ibexa\Bundle\Core\Matcher\ServiceAwareMatcherFactory
arguments:
- '@Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry'
- '@Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface'
- '@ibexa.api.repository'
- 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased'

Expand All @@ -61,7 +66,7 @@ services:
ibexa.content_view.default_matcher_factory:
class: Ibexa\Bundle\Core\Matcher\ServiceAwareMatcherFactory
arguments:
- '@Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry'
- '@Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface'
- '@ibexa.api.repository'
- 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased'

Expand All @@ -82,9 +87,9 @@ services:
ibexa.location_view.matcher_factory:
class: Ibexa\Bundle\Core\Matcher\ServiceAwareMatcherFactory
arguments:
- '@Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry'
- '@ibexa.api.repository'
- 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased'
$viewMatcherRegistry: '@Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface'
$repository: '@ibexa.api.repository'
$relativeNamespace: 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased'

ibexa.location_view.matcher_factory.dynamically_configured:
class: Ibexa\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator
Expand Down
20 changes: 20 additions & 0 deletions src/contracts/MVC/View/ViewMatcherRegistryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\Core\MVC\View;

use Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface;

interface ViewMatcherRegistryInterface
{
public function setMatcher(string $matcherIdentifier, ViewMatcherInterface $matcher): void;

public function getMatcher(string $matcherIdentifier): ViewMatcherInterface;

public function hasMatcher(string $matcherIdentifier): bool;
}
6 changes: 2 additions & 4 deletions src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ class ClassNameMatcherFactory implements ConfigurableMatcherFactoryInterface

/**
* Namespace built-in matchers are relative to.
*
* @var string
*/
protected $matcherRelativeNamespace;
protected ?string $matcherRelativeNamespace;

/**
* Already matched value objects with their config hash.
Expand All @@ -49,7 +47,7 @@ class ClassNameMatcherFactory implements ConfigurableMatcherFactoryInterface
*/
protected $alreadyMatched = [];

public function __construct(Repository $repository, $relativeNamespace = null, array $matchConfig = [])
public function __construct(Repository $repository, ?string $relativeNamespace = null, array $matchConfig = [])
{
$this->repository = $repository;
$this->matcherRelativeNamespace = $relativeNamespace;
Expand Down

This file was deleted.

Loading

0 comments on commit 2c8459d

Please sign in to comment.