Skip to content

Commit

Permalink
Merge pull request #63 from sitegeist/feature/collectionConverter
Browse files Browse the repository at this point in the history
[FEATURE] Convert arguments specified as collection "[]"
  • Loading branch information
s2b authored Jul 16, 2020
2 parents 551fcf2 + 1943571 commit fcb98a2
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Classes/Fluid/ViewHelper/ComponentRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ protected function initializeComponentParams()
);

// Register argument definitions from parameter viewhelpers
$componentArgumentConverter = $this->getComponentArgumentConverter();
foreach ($paramNodes as $paramNode) {
$param = [];
foreach ($paramNode->getArguments() as $argumentName => $argumentNode) {
Expand All @@ -392,9 +393,8 @@ protected function initializeComponentParams()
}

// Resolve type aliases
$param['type'] = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases'][$param['type']] ?? $param['type'];
$param['type'] = $componentArgumentConverter->resolveTypeAlias($param['type']);

$componentArgumentConverter = $this->getComponentArgumentConverter();
// Enforce boolean node, see implementation in ViewHelperNode::rewriteBooleanNodesInArgumentsObjectTree()
if ($param['type'] === 'boolean' || $param['type'] === 'bool') {
$param['default'] = BooleanNode::convertToBoolean($param['default'], $renderingContext);
Expand Down
102 changes: 102 additions & 0 deletions Classes/Utility/ComponentArgumentConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,30 @@ class ComponentArgumentConverter implements \TYPO3\CMS\Core\SingletonInterface
],
];

/**
* Registered argument type aliases
* [alias => full php class name]
*
* @var array
*/
protected $typeAliases = [];

/**
* Runtime cache to speed up conversion checks
*
* @var array
*/
protected $conversionCache = [];

public function __construct()
{
if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases'])
&& is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases'])
) {
$this->typeAliases =& $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases'];
}
}

/**
* Adds an interface to specify argument type conversion to list
*
Expand All @@ -98,6 +115,47 @@ public function removeConversionInterface(string $fromType): self
return $this;
}

/**
* Adds an alias for an argument type
*
* @param string $alias
* @param string $type
* @return self
*/
public function addTypeAlias(string $alias, string $type): self
{
$this->typeAliases[$alias] = $type;
return $this;
}

/**
* Removes an alias for an argument type
*
* @param string $alias
* @return self
*/
public function removeTypeAlias(string $alias): self
{
unset($this->typeAliases[$alias]);
return $this;
}

/**
* Replaces potential argument type alias with real php class name
*
* @param string $type e. g. MyAlias
* @return string e. g. Vendor\MyExtension\MyRealClass
*/
public function resolveTypeAlias(string $type): string
{
if ($this->isCollectionType($type)) {
$subtype = $this->extractCollectionItemType($type);
return $this->resolveTypeAlias($subtype) . '[]';
} else {
return $this->typeAliases[$type] ?? $type;
}
}

/**
* Checks if a given variable type can be converted to another
* data type by using alternative constructors in $this->conversionInterfaces
Expand Down Expand Up @@ -128,6 +186,8 @@ public function canTypeBeConvertedToType(string $givenType, string $toType): boo
$toType,
$this->conversionInterfaces[$givenType][0]
);
} elseif ($this->isCollectionType($toType) && $this->isAccessibleArray($givenType)) {
$canBeConverted = true;
}

// Add to runtime cache
Expand All @@ -153,8 +213,50 @@ public function convertValueToType($value, string $toType)
return $value;
}

// Attempt to convert a collection of objects
if ($this->isCollectionType($toType)) {
$subtype = $this->extractCollectionItemType($toType);
foreach ($value as &$item) {
$item = $this->convertValueToType($item, $subtype);
}
return $value;
}

// Call alternative constructor provided by interface
$constructor = $this->conversionInterfaces[$givenType][1];
return $toType::$constructor($value);
}

/**
* Checks if the provided type describes a collection of values
*
* @param string $type
* @return boolean
*/
protected function isCollectionType(string $type): bool
{
return substr($type, -2) === '[]';
}

/**
* Extracts the type of individual items from a collection type
*
* @param string $type e. g. Vendor\MyExtension\MyClass[]
* @return string e. g. Vendor\MyExtension\MyClass
*/
protected function extractCollectionItemType(string $type): string
{
return substr($type, 0, -2);
}

/**
* Checks if the given type is behaving like an array
*
* @param string $typeOrClassName
* @return boolean
*/
protected function isAccessibleArray(string $typeOrClassName): bool
{
return $typeOrClassName === 'array' || is_subclass_of($typeOrClassName, \ArrayAccess::class);
}
}
4 changes: 2 additions & 2 deletions ext_emconf.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
$EM_CONF[$_EXTKEY] = [
$EM_CONF['fluid_components'] = [
'title' => 'Fluid Components',
'description' => 'Encapsulated frontend components with Fluid\'s ViewHelper syntax',
'category' => 'fe',
Expand All @@ -9,7 +9,7 @@
'state' => 'stable',
'uploadfolder' => false,
'clearCacheOnLoad' => false,
'version' => '2.1.0',
'version' => '2.2.0',
'constraints' => [
'depends' => [
'typo3' => '9.5.0-10.9.99',
Expand Down

0 comments on commit fcb98a2

Please sign in to comment.