Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Fr Validator Symfony Laravel Package

petroslabs/fr-validator-symfony

Symfony Validator constraints for French data: validate SIRET, SIREN, VAT numbers, postal codes, and phone formats with drop-in constraint classes. Integrates with Symfony's validator service and works in Laravel apps built on the Symfony Validator component.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin adapter layer: it turns each of the 9 checks in petroslabs/fr-validator (Nir, Rib, Siren, Siret, Iban, Bic, Vat, Naf, Phone) into a Symfony Validator constraint, and surfaces the core library's typed failure reason as both a message parameter ({{ reason }}-style) and the violation code. It is not a Laravel-native validation rule — Laravel's Illuminate\Validation stack is separate from Symfony's Validator. Plan your first use around that reality.

Minimal steps:

  1. composer require petroslabs/fr-validator-symfony-validator (this pulls in petroslabs/fr-validator and requires symfony/validator). Confirm the exact package name against the repo's composer.json before adding it to require in a project.
  2. Look first at the constraint class you need — start with Siret or Iban, the two highest-traffic checks in French business apps — and at how the constraint exposes the failure reason (message parameter) and the violation code.
  3. First use case: validate a DTO before persisting, using Symfony's ValidatorBuilder directly:
use Symfony\Component\Validator\Validation;
use Petroslabs\FrValidator\Symfony\Constraint\Siret;

$validator = Validation::createValidator();

$violations = $validator->validate($siret, [new Siret()]);

foreach ($violations as $violation) {
    dump($violation->getCode());      // typed failure reason from fr-validator
    dump($violation->getParameters()); // includes the reason as a message parameter
}

If you only ever live inside Laravel controllers/FormRequests, jump straight to the "bridging" pattern in the next section rather than building a parallel validator service.

Implementation Patterns

Attribute-driven DTO validation. Apply constraints as attributes on a request DTO and validate the DTO at the edge of your bounded context. This is the pattern the package is designed for:

class CompanyDto
{
    #[Siret]
    public string $siret;

    #[Iban]
    public string $iban;

    #[Bic]
    public string $bic;
}

Bridge into Laravel's validator via a custom Rule. Because Laravel does not consume Symfony constraints natively, wrap them. This keeps the failure code and message parameters available to your validation response:

class SymfonyConstraintRule implements \Illuminate\Contracts\Validation\ValidationRule
{
    public function __construct(private object $constraint) {}

    public function validate(string $attribute, mixed $value, \Closure $fail): void
    {
        foreach (Validation::createValidator()->validate($value, [$this->constraint]) as $v) {
            $fail($v->getMessage())->translate(); // or map $v->getCode() to your own message
        }
    }
}

// FormRequest
public function rules(): array
{
    return [
        'siret' => ['required', new SymfonyConstraintRule(new Siret())],
        'iban'  => ['required', new SymfonyConstraintRule(new Iban())],
        'bic'   => ['required', new SymfonyConstraintRule(new Bic())],
    ];
}

Use violation codes, not messages, for API error mapping. The typed failure reason exposed as code is the stable machine-readable contract. Map it in a single place (e.g., an enum-backed ReasonCode => HTTP/error-key map) so user-facing text and API error keys stay decoupled from the core library's enum:

$violations->get(0)->getCode(); // e.g. a fr-validator reason case -> your error key

Localize via message parameters. The reason is passed as a message parameter, so customise the constraint's message and translate the injected reason. Prefer overriding messages per-constraint at construction (new Siret(message: '...')) or in a translation catalogue rather than string-matching the default message.

Group checks by field, not field type. Siren vs Siret differ by length/format; a form field may accept either depending on the entity (company vs establishment). Compose constraints deliberately and test both branches rather than assuming one constraint covers the field.

Cross-field checks for Iban/Bic/Vat. These are inherently relational (IBAN↔BIC, VAT↔country). Run the single-field constraints first, then a second pass for cross-field consistency to produce one clear error instead of several.

Testing. Symfony's Validation::createValidator() makes these trivially unit-testable without booting Laravel — write value-object/DTO tests here instead of HTTP feature tests.

Gotchas and Tips

  • Two validation stacks. This is the biggest trap: a constraint being "for Symfony Validator" does not make it a Illuminate\Validation\Rule. Nothing auto-registers with Laravel. Plan for the bridge rule (above) or route these checks through a Symfony validator service intentionally.
  • 0.x semver. v0.1.0 is a first tag. Pin the version and read CHANGELOG.md on every bump — constraint namespaces, constructor signatures, and the reason-as-code mapping are all fair game to change before 1.0.
  • Codes are typed reasons, not stable strings. The violation code comes from the core library's failure-reason enum/cases. Great for mapping, but if fr-validator renames a case, your map breaks. Keep the mapping in one adapter class and cover it with tests.
  • Messages need translation. Default constraint messages are generic; the interesting information is the injected reason parameter. If you render getMessage() raw you'll get a generic string — translate/format with the parameters, or override message at construction.
  • Normalisation is upstream. Any trimming/spacing/format normalisation is the core library's job, not the constraint's. Validate the raw submitted value; don't pre-clean silently in the bridge, or you'll mask genuine format errors.
  • Phone/VAT region assumptions. Phone and Vat validity are region-dependent in the core library; if you support non-FR input, verify how the underlying check treats +33, foreign IBANs, and EU VAT formats before relying on the constraint as a hard reject.
  • Error count explosion. A failing IBAN plus a failing BIC yields two violations. For UX, aggregate per-field and surface one message; use violation codes to decide precedence.
  • Extension points to lean on: custom message per constraint instance, the reason parameter for templating, and wrapping the constraint in your own Laravel Rule so you control message/attribute naming (:attribute) and error keys centrally.
  • Debugging: dump $violation->getParameters() and $violation->getCode() together — the parameters tell you what failed (reason), the code tells you why in a form you can branch on. If a value passes unexpectedly, reproduce it against petroslabs/fr-validator directly to isolate adapter vs core behaviour.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin