Skip to content

[TASK] Refactor dependency injection #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class News extends \GeorgRinger\News\Domain\Model\News
* "api_get_item_t3apinews_news",
* "api_patch_item_t3apinews_news",
* })
* @T3api\ORM\Cascade("persist")
*/
protected $falMedia;

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

declare(strict_types=1);

namespace SourceBroker\T3api\Attribute;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[\Attribute(\Attribute::TARGET_CLASS)]
class AsApiResourcePathProvider extends Autoconfigure
{
public const TAG_NAME = 't3api.api_resource_path_provider';
public function __construct(int $priority = 0)
{
parent::__construct(
tags: [
[self::TAG_NAME => ['priority' => $priority]],
]
);
}
}
21 changes: 21 additions & 0 deletions Classes/Attribute/AsOperationHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SourceBroker\T3api\Attribute;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[\Attribute(\Attribute::TARGET_CLASS)]
class AsOperationHandler extends Autoconfigure
{
public const TAG_NAME = 't3api.operation_handler';
public function __construct(int $priority = 0)
{
parent::__construct(
tags: [
[self::TAG_NAME => ['priority' => $priority]],
]
);
}
}
21 changes: 21 additions & 0 deletions Classes/Attribute/AsProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SourceBroker\T3api\Attribute;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[\Attribute(\Attribute::TARGET_CLASS)]
class AsProcessor extends Autoconfigure
{
public const TAG_NAME = 't3api.processor';
public function __construct(int $priority = 0)
{
parent::__construct(
tags: [
[self::TAG_NAME => ['priority' => $priority]],
]
);
}
}
21 changes: 21 additions & 0 deletions Classes/Attribute/AsSerializerHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SourceBroker\T3api\Attribute;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[\Attribute(\Attribute::TARGET_CLASS)]
class AsSerializerHandler extends Autoconfigure
{
public const TAG_NAME = 't3api.serializer_handler';
public function __construct(int $priority = 0)
{
parent::__construct(
tags: [
[self::TAG_NAME => ['priority' => $priority]],
]
);
}
}
21 changes: 21 additions & 0 deletions Classes/Attribute/AsSerializerObjectConstructor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SourceBroker\T3api\Attribute;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[\Attribute(\Attribute::TARGET_CLASS)]
class AsSerializerObjectConstructor extends Autoconfigure
{
public const TAG_NAME = 't3api.serializer_object_constructor';
public function __construct(int $priority = 0)
{
parent::__construct(
tags: [
[self::TAG_NAME => ['priority' => $priority]],
]
);
}
}
21 changes: 21 additions & 0 deletions Classes/Attribute/AsSerializerSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SourceBroker\T3api\Attribute;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[\Attribute(\Attribute::TARGET_CLASS)]
class AsSerializerSubscriber extends Autoconfigure
{
public const TAG_NAME = 't3api.serializer_subscriber';
public function __construct(int $priority = 0)
{
parent::__construct(
tags: [
[self::TAG_NAME => ['priority' => $priority]],
]
);
}
}
75 changes: 49 additions & 26 deletions Classes/Configuration/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,53 @@ class Configuration
{
public static function getOperationHandlers(): array
{
return self::getClassNamesSortedByPriority($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['operationHandlers']);
trigger_error('Configuration::getOperationHandlers() will be removed in t3api v4.0.', E_USER_DEPRECATED);
return self::getClassNamesSortedByPriority($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['operationHandlers'] ?? []);
}

public static function getProcessors(): array
{
return self::getClassNamesSortedByPriority($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['processors']);
trigger_error('Configuration::getProcessors() will be removed in t3api v4.0.', E_USER_DEPRECATED);
return self::getClassNamesSortedByPriority($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['processors'] ?? []);
}

public static function getCollectionResponseClass(): string
public static function getSerializerHandlers(): array
{
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['collectionResponseClass'];
trigger_error('Configuration::getSerializerHandlers() will be removed in t3api v4.0.', E_USER_DEPRECATED);
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['serializerHandlers'] ?? [];
}

public static function getCors(): array
public static function getSerializerSubscribers(): array
{
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['cors'];
trigger_error('Configuration::getSerializerSubscribers() will be removed in t3api v4.0.', E_USER_DEPRECATED);
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['serializerSubscribers'] ?? [];
}

protected static function getClassNamesSortedByPriority(?array $items): array
public static function getSerializerObjectConstructors(): array
{
$items = $items ?? [];
$items = array_map(
static function ($class, $priority): array {
return [
'className' => $class,
'priority' => is_numeric($priority) ? $priority : 50,
];
},
array_keys($items),
$items
);
trigger_error('Configuration::getSerializerObjectConstructors() will be removed in t3api v4.0.', E_USER_DEPRECATED);
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['serializerObjectConstructors'] ?? [];
}

usort(
$items,
static function (array $itemA, array $itemB): int {
return $itemB['priority'] <=> $itemA['priority'];
}
);
public static function getCollectionResponseClass(): string
{
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['collectionResponseClass'];
}

return array_column($items, 'className');
public static function getCors(): array
{
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['cors'];
}

/**
* @return \Generator|ApiResourcePathProvider[]
*/
public static function getApiResourcePathProviders(): \Generator
{
$apiResourcePathProvidersClasses = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['apiResourcePathProviders'];
trigger_error('Configuration::getApiResourcePathProviders() will be removed in t3api v4.0.', E_USER_DEPRECATED);

$apiResourcePathProvidersClasses
= $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3api']['apiResourcePathProviders'] ?? [];

foreach ($apiResourcePathProvidersClasses as $apiResourcePathProviderClass) {
$apiResourcePathProvider = GeneralUtility::makeInstance($apiResourcePathProviderClass);
Expand All @@ -77,4 +76,28 @@ public static function getApiResourcePathProviders(): \Generator
yield $apiResourcePathProvider;
}
}

protected static function getClassNamesSortedByPriority(?array $items): array
{
$items = $items ?? [];
$items = array_map(
static function ($class, $priority): array {
return [
'className' => $class,
'priority' => is_numeric($priority) ? $priority : 50,
];
},
array_keys($items),
$items
);

usort(
$items,
static function (array $itemA, array $itemB): int {
return $itemB['priority'] <=> $itemA['priority'];
}
);

return array_column($items, 'className');
}
}
42 changes: 25 additions & 17 deletions Classes/Dispatcher/AbstractDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface;
use SourceBroker\T3api\Configuration\Configuration;
use SourceBroker\T3api\Domain\Model\OperationInterface;
use SourceBroker\T3api\Domain\Repository\ApiResourceRepository;
use SourceBroker\T3api\Event\AfterProcessOperationEvent;
use SourceBroker\T3api\Exception\RouteNotFoundException;
use SourceBroker\T3api\Factory\OperationHandlerCollectionFactory;
use SourceBroker\T3api\Factory\ProcessorsCollectionFactory;
use SourceBroker\T3api\OperationHandler\OperationHandlerInterface;
use SourceBroker\T3api\Processor\ProcessorInterface;
use SourceBroker\T3api\Serializer\ContextBuilder\DeserializationContextBuilder;
Expand All @@ -21,7 +22,6 @@
use Symfony\Component\Routing\Exception\ResourceNotFoundException as SymfonyResourceNotFoundException;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use TYPO3\CMS\Core\Utility\GeneralUtility;

