Skip to content

Commit

Permalink
[FEATURE] Extendable component data object
Browse files Browse the repository at this point in the history
  • Loading branch information
s2b committed Jun 8, 2023
1 parent 433e7e2 commit f1ef831
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 93 deletions.
89 changes: 89 additions & 0 deletions Classes/Domain/Model/Component.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace SMS\FluidComponents\Domain\Model;

class Component
{
protected string $namespace;
protected string $package;
protected string $name;
protected string $file;

protected array $data = [];
protected ?string $class = null;
protected ?string $prefix = null;

public function __construct(
string $fullNamespace,
string $package,
string $name,
string $file
) {
$this->namespace = $fullNamespace;
$this->package = $package;
$this->name = $name;
$this->file = $file;
}

public function getNamespace(): string
{
return $this->namespace;
}

public function getPackage(): string
{
return $this->package;
}

public function getName(): string
{
return $this->name;
}

public function getPath(): string
{
return dirname($this->file);
}

public function getFile(): string
{
return $this->file;
}

public function getData(): array
{
return $this->data;
}

public function setData(array $data): void
{
$this->data = $data;
}

public function getClass(): ?string
{
return $this->class;
}

public function setClass(string $class): void
{
$this->class = $class;
}

public function getPrefix(): ?string
{
return $this->prefix;
}

public function setPrefix(string $prefix): void
{
$this->prefix = $prefix;
}

public function __toString(): string
{
return $this->file;
}
}
117 changes: 28 additions & 89 deletions Classes/Fluid/ViewHelper/ComponentRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

use Psr\Container\ContainerInterface;
use SMS\FluidComponents\Domain\Model\RequiredSlotPlaceholder;
use SMS\FluidComponents\Domain\Model\Component;
use SMS\FluidComponents\Domain\Model\Slot;
use SMS\FluidComponents\Interfaces\ComponentAware;
use SMS\FluidComponents\Interfaces\EscapedParameter;
use SMS\FluidComponents\Interfaces\RenderingContextAware;
use SMS\FluidComponents\Utility\ComponentArgumentConverter;
use SMS\FluidComponents\Service\ComponentDataLoader;
use SMS\FluidComponents\Utility\ComponentLoader;
use SMS\FluidComponents\Utility\ComponentPrefixer\ComponentPrefixerInterface;
use SMS\FluidComponents\Utility\ComponentPrefixer\GenericComponentPrefixer;
use SMS\FluidComponents\Utility\ComponentSettings;
use SMS\FluidComponents\ViewHelpers\ComponentViewHelper;
use SMS\FluidComponents\ViewHelpers\ContentViewHelper;
Expand Down Expand Up @@ -49,6 +49,13 @@ class ComponentRenderer extends AbstractViewHelper
*/
protected $componentNamespace;

/**
* Object containing information about the current component
*
* @var Component
*/
protected Component $component;

/**
* Cache for component template instance used for rendering
*
Expand All @@ -66,13 +73,6 @@ class ComponentRenderer extends AbstractViewHelper
*/
protected static $componentArgumentDefinitionCache = [];

/**
* Cache of component prefixer objects
*
* @var array
*/
protected static $componentPrefixerCache = [];

/**
* Components are HTML markup which should not be escaped
*
Expand All @@ -97,6 +97,11 @@ class ComponentRenderer extends AbstractViewHelper
*/
protected ComponentSettings $componentSettings;

/**
* @var ComponentDataLoader
*/
protected ComponentDataLoader $componentDataLoader;

