Skip to content

Create custom view matcher

In addition to the built-in view matchers, you can also create custom matchers to use in template configuration.

To do it, create a matcher class that implements Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MatcherInterface.

Matcher class

The matcher class must implement the following methods:

  • matchLocation - checks if a location object matches.
  • matchContentInfo - checks if a ContentInfo object matches.
  • match - checks if the View object matches.
  • setMatchingConfig - receives the matcher's config from the view rule.

The following example shows how to implement an Owner matcher. This matcher identifies content items that have the provided owner or owners.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php declare(strict_types=1);

namespace App\View\Matcher;

use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MatcherInterface;
use Ibexa\Core\MVC\Symfony\View\ContentValueView;
use Ibexa\Core\MVC\Symfony\View\LocationValueView;
use Ibexa\Core\MVC\Symfony\View\View;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;

class Owner implements MatcherInterface
{
    private UserService $userService;

    /** @var string[] */
    private array $matchingUserLogins;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    /**
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
     */
    public function matchLocation(Location $location): bool
    {
        return $this->hasOwner($location->getContentInfo());
    }

    /**
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
     */
    public function matchContentInfo(ContentInfo $contentInfo): bool
    {
        return $this->hasOwner($contentInfo);
    }

    /**
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
     */
    public function match(View $view): ?bool
    {
        if ($view instanceof LocationValueView) {
            return $this->matchLocation($view->getLocation());
        }

        if ($view instanceof ContentValueView) {
            return $this->matchContentInfo($view->getContent()->contentInfo);
        }

        return false;
    }

    /**
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
     */
    private function hasOwner(ContentInfo $contentInfo): bool
    {
        $owner = $this->userService->loadUser($contentInfo->ownerId);

        return in_array($owner->login, $this->matchingUserLogins, true);
    }

    /**
     * @param array<string> $matchingConfig
     */
    public function setMatchingConfig($matchingConfig): void
    {
        if (!is_array($matchingConfig)) {
            throw new InvalidArgumentException('App\Owner view matcher configuration has to be an array');
        }

        $this->matchingUserLogins = $matchingConfig;
    }
}

The matcher checks whether the owner of the current content (by its ContentInfo or location) matches any of the values passed in configuration.

Matcher service

You configure your matcher as a service, tag it ibexa.view.matcher, and associate it with the identifier to use in view rules:

1
2
3
4
5
services:
    App\View\Matcher\Owner:
        autowire: true
        tags:
            - { name: ibexa.view.matcher, identifier: App\Owner }

View configuration

To apply the matcher in view configuration, indicate the matcher by its identifier.

The following configuration uses a special template to render articles owned by the users with provided logins:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ibexa_design_engine:
    design_list:
        my_design: [ my_theme ]

ibexa:
    system:
        site_group:
            design: my_design
            content_view:
                full:
                    editor_articles:
                        template: '@ibexadesign/full/featured_article.html.twig'
                        match:
                            Identifier\ContentType: article
                            App\Owner: [johndoe, janedoe]

Note

If you use a matcher that is a service instead of a simple class, tag the service with ibexa.view.matcher.