-
-
Notifications
You must be signed in to change notification settings - Fork 383
Closed
Description
I want to validate all data passed from twig into my php component. Right now I can do such thing only by creating a setter for each property. Eventually use mount
method and explicitly define there each property as an argument.
What I need is more generic method to validate such data. I was thinking about 2 solutions.
- Built-in method like
mount
namedconfigureProperties
- A callback to some method inside my component.
As I prefer the second solution I wrote some code to show you the case using the first one (was easier to me).
//ComponentFactory.php
public function create(string $name, array $data = []): object
{
$component = $this->getComponent($name);
$this->mount($component, $data);
$this->configureProperties($component, $data); // <- add this line
foreach ($data as $property => $value) {
if (!$this->propertyAccessor->isWritable($component, $property)) {
throw new \LogicException(sprintf('Unable to write "%s" to component "%s". Make sure this is a writable property or create a mount() with a $%s argument.', $property, \get_class($component), $property));
}
$this->propertyAccessor->setValue($component, $property, $value);
}
return $component;
}
// add these methods
private function configureProperties(object $component, array &$data): void
{
try {
$method = (new \ReflectionClass($component))->getMethod('configureProperties');
} catch (\ReflectionException $e) {
// no hydrate method
return;
}
$data = array_merge($data, $this->getComponentPropertiesDefaultValues($component));
$optionsResolver = new OptionsResolver();
$component->configureProperties($optionsResolver);
$data = $optionsResolver->resolve($data);
}
private function getComponentPropertiesDefaultValues(object $component): array
{
$reflection = new \ReflectionClass($component);
$propertiesReflections = array_filter(
$reflection->getProperties(\ReflectionProperty::IS_PUBLIC),
static fn($property): bool => !$property->isStatic() && $property->getDefaultValue() !== null,
);
$properties = [];
array_map(static function ($property) use (&$properties) {
$properties[$property->name] = $property->getDefaultValue();
}, $propertiesReflections);
return $properties;
}
// Components/CustomComponent.php
final class CustomComponent
{
public string $name;
public string $size = 'm';
public string $align;
public function configureProperties(OptionsResolver $optionsResolver)
{
$optionsResolver
->setDefined(['size'])
->setDefaults(['align' => 'center'])
->setAllowedValues('size', ['xs', 's', 'm', 'l', 'xl',])
->setAllowedValues('align', ['left', 'center', 'right'])
->setRequired(['name']);
}
#components/custom_component.html.twig
<div class="size-{{ this.size }} align-{{ this.align }}">
<h1>{{ this.name }}</h1>
<p>Size: {{ this.size }}</p>
<p>Align: {{ this.align }}</p>
</div>
{{ component('custom_component', {name: 'some name'}) }}
configureProperties
method in ComponentFactory
is responsible for setting default values defined in component's class properties into $data
variable. Then it tries to resolve provided options using my custom components rules and allows TwigComponent to set validated data.
Metadata
Metadata
Assignees
Labels
No labels