abstract class AbstractDispatcher
{
Expand All @@ -35,20 +35,32 @@ abstract class AbstractDispatcher

protected DeserializationContextBuilder $deserializationContextBuilder;

protected OperationHandlerCollectionFactory $operationHandlerCollectionFactory;

protected ProcessorsCollectionFactory $processorsCollectionFactory;

public function __construct(
SerializerService $serializerService,
ApiResourceRepository $apiResourceRepository,
SerializationContextBuilder $serializationContextBuilder,
DeserializationContextBuilder $deserializationContextBuilder,
EventDispatcherInterface $eventDispatcherInterface
EventDispatcherInterface $eventDispatcherInterface,
OperationHandlerCollectionFactory $operationHandlerCollectionFactory,
ProcessorsCollectionFactory $processorsCollectionFactory
) {
$this->serializerService = $serializerService;
$this->apiResourceRepository = $apiResourceRepository;
$this->serializationContextBuilder = $serializationContextBuilder;
$this->deserializationContextBuilder = $deserializationContextBuilder;
$this->eventDispatcher = $eventDispatcherInterface;
$this->operationHandlerCollectionFactory = $operationHandlerCollectionFactory;
$this->processorsCollectionFactory = $processorsCollectionFactory;

$this->init();
}

abstract protected function init(): void;

/**
* @throws RouteNotFoundException
* @throws \Exception
Expand Down Expand Up @@ -104,8 +116,7 @@ protected function processOperation(
);
}

/** @var OperationHandlerInterface $handler */
$handler = GeneralUtility::makeInstance(array_shift($handlers));
$handler = array_shift($handlers);
$result = $handler->handle($operation, $request, $route, $response);

$afterProcessOperationEvent = new AfterProcessOperationEvent(
Expand All @@ -126,42 +137,39 @@ protected function processOperation(
protected function getHandlersSupportingOperation(OperationInterface $operation, Request $request): array
{
return array_filter(
Configuration::getOperationHandlers(),
static function (string $operationHandlerClass) use ($operation, $request) {
if (!is_subclass_of($operationHandlerClass, OperationHandlerInterface::class, true)) {
$this->operationHandlerCollectionFactory->get(),
static function ($operationHandler) use ($operation, $request) {
if (!$operationHandler instanceof OperationHandlerInterface) {
throw new \RuntimeException(
sprintf(
'Operation handler `%s` needs to be an instance of `%s`',
$operationHandlerClass,
$operationHandler::class,
OperationHandlerInterface::class
),
1591018489732
);
}

return call_user_func($operationHandlerClass . '::supports', $operation, $request);
return $operationHandler::supports($operation, $request);
}
);
}

protected function callProcessors(Request $request, ResponseInterface $response): void
{
array_filter(
Configuration::getProcessors(),
static function (string $processorClass) use ($request, &$response) {
if (!is_subclass_of($processorClass, ProcessorInterface::class, true)) {
$this->processorsCollectionFactory->get(),
static function ($processor) use ($request, &$response) {
if (!$processor instanceof ProcessorInterface) {
throw new \RuntimeException(
sprintf(
'Process `%s` needs to be an instance of `%s`',
$processorClass,
$processor::class,
ProcessorInterface::class
),
1603705384
);
}

/** @var ProcessorInterface $processor */
$processor = GeneralUtility::makeInstance($processorClass);
$processor->process($request, $response);
}
);
Expand Down
Loading
Loading