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

Iso Codes Validator Laravel Package

sllh/iso-codes-validator

Adds Laravel validation rules for ISO standards, letting you verify country, language, currency, and other ISO codes in forms and APIs. Integrates seamlessly with existing validators to keep input standards-compliant and clean, with support for common ISO formats.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require sllh/iso-codes-validator

This is a thin Symfony Validator wrapper around ronanguilloux/iso-codes. There is no bundle and no service wiring — each ISO check is a plain Constraint subclass, so anywhere the Symfony validator runs (full-stack app, console command, standalone ValidatorBuilder) can use it immediately once the package is on the autoloader.

Where to look first: the constraints directory in the vendor tree —

vendor/sllh/iso-codes-validator/src/Constraints/

That folder is the API surface. Every Foo constraint has a matching FooValidator that delegates to the underlying IsoCodes\... class, and the constructor signature tells you which options are required (e.g. Ssn needs a country/format argument). Read the constraint you intend to use before wiring it into a form — that is faster than any documentation for this package.

First use case — validating an IBAN on an account form. Put the constraint on the DTO/entity rather than the form so API payloads, forms and imports all share the same rule:

use Sllh\IsoCodesValidator\Constraints\Iban;
use Symfony\Component\Validator\Constraints as Assert;

class BankAccount
{
    #[Assert\NotBlank]
    #[Iban]
    public ?string $iban = null;
}

Then validate normally:

$violations = $validator->validate($account);
if (count($violations) > 0) {
    // $violations[0]->getMessage() -> "This value is not a valid IBAN."
}

Remember what this buys you: format, country prefix and checksum only. It tells you the string is shaped like a real IBAN; it does not tell you the account exists. For onboarding flows, pair it with a lookup provider.

Implementation Patterns

Constraint on the model, not the form. The most common mistake is attaching ISO constraints only inside FormType. Prefer attributes/annotations on the DTO:

class Signup
{
    #[Assert\NotBlank]
    #[Country]                       // ISO 3166-1 alpha-2
    public ?string $country = null;

    #[Assert\NotBlank]
    #[Assert\Length(max: 34)]
    #[Iban]
    public ?string $iban = null;

    #[Assert\NotBlank]
    #[Bic]
    public ?string $bic = null;
}

Forms then inherit the rules for free, and the same DTO validates JSON payloads via $validator->validate($dto) after deserialization.

Form-level when the field is form-only. For ad-hoc inputs that never leave the form layer:

$builder->add('iban', TextType::class, [
    'constraints' => [new Assert\NotBlank(), new Iban()],
]);

Paired-field constraints (VAT / SSN). These depend on the country, so a single attribute can't express the rule. Two workable patterns:

  1. A Callback that reads the sibling property and calls the validator on it.
  2. A small custom constraint + validator that grabs $this->context->getObject()->country and then runs the ISO check via ConstraintValidatorFactory, surfacing the same violation path.

For SSN-style checks, the underlying library expects a country argument — set it from the sibling value, never hard-code it.

Normalization before validation. ISO checks are strict about shape. Strip spaces, dashes and lowercase input before the constraint runs — either in a setter ($this->iban = strtoupper(str_replace(' ', '', $iban))) or with a Callback placed ahead of Iban in the constraint list. Order in the list is execution order.

Validation groups for drafts. Give soft checks (Bic, PhoneNumber) a draft group and keep hard checks (Iban, VatNumber) in Default, so a partially completed signup returns useful errors without blocking on optional fields.

Standalone / non-Symfony usage. Outside full-stack Symfony, register the constraint normally with Validation::createValidator(). The default ConstraintValidatorFactory resolves Foo → FooValidator by convention in the same namespace, which is exactly how this package is laid out — so no manual registration is usually needed. If a validator isn't found, check the validatedBy() return value against the class that actually exists (namespace case-sensitivity bites on Linux).

Testing. Don't assert "no violations" with one happy-path IBAN. Use a data provider of documented valid/invalid fixtures per constraint and assert the violation count and getMessage(); you're testing both the package and the underlying dataset, and the dataset moves between releases.

Gotchas and Tips

Attribute support is the first trap. On PHP 8, an attribute only works if the constraint class is declared #[\Attribute]. Older community constraints predate that, and the symptom is silent — the attribute is parsed as a comment-like token and the constraint simply never runs. If your IBAN passes everything, check src/Constraints/Iban.php for the attribute declaration; if it's missing, fall back to YAML/XML/annotations or new Iban() in a constraints array, or add the attribute via a subclass.

Null and empty string. Symfony constraints typically skip null; behaviour on '' varies by validator. Always pair with Assert\NotBlank and don't rely on the ISO constraint alone to enforce presence.

Country codes are alpha-2. UK fails, GB passes. USA fails, US passes. Store and normalize to alpha-2 at the boundary and you avoid a whole class of "valid country rejected" bug reports.

"Valid" ≠ "registered". VAT numbers and SSNs here are checksum/length checks. Do not treat a passing VatNumber as proof of a real, active business — that requires the VIES (or equivalent) lookup.

The ISO datasets are a snapshot. Country/currency/language lists and IBAN structures are static files shipped with iso-codes. Newly added countries, currency changes or bank restructuring won't be reflected until you bump the dependency. If you need to move faster than the package, wrap IsoCodes\... directly — this package is only a few lines per constraint, so that's a low-cost exit.

Maintenance risk. Low star count, zero dependents, and Symfony's Constraint/ConstraintValidator interfaces change across majors (return types on validatedBy(), validate() signature, translation strategy). Expect the package to lag behind contemporary Symfony versions. Practical mitigation: pin the package and Symfony's validator to known-good versions in composer.json, and keep a one-file local fallback wrapper ready.

Translation domain. Messages come from the constraint's default message option. Override per rule at construction (new Iban(message: 'account.iban.invalid')) and put the strings in your translation catalogs — otherwise you get generic English text in a localized UI.

Debugging checklist when a check misbehaves:

  1. $validator->validate($value, new Iban()) directly in a tinker/REPL — isolates the constraint from your DTO and annotations.
  2. Inspect $violation->getCode() and the constraint name to confirm which rule failed.
  3. Dump the normalized input — trailing whitespace from a form is the usual culprit.
  4. Confirm the constraint class was actually applied (typo'd import, missing attribute declaration, or a group that wasn't requested).

Extension points. All constraints are ordinary Symfony constraints: subclass them to add options or swap the message, and swap the validator by overriding validatedBy(). For cross-field logic (country + VAT), that subclass is the cleanest place to read $this->context->getObject() rather than scattering Callbacks through the DTO.

Performance. Fine at request scope — the datasets are small enough to load in-process. If you're bulk-validating imports or batches (hundreds of thousands of rows), memoize normalized results and measure once; the per-call object allocation is not free, but the bottleneck is usually your own string normalization, not the checksum.

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