Skip to content

Commit

Permalink
Switch annotations for controls/validations to php8 attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
dakorpar committed May 12, 2021
1 parent f8a6543 commit 28941c1
Show file tree
Hide file tree
Showing 18 changed files with 239 additions and 233 deletions.
25 changes: 25 additions & 0 deletions src/Attributes/ChoiceControlItems.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare (strict_types = 1);

namespace Wedo\Api\Attributes;

use Attribute;

#[Attribute]
class ChoiceControlItems
{

/** @var mixed[] */
public array $items;

public bool $useKeys;

/**
* @param mixed[] $items
*/
public function __construct(array $items, bool $useKeys = false)
{
$this->items = $items;
$this->useKeys = $useKeys;
}

}
22 changes: 22 additions & 0 deletions src/Attributes/ContainerType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare (strict_types = 1);

namespace Wedo\Api\Attributes;

use Attribute;

#[Attribute]
class ContainerType
{

/** @var class-string */
public string $value;

/**
* @param class-string $value
*/
public function __construct(string $value)
{
$this->value = $value;
}

}
34 changes: 34 additions & 0 deletions src/Attributes/Control.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php declare (strict_types = 1);

namespace Wedo\Api\Attributes;

use Attribute;

#[Attribute]
class Control
{

public const TEXT = 'Text';
public const PASSWORD = 'Password';
public const TEXT_AREA = 'TextArea';
public const EMAIL = 'Email';
public const INTEGER = 'Integer';
public const UPLOAD = 'Upload';
public const MULTI_UPLOAD = 'MultiUpload';
public const HIDDEN = 'Hidden';
public const CHECKBOX = 'Checkbox';
public const RADIO_LIST = 'RadioList';
public const CHECKBOX_LIST = 'CheckboxList';
public const SELECT = 'Select';
public const MULTI_SELECT = 'MultiSelect';
public const IMAGE = 'Image';
public const CONTAINER = 'Container';

public string $value;

public function __construct(string $value)
{
$this->value = $value;
}

}
18 changes: 18 additions & 0 deletions src/Attributes/RequiredRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare (strict_types = 1);

namespace Wedo\Api\Attributes;

use Attribute;

#[Attribute]
class RequiredRule
{

public bool $required;

public function __construct(bool $required = true)
{
$this->required = $required;
}

}
25 changes: 25 additions & 0 deletions src/Attributes/ValidationRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare (strict_types = 1);

namespace Wedo\Api\Attributes;

use Attribute;

#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_PROPERTY)]
class ValidationRule
{

/** @var callable|string */
public $validator;

public string|object|null $errorMessage;

public mixed $args;

public function __construct(callable|string $validator, string|object|null $errorMessage = null, mixed $args = null)
{
$this->validator = $validator;
$this->errorMessage = $errorMessage;
$this->args = $args;
}

}
79 changes: 51 additions & 28 deletions src/Helpers/FormBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,81 @@
use Nette\ArgumentOutOfRangeException;
use Nette\Forms\Container;
use Nette\Forms\Controls\BaseControl;
use Nette\Forms\Controls\ChoiceControl;
use Nette\NotSupportedException;
use ReflectionProperty;
use Throwable;
use Wedo\Api\Attributes\ChoiceControlItems;
use Wedo\Api\Attributes\ContainerType;
use Wedo\Api\Attributes\Control;
use Wedo\Api\Requests\BaseRequest;
use Wedo\Utilities\ClassNameHelper;

class FormBuilder
{

/**
* @param array<int|string, string[]> $properties
* @param ReflectionProperty[] $properties
* @param mixed[] $data
*/
public function createForm(array $properties, BaseRequest $request, Container $form, array $data): void
{
/**
* @var string $property
* @var string[][] $annotations
*/
foreach ($properties as $property => $annotations) {
$controlType = $this->getControlType($annotations);
foreach ($properties as $property) {
$controlType = $this->getControlType($property);
$controlAddCallback = [$form, 'add' . $controlType];

if ((!method_exists($form, $controlAddCallback[1]) && $controlType !== 'Date') || !is_callable($controlAddCallback)) {
throw new NotSupportedException('Control of type ' . $controlType . ' does not exist!');
try {
$control = $controlAddCallback($property->getName()); //@phpstan-ignore-line
} catch (Throwable $ex) {
throw new NotSupportedException('Cannot add control of type ' . $controlType, 0, $ex);
}

$control = call_user_func($controlAddCallback, $property);

if ($controlType === 'Container') {
if ($controlType === Control::CONTAINER) {
/** @phpstan-ignore-next-line */
$request->$property = [];

/** @phpstan-ignore-next-line */
if (empty($data[$property])) {
if (empty($data[$property->getName()])) {
continue;
}

$values = $data[$property];
$values = $data[$property->getName()];

$requestTypeAttributes = $property->getAttributes(ContainerType::class);

if (count($requestTypeAttributes) === 0) {
throw new NotSupportedException('ContainerType attribute not found for ' .
$property->getDeclaringClass()->getName() . '::' . $property->getName());
}

/** @var ContainerType $requestTypeAttribute */
$requestTypeAttribute = $requestTypeAttributes[0]->newInstance();

$reqType = rtrim($annotations['var'][0], '][');
$reqType = ClassNameHelper::extractFqnFromObjectUseStatements($request, $reqType);
$requestType = $requestTypeAttribute->value;

foreach ($values as $key => $value) {
/** @var Container $container */
$container = $control->addContainer($key);
$item = new $reqType();
$item = new $requestType();
$item->buildForm($value, $container, $item);
/** @phpstan-ignore-next-line */
$request->$property[] = $item;
$request->{$property->getName()}[] = $item;
}
}

unset($annotations['var'], $annotations['description'], $annotations['control']);

if ($control instanceof BaseControl) {
$request->setValidationRules($annotations, $control);
$request->setValidationRules($property, $control);
}

if ($control instanceof ChoiceControl) {
$itemsAttributes = $property->getAttributes(ChoiceControlItems::class);

if (count($itemsAttributes) === 0) {
throw new NotSupportedException('Choice control must have ChoiceControlItems attribute set!');
}

/** @var ChoiceControlItems $itemAttribute */
$itemAttribute = $itemsAttributes[0]->newInstance();
$control->setItems($itemAttribute->items, $itemAttribute->useKeys);
}
}
}
Expand All @@ -71,17 +91,20 @@ public function createEmptyForm(): Form
}

/**
* @param string[][] $annotations
* @throws ArgumentOutOfRangeException
* @throws NotSupportedException
*/
protected function getControlType(array $annotations): string
protected function getControlType(ReflectionProperty $property): string
{
if (!isset($annotations['control'])) {
throw new ArgumentOutOfRangeException('@control annotation not set on request!');
$controlAttributes = $property->getAttributes(Control::class);
if (count($controlAttributes) === 0) {
throw new ArgumentOutOfRangeException('#[Control] Attribute not set on ' .
$property->getDeclaringClass()->getName() . '::' . $property->getName());
}

return $annotations['control'][0];
/** @var Control $controlAttribute */
$controlAttribute = $controlAttributes[0]->newInstance();

return $controlAttribute->value;
}

}

0 comments on commit 28941c1

Please sign in to comment.