/**
* @var ComponentArgumentConverter
*/
Expand All @@ -116,11 +121,13 @@ class ComponentRenderer extends AbstractViewHelper
public function __construct(
ComponentLoader $componentLoader,
ComponentSettings $componentSettings,
ComponentDataLoader $componentDataLoader,
ComponentArgumentConverter $componentArgumentConverter,
ContainerInterface $container
) {
$this->componentLoader = $componentLoader;
$this->componentSettings = $componentSettings;
$this->componentDataLoader = $componentDataLoader;
$this->componentArgumentConverter = $componentArgumentConverter;
$this->container = $container;
}
Expand All @@ -134,39 +141,20 @@ public function __construct(
public function setComponentNamespace($componentNamespace)
{
$this->componentNamespace = $componentNamespace;
$this->component = $this->componentLoader->findComponent($this->componentNamespace);
return $this;
}

/**
* Returns the namespace of the component the viewhelper renders
*
* @return void
* @return string
*/
public function getComponentNamespace()
{
return $this->componentNamespace;
}

/**
* Returns the component prefix
*
* @return string
*/
public function getComponentClass()
{
return $this->getComponentPrefixer()->prefix($this->componentNamespace);
}

/**
* Returns the component prefix
*
* @return string
*/
public function getComponentPrefix()
{
return $this->getComponentClass() . $this->getComponentPrefixer()->getSeparator();
}

/**
* Renders the component the viewhelper is responsible for
* TODO this can probably be improved by using renderComponent() directly
Expand All @@ -193,12 +181,11 @@ public function render()
}
$variableContainer = $renderingContext->getVariableProvider();

// Load additional data for component
$this->componentDataLoader->loadData($this->component);

// Provide information about component to renderer
$variableContainer->add('component', [
'namespace' => $this->componentNamespace,
'class' => $this->getComponentClass(),
'prefix' => $this->getComponentPrefix(),
]);
$variableContainer->add('component', $this->component);
$variableContainer->add('settings', $this->componentSettings);

// Provide supplied arguments from component call to renderer
Expand Down Expand Up @@ -228,12 +215,10 @@ public function render()

// Initialize component rendering template
if (!isset($this->parsedTemplate)) {
$componentFile = $this->componentLoader->findComponent($this->componentNamespace);

$this->parsedTemplate = $renderingContext->getTemplateParser()->getOrParseAndStoreTemplate(
$this->getTemplateIdentifier($componentFile),
function () use ($componentFile) {
return file_get_contents($componentFile);
$this->getTemplateIdentifier($this->component->getFile()),
function () {
return file_get_contents($this->component->getFile());
}
);
}
Expand Down Expand Up @@ -459,12 +444,10 @@ protected function initializeComponentParams()
{
$renderingContext = $this->getRenderingContext();

$componentFile = $this->componentLoader->findComponent($this->componentNamespace);

// Parse component template without using the cache
$parsedTemplate = $renderingContext->getTemplateParser()->parse(
file_get_contents($componentFile),
$this->getTemplateIdentifier($componentFile)
file_get_contents($this->component->getFile()),
$this->getTemplateIdentifier($this->component->getFile())
);

// Extract all component viewhelpers
Expand All @@ -476,7 +459,7 @@ protected function initializeComponentParams()
if (count($componentNodes) > 1) {
throw new Exception(sprintf(
'Only one component per file allowed in: %s',
$componentFile
$this->component->getFile()
), 1527779393);
}

Expand Down Expand Up @@ -607,50 +590,6 @@ protected function getTemplateIdentifier(string $pathAndFilename, string $prefix
);
}

/**
* Returns the prefixer object responsible for the current component namespaces
*
* @return ComponentPrefixerInterface
*/
protected function getComponentPrefixer()
{
if (!isset(self::$componentPrefixerCache[$this->componentNamespace])) {
if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer']) &&
is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer'])
) {
arsort($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer']);
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer'] as $namespace => $prefixer) {
$namespace = ltrim($namespace, '\\');
if (strpos($this->componentNamespace, $namespace) === 0) {
$componentPrefixerClass = $prefixer;
break;
}
}
}

if (empty($componentPrefixerClass)) {
$componentPrefixerClass = GenericComponentPrefixer::class;
}

if ($this->container->has($componentPrefixerClass)) {
$componentPrefixer = $this->container->get($componentPrefixerClass);
} else {
$componentPrefixer = GeneralUtility::makeInstance($componentPrefixerClass);
}

if (!($componentPrefixer instanceof ComponentPrefixerInterface)) {
throw new Exception(sprintf(
'Invalid component prefixer: %s',
$componentPrefixerClass
), 1530608357);
}

self::$componentPrefixerCache[$this->componentNamespace] = $componentPrefixer;
}

return self::$componentPrefixerCache[$this->componentNamespace];
}

/**
* @return RenderingContext
*/
Expand Down
10 changes: 10 additions & 0 deletions Classes/Interfaces/ComponentDataProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace SMS\FluidComponents\Interfaces;

use SMS\FluidComponents\Domain\Model\Component;

interface ComponentDataProvider
{
public function applyData(Component $component);
}
36 changes: 36 additions & 0 deletions Classes/Service/ComponentDataLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace SMS\FluidComponents\Service;

use SMS\FluidComponents\Domain\Model\Component;
use TYPO3\CMS\Core\SingletonInterface;

class ComponentDataLoader implements SingletonInterface
{
private array $loadedComponents = [];
private iterable $dataProviders;

public function __construct(iterable $dataProviders)
{
$this->dataProviders = $dataProviders;
}

public function reset(): void
{
$this->loadedComponents = [];
}

public function loadData(Component $component): void
{
if (isset($this->loadedComponents[$component->getNamespace()])) {
return;
}

foreach ($this->dataProviders as $dataProvider) {
$dataProvider->applyData($component);
}
$this->loadedComponents[$component->getNamespace()] = true;
}
}
25 changes: 25 additions & 0 deletions Classes/Service/DataProvider/ComponentPrefixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace SMS\FluidComponents\Service\DataProvider;

use SMS\FluidComponents\Domain\Model\Component;
use SMS\FluidComponents\Interfaces\ComponentDataProvider;

class ComponentPrefixer implements ComponentDataProvider
{
public function applyData(Component $component): void
{
if ($component->getPrefix() !== null) {
return;
}

$vendorName = substr($component->getPackage(), 0, strpos($component->getPackage(), '\\'));
$componentName = str_replace('\\', '', $component->getName());
$prefix = strtolower($vendorName) . $componentName;

$component->setClass($prefix);
$component->setPrefix($prefix . '_');
}
}
Loading

0 comments on commit f1ef831

Please sign in to comment.