From api-platform
Creates custom validation constraints for API Platform resources when built-in Symfony constraints are insufficient. Handles rate limits, uniqueness, cross-field, and conditional validation on write operations.
How this skill is triggered — by the user, by Claude, or both
Slash command
/api-platform:custom-validatorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Create custom validators when built-in Symfony constraints are insufficient.
Create custom validators when built-in Symfony constraints are insufficient.
<?php
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class IsValidAccountLimit extends Constraint
{
public string $message = 'Account limit reached for your current plan.';
public int $limit = 50;
}
Convention: {ConstraintName}Validator in the same namespace.
<?php
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class IsValidAccountLimitValidator extends ConstraintValidator
{
public function __construct(
private readonly UserHelper $userHelper,
private readonly AccountRepository $accountRepository,
) {}
public function validate(mixed $value, Constraint $constraint): void
{
if (!$constraint instanceof IsValidAccountLimit) {
throw new \InvalidArgumentException('Unexpected constraint type');
}
if (null === $value || '' === $value) {
return;
}
$user = $this->userHelper->getUser();
$count = $this->accountRepository->countByUser($user->getId());
if ($count >= $constraint->limit) {
$this->context->buildViolation($constraint->message)
->addViolation();
}
}
}
use App\Validator\IsValidAccountLimit;
use Symfony\Component\Validator\Constraints as Assert;
class Account
{
#[Assert\NotBlank(groups: ['account:write'])]
#[Assert\Email(groups: ['account:write'])]
#[IsValidAccountLimit(groups: ['account:write'])]
public ?string $address;
}
For constraints that validate across multiple fields:
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
class MongoDBUnique extends Constraint
{
public array $fields = [];
public string $message = 'This value is already used.';
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}
Usage:
#[MongoDBUnique(fields: ['address'], groups: ['account:write', 'account:patch'])]
class Account {}
Target a constraint to specific operations by passing groups: (as shown above)
and wiring those groups into each operation's validationContext — see
operations for the full per-operation setup.
For quick one-off validation on DTOs:
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class SendMessage
{
public ?string $text = null;
public ?string $html = null;
#[Assert\Callback]
public function validateContent(ExecutionContextInterface $context): void
{
if (null === $this->text && null === $this->html) {
$context->buildViolation('Either text or html content is required.')
->atPath('text')
->addViolation();
}
}
}
Laravel does not use Symfony Constraints/ConstraintValidator. Validation is
declared with Laravel validation rules via the
rules option on #[ApiResource] or per-operation (rules can be an array, a closure,
or a FormRequest class-string). The ValidateProvider runs them before the
processor and emits the same 422 ConstraintViolationList shape.
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
#[ApiResource(rules: ['title' => 'required|min:2'])]
#[Post(rules: ['isbn' => ['required', 'string']])] // per-operation override
class Book extends Model {}
For business rules beyond built-in rules, write a Laravel custom rule (a class
implementing Illuminate\Contracts\Validation\ValidationRule, scaffold with
php artisan make:rule) or a closure rule, and reference it in rules. A
FormRequest class-string is also accepted — its authorize()/rules() run, and an
AuthorizationException maps to 403, ValidationException to 422. Validation groups
and Symfony validationContext do not apply on Laravel; scope rules per operation
instead. Partial PATCH can relax required to sometimes via
partial_patch_validation in config/api-platform.php.
npx claudepluginhub api-platform/skillset --plugin api-platformConfigures API Platform operations with security expressions, validation groups, parameter validation, deprecation headers, and nested PATCH. Use for endpoint access control, validation, and debugging merge-patch.
Provides Laravel validation patterns using Form Requests, custom rules, conditional validation, and input sanitization. Useful for form handling and data validation in Laravel apps.
Creates Symfony forms with custom Form Types, validation constraints, HTTP 422 response handling, and multi-step form flows.