Skip to content

Commit

Permalink
Merge pull request #10 from Tivix/feature/rules-messages
Browse files Browse the repository at this point in the history
Add possibility to add rule message
  • Loading branch information
krzysztofchadynka committed Jan 9, 2023
2 parents 811b6a5 + b8a0b57 commit e4a1a98
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 25 deletions.
6 changes: 4 additions & 2 deletions src/Features/Data/Attributes/Validation/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
namespace Kellton\Tools\Features\Data\Attributes\Validation;

use Attribute;
use Illuminate\Contracts\Validation\Rule as ValidationRule;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
final class Rule extends ValidationAttribute
{
/**
* Rule constructor.
*
* @param string $rule
* @param string|ValidationRule $rule
* @param string|null $message
*/
public function __construct(public readonly string $rule)
public function __construct(public readonly string|ValidationRule $rule, public readonly ?string $message = null)
{
}
}
2 changes: 1 addition & 1 deletion src/Features/Data/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ public static function getValidationRules(): array
/** @var RuleService $service */
$service = app(RuleService::class);

return $service->getByClass(static::class)->toArray();
return $service->getByClass(static::class)->transform(fn ($value) => $value->get('rules'))->toArray();
}
}
36 changes: 33 additions & 3 deletions src/Features/Data/Services/DataService.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function create(string $class, mixed ...$payload): Data
$payload = array_merge($payload->route()->parameters, $payload->all());
}

if($payload === null) {
if ($payload === null) {
$payload = [];
}

Expand Down Expand Up @@ -91,15 +91,45 @@ public function create(string $class, mixed ...$payload): Data
*/
private function validate(Definition $definition, Collection $payload): Collection
{
$rules = $this->ruleService->get($definition);
$definitions = $this->ruleService->get($definition);

$validator = Validator::make($payload->toArray(), $rules->toArray());
$rules = $definitions->map(fn (Collection $value) => $value->get('rules'));
$messages = $definitions->map(fn (Collection $value) => $value->get('messages'));

$validator = Validator::make(
$payload->toArray(),
$rules->toArray(),
$this->parseValidationErrorMessages($messages)
);

$validator->validate();

return collect_all($validator->validated());
}

/**
* Parse validation error messages to array.
*
* @param Collection $validationErrorMessages
*
* @return array
*/
private function parseValidationErrorMessages(Collection $validationErrorMessages): array
{
$validationMessages = [];

$validationErrorMessages->each(function (Collection $errorMessages, $fieldName) use (&$validationMessages) {
if ($errorMessages->isNotEmpty()) {
$errorMessages->each(function ($errorMessage, $ruleName) use ($fieldName, &$validationMessages) {
$keyName = $fieldName . '.' . explode(':', $ruleName)[0];
$validationMessages[$keyName] = $errorMessage;
});
}
});

return $validationMessages;
}

/**
* Resolve map properties.
*
Expand Down
33 changes: 22 additions & 11 deletions src/Features/Data/Services/RuleService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use BackedEnum;
use Carbon\Carbon;
use Kellton\Tools\Features\Data\Attributes\Validation\Rule;
use Kellton\Tools\Features\Data\Attributes\Validation\ValidationAttribute;
use Kellton\Tools\Features\Data\Definition;
use Kellton\Tools\Features\Data\Exceptions\MissingConstructor;
Expand Down Expand Up @@ -39,7 +40,9 @@ public function __construct(public readonly DefinitionService $definitionService
*/
public function get(Definition $definition): Collection
{
return $definition->properties->mapWithKeys(fn (Property $property) => $this->resolve($property)->all());
$data = $definition->properties->mapWithKeys(fn (Property $property) => $this->resolve($property)->all());

return $data;
}

/**
Expand Down Expand Up @@ -77,7 +80,7 @@ private function resolve(Property $property): Collection
return $this->getNestedRules($property, $propertyName);
}

return collect([$propertyName => $this->getRulesForProperty($property)]);
return collect([$propertyName => $this->getDataForProperty($property)]);
}

/**
Expand All @@ -87,9 +90,14 @@ private function resolve(Property $property): Collection
*
* @return Collection
*/
protected function getRulesForProperty(Property $property): Collection
protected function getDataForProperty(Property $property): Collection
{
$rules = collect();
$data = collect([
'rules' => collect(),
'messages' => collect(),
]);

$rules = $data->get('rules');

if ($property->isNullable) {
$rules->add('nullable');
Expand All @@ -108,9 +116,9 @@ protected function getRulesForProperty(Property $property): Collection
}

$this->resolveTypes($property, $rules);
$this->resolveAttributeRules($property, $rules);
$this->resolveAttributeRules($property, $data);

return $rules;
return $data;
}

/**
Expand All @@ -132,7 +140,7 @@ protected function getNestedRules(Property $property, string $propertyName): Col
default => throw new TypeError()
};

$parentRules = $this->getRulesForProperty($property);
$parentRules = $this->getDataForProperty($property);

$definition = $this->definitionService->get($property->dataClass);
$rules = $this->get($definition);
Expand Down Expand Up @@ -179,17 +187,20 @@ private function resolveTypes(Property $property, Collection $rules): void
* Resolve rules for the attributes.
*
* @param Property $property
* @param Collection $rules
* @param Collection $data
*
* @return void
*/
private function resolveAttributeRules(Property $property, Collection $rules): void
private function resolveAttributeRules(Property $property, Collection $data): void
{
$property
->attributes
->filter(fn (object $attribute) => is_subclass_of($attribute, ValidationAttribute::class))
->each(function (ValidationAttribute $rule) use ($rules) {
$rules->add($rule->rule);
->each(function (ValidationAttribute $rule) use ($data) {
$data->get('rules')->add($rule->rule);
if ($rule instanceof Rule && $rule->message !== null) {
$data->get('messages')->put($rule->rule, $rule->message);
}
});
}
}
17 changes: 17 additions & 0 deletions tests/Data/TestData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Kellton\Tools\Tests\Data;

use Kellton\Tools\Features\Data\Attributes\Validation\Rule;
use Kellton\Tools\Features\Data\Data;

readonly class TestData extends Data
{
public function __construct(
public string $firstName,
public string $lastName,
#[Rule('email', message: 'Wrong email address format!')]
public string $email
) {
}
}
22 changes: 14 additions & 8 deletions tests/Feature/DataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Kellton\Tools\Tests\Feature;

use Illuminate\Validation\ValidationException;
use Kellton\Tools\Features\Data\Data;
use Kellton\Tools\Features\Data\Exceptions\MissingConstructor;
use Kellton\Tools\Tests\Data\IndexData;
use Kellton\Tools\Tests\Data\TestData;
use Kellton\Tools\Tests\TestCase;
use ReflectionException;

Expand All @@ -23,7 +25,7 @@ class DataTest extends TestCase
*/
public function testCreateShouldSucceed(): void
{
$data = new TestData('John', 'Doe');
$data = new TestData('John', 'Doe', 'john.doe@kellton.com');
$this->assertInstanceOf(Data::class, $data);

$validationRules = $data::getValidationRules();
Expand All @@ -47,14 +49,18 @@ public function testFiltersDataShouldSucceed(): void
$this->assertIsArray($validationRules);
$this->assertNotEmpty($validationRules);
}
}

/**
* Class TestData is used for testing readonly Data class.
*/
readonly class TestData extends Data
{
public function __construct(public string $firstName, public string $lastName)
public function testRuleMessageShouldSucceed(): void
{
try {
TestData::create([
'firstName' => 'John',
'lastName' => 'Doe',
'email' => 'john',
]);
} catch (ValidationException $e) {
$message = data_get($e->errors(), 'email.0');
$this->assertSame('Wrong email address format!', $message);
}
}
}

0 comments on commit e4a1a98

Please sign in to comment.