Skip to content

Commit

Permalink
wip: register additional properties during compile time
Browse files Browse the repository at this point in the history
todo:
- register additional properties from schema extensions the new way
- deprecate event listener
- document new registration
- add changelog entries (feature, deprecation)
- add issue to drop event listener and tx_schema cache
  • Loading branch information
brotkrueml committed Mar 4, 2025
1 parent 936202a commit a6fe752
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 1 deletion.
28 changes: 28 additions & 0 deletions Classes/Core/AdditionalPropertiesInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\Core;

/**
* @api
*/
interface AdditionalPropertiesInterface
{
/**
* @return non-empty-string
*/
public function getType(): string;

/**
* @return list<non-empty-string>
*/
public function getAdditionalProperties(): array;
}
9 changes: 8 additions & 1 deletion Classes/Core/Model/AbstractType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Brotkrueml\Schema\Attributes\Type;
use Brotkrueml\Schema\Event\RegisterAdditionalTypePropertiesEvent;
use Brotkrueml\Schema\Extension;
use Brotkrueml\Schema\Type\AdditionalPropertiesProvider;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand Down Expand Up @@ -58,14 +59,20 @@ protected function addAdditionalProperties(): void
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache(Extension::CACHE_IDENTIFIER);
$additionalProperties = $cache->get($cacheEntryIdentifier);
if ($additionalProperties === false) {
$additionalPropertiesProvider = GeneralUtility::makeInstance(AdditionalPropertiesProvider::class);
$additionalProperties = [];
foreach ($additionalPropertiesProvider->get($this->getType()) as $class) {
$additionalProperties = [...$additionalProperties, ...(new $class())->getAdditionalProperties()];
}

$event = new RegisterAdditionalTypePropertiesEvent(static::class);

/** @var EventDispatcherInterface $eventDispatcher */
$eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
/** @var RegisterAdditionalTypePropertiesEvent $event */
$event = $eventDispatcher->dispatch($event);

$additionalProperties = $event->getAdditionalProperties();
$additionalProperties = [...$additionalProperties, ...$event->getAdditionalProperties()];
$cache->set($cacheEntryIdentifier, $additionalProperties, [], 0);
}

Expand Down
38 changes: 38 additions & 0 deletions Classes/DependencyInjection/AdditionalPropertiesPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\DependencyInjection;

use Brotkrueml\Schema\Type\AdditionalPropertiesProvider;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* @internal
*/
final class AdditionalPropertiesPass implements CompilerPassInterface
{
public function __construct(
private readonly string $tagName,
) {}

public function process(ContainerBuilder $container): void
{
if (! $container->hasDefinition(AdditionalPropertiesProvider::class)) {
return;
}

$providerDefinition = $container->getDefinition(AdditionalPropertiesProvider::class)->setPublic(true);
foreach (\array_keys($container->findTaggedServiceIds($this->tagName)) as $id) {
$providerDefinition->addMethodCall('add', [$id]);
}
}
}
45 changes: 45 additions & 0 deletions Classes/Type/AdditionalPropertiesProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\Type;

use Brotkrueml\Schema\Core\AdditionalPropertiesInterface;

/**
* @internal
*/
final class AdditionalPropertiesProvider
{
/**
* @var array<non-empty-string, list<class-string<AdditionalPropertiesInterface>>>
*/
private array $additionalProperties = [];

/**
* @param class-string<AdditionalPropertiesInterface> $class
*/
public function add(string $class): void
{
$type = (new $class())->getType();
if (! isset($this->additionalProperties[$type])) {
$this->additionalProperties[$type] = [];
}
$this->additionalProperties[$type][] = $class;
}

/**
* @return list<class-string<AdditionalPropertiesInterface>>
*/
public function get(string $type): array
{
return $this->additionalProperties[$type] ?? [];
}
}
5 changes: 5 additions & 0 deletions Configuration/Services.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
use Brotkrueml\Schema\Cache\PagesCacheService;
use Brotkrueml\Schema\Configuration\Configuration;
use Brotkrueml\Schema\Configuration\ConfigurationProvider;
use Brotkrueml\Schema\Core\AdditionalPropertiesInterface;
use Brotkrueml\Schema\Core\Model\TypeInterface;
use Brotkrueml\Schema\DependencyInjection\AdditionalPropertiesPass;
use Brotkrueml\Schema\DependencyInjection\TypeProviderPass;
use Brotkrueml\Schema\EventListener\AddBreadcrumbList;
use Brotkrueml\Schema\EventListener\AddWebPageType;
Expand All @@ -33,6 +35,9 @@
$builder->registerForAutoconfiguration(TypeInterface::class)->addTag('tx_schema.type');
$builder->addCompilerPass(new TypeProviderPass('tx_schema.type'));

$builder->registerForAutoconfiguration(AdditionalPropertiesInterface::class)->addTag('tx_schema.additional_properties');
$builder->addCompilerPass(new AdditionalPropertiesPass('tx_schema.additional_properties'));

$services = $configurator->services();
$services->defaults()
->autowire()
Expand Down
29 changes: 29 additions & 0 deletions Tests/Fixtures/Model/AdditionalProperties/Event.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\Tests\Fixtures\Model\AdditionalProperties;

use Brotkrueml\Schema\Core\AdditionalPropertiesInterface;

final class Event implements AdditionalPropertiesInterface
{
public function getType(): string
{
return 'Event';
}

public function getAdditionalProperties(): array
{
return [
'additional-event-property',
];
}
}
29 changes: 29 additions & 0 deletions Tests/Fixtures/Model/AdditionalProperties/Person1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\Tests\Fixtures\Model\AdditionalProperties;

use Brotkrueml\Schema\Core\AdditionalPropertiesInterface;

final class Person1 implements AdditionalPropertiesInterface
{
public function getType(): string
{
return 'Person';
}

public function getAdditionalProperties(): array
{
return [
'additional-person-property-1',
];
}
}
29 changes: 29 additions & 0 deletions Tests/Fixtures/Model/AdditionalProperties/Person2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\Tests\Fixtures\Model\AdditionalProperties;

use Brotkrueml\Schema\Core\AdditionalPropertiesInterface;

final class Person2 implements AdditionalPropertiesInterface
{
public function getType(): string
{
return 'Person';
}

public function getAdditionalProperties(): array
{
return [
'additional-person-property-2',
];
}
}
53 changes: 53 additions & 0 deletions Tests/Unit/Type/AdditionalPropertiesProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Brotkrueml\Schema\Tests\Unit\Type;

use Brotkrueml\Schema\Tests\Fixtures\Model\AdditionalProperties\Event;
use Brotkrueml\Schema\Tests\Fixtures\Model\AdditionalProperties\Person1;
use Brotkrueml\Schema\Tests\Fixtures\Model\AdditionalProperties\Person2;
use Brotkrueml\Schema\Type\AdditionalPropertiesProvider;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversClass(AdditionalPropertiesProvider::class)]
final class AdditionalPropertiesProviderTest extends TestCase
{
private AdditionalPropertiesProvider $subject;

protected function setUp(): void
{
$this->subject = new AdditionalPropertiesProvider();
}

#[Test]
public function getReturnsEmptyArrayIfTypeIsNotAvailable(): void
{
$actual = $this->subject->get('NonExisting');

self::assertSame([], $actual);
}

#[Test]
public function getReturnsPreviouslyAddedClassesCorrectly(): void
{
$this->subject->add(Person1::class);
$this->subject->add(Person2::class);
$this->subject->add(Event::class);

$actual = $this->subject->get('Person');

self::assertCount(2, $actual);
self::assertContains(Person1::class, $actual);
self::assertContains(Person2::class, $actual);
}
}

0 comments on commit a6fe752

Please sign in to comment.