michaelcozzolino/symfony-validation-enhancements-bundle
A Symfony bundle that enhances the Validator component with additional validation features and constraints. Integrates quickly into existing Symfony applications to simplify common validation scenarios and keep validation logic reusable and configuration-driven.
This is a Symfony bundle, not a native Laravel package — v1.0.0 is its first stable release, so treat it as "usable but young." In a Laravel codebase you'll consume the underlying constraints/validators rather than the bundle wiring.
composer require michaelcozzolino/symfony-validation-enhancements-bundlesrc/Validator/Constraints/ and src/Validator/ — EntityExistsValidator is the headline feature and the only piece with real day-to-day value for a Laravel dev.README.md and docs/ (added in PR #12) for the renamed rule names — v1.0.0 changed them, so any snippet you find from v0.2.0 or blog posts will be wrong.FormRequest with a declarative "does this entity exist / not exist" check.use MichaelCozzolino\Validation\Constraints\EntityExists;
// exists check (mirrors Laravel's `exists` rule, but backed by your Doctrine/entity layer)
new EntityExists(['entity' => User::class, 'field' => 'email']);
FormRequest wrapper. Keep the Symfony constraint out of your controllers and wrap it in a Laravel Rule object so your validation stays idiomatic:
class EntityExistsRule implements ValidationRule
{
public function __construct(
private string $entity,
private string $field,
private bool $mustNotExist = false,
) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$constraint = new EntityExists([
'entity' => $this->entity,
'field' => $this->field,
'mustNotExist' => $this->mustNotExist,
]);
$violations = app(ValidatorInterface::class)->validate($value, $constraint);
if (count($violations) > 0) {
$fail($violations[0]->getMessage());
}
}
}
Check the constraint's actual option key for the non-existence mode (PR #14) — it may be mustNotExist, notExists, or a separate constraint class; confirm against the renamed rules list before shipping.
Registration rule (Symfony only). If you're bridging into a Symfony app, register the bundle in config/bundles.php and make sure doctrine/orm (or whichever entity layer the validator queries) is available in the container — the validator resolves entities through a service, not static calls.
Error listener for API responses. The new validation error listener is the second real feature: it normalizes validation failures into a consistent payload. Use it when you want one JSON error shape across every endpoint, rather than per-controller ValidationException formatting. In Laravel you'd typically do this via App\Exceptions\Handler + ValidationException::errors(); adopt the listener only if you need the exact Symfony-shaped output (e.g., a shared API consumed by a Symfony frontend).
Reuse across requests. Cache constraint instances per entity/field pair — building a constraint per attribute per request is cheap, but the entity metadata lookup it triggers is not always memoized. A small EntityExistsRule::for(User::class, 'email') factory keeps call sites readable:
'email' => ['required', 'email', EntityExistsRule::for(User::class, 'email')],
Test with Pest. The package converted its own suite to Pest in v1.0.0, so its test files are the best documentation of intended behavior — including edge cases for the non-existence mode. Mirror those cases in your app tests.
ValidatorInterface and error-listener plumbing. In Laravel you're importing a Symfony component graph — expect symfony/validator and symfony/http-kernel transitively. Audit composer.lock for framework version conflicts (Laravel 10/11 pin different Symfony majors).unique. "The entity must not exist" checks a single field by value. It will not respect soft deletes, tenancy scopes, or global query scopes unless the validator's repository lookup does — verify against your SoftDeletes models, where a "non-existent" email may still be a deleted row that blocks re-registration.errors, back()->withErrors()). Scope it to API routes / Accept: application/json requests, or skip it in favor of Laravel's native ValidationException handling.EntityExists is a SELECT 1 (or worse, a hydrated fetch). On bulk imports or array validation with * wildcards this multiplies fast — batch pre-check with a single whereIn query and fall back to the rule for single-field validation.null and '' before combining it with nullable — a naive constraint will fire a query on empty input and report "not found" style errors instead of letting nullable short-circuit.CHANGELOG before minor bumps.How can I help you explore Laravel packages today?