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

Zip Code Validator Laravel Package

barbieswimcrew/zip-code-validator

Validate postal and zip codes with country-specific rules. This package adds Laravel validation support for dozens of countries, using accurate regex patterns plus an extensible interface for defining your own formats.

Deep Wiki
Context7

Getting Started

Install with Composer:

composer require barbieswimcrew/zip-code-validator

On modern Symfony with Flex the package/bundle is auto-registered. On older skeletons, register Barbieswimcrew\ZipCodeValidator\ZipCodeValidatorBundle in app/AppKernel.php or config/bundles.php.

Look first at:

  • src/Constraints/ZipCode.php — available options (countryCode, message), default messages, and the validatedBy() target class.
  • src/Constraints/ZipCodeValidator.php — the country-to-regex table and the exact supported ISO codes. This is the source of truth for what will actually validate.

Minimal first use case — a German/EU address form:

use Barbieswimcrew\ZipCodeValidator\Constraints\ZipCode;
use Symfony\Component\Validator\Constraints as Assert;

class Address
{
    #[Assert\NotBlank]
    #[ZipCode(countryCode: 'DE')]
    private string $postalCode = '';
}

For older Symfony/PHP use the annotation equivalent:

/**
 * @ZipCode(countryCode="DE")
 */
private $postalCode;

Then confirm the constraint is wired up:

bin/console debug:validator 'App\Entity\Address'

The first realistic use case is a checkout/registration form where the country is fixed or selected from a short list; add the constraint to the postal-code field and let Symfony's form layer surface violations automatically.

Implementation Patterns

Static country per DTO. The simplest and most robust pattern: one address class per country context, or a single class with a fixed countryCode. This avoids trying to make the constraint read a sibling country property (it cannot).

Validate arbitrary values through the validator service. When the country is dynamic, do not fight the attribute system — validate at runtime:

use Symfony\Component\Validator\Validator\ValidatorInterface;
use Barbieswimcrew\ZipCodeValidator\Constraints\ZipCode;

final class PostalCodeChecker
{
    public function __construct(private ValidatorInterface $validator) {}

    public function isValid(string $postalCode, string $countryCode): bool
    {
        $violations = $this->validator->validate(
            $postalCode,
            new ZipCode(['countryCode' => strtoupper($countryCode)])
        );

        return count($violations) === 0;
    }
}

This is the pattern to use in importers, API payload validators, and anywhere the country arrives as data rather than as a class-level constant.

Dynamic country on an object. Wrap the constraint in a custom constraint/validator that reads $object->getCountry() and delegates to ZipCodeValidator, or use a Callback constraint that instantiates ZipCode with the sibling value. Prefer the callback for small cases; prefer a dedicated constraint when the rule is reused.

Normalize before validating. Add a form data transformer or a setter that trims, uppercases, and strips/normalizes separators before validation runs. Example: "1234 ab" → "1234 AB" for NL, "sw1a 1aa" → "SW1A 1AA" for GB. Some regexes expect a space, some expect none; check the pattern in ZipCodeValidator for the target country and normalize to exactly that shape.

Combine constraints deliberately.

#[Assert\NotBlank]
#[Assert\Type('string')]
#[ZipCode(countryCode: 'US')]
private string $zip;

ZipCode only checks format; it does not imply non-blank, and many Symfony validators treat null/'' as valid. Always pair with NotBlank for required fields.

Form integration. Attach the constraint directly to the form field ('constraints' => [new ZipCode(['countryCode' => 'DE'])]) and use error_mapping when the field name differs from the entity property. Keep the constraint on the entity/DTO as well if the same object is validated outside forms.

Bulk validation. For imports, validate row DTOs with validator->validate($row) and collect violations into a row-indexed error array. Do not validate the whole collection at once if you need per-row reporting.

Testing. Use Symfony's ConstraintValidatorTestCase if you write a wrapper validator, and integration-test through Validation::createValidatorBuilder()->enableAttributeMapping()->getValidator() rather than calling the constraint validator directly. Include data providers per country, including edge cases (leading zeros, spaces, letters).

Gotchas and Tips

  • countryCode is mandatory. Omitting it causes an exception during validation, not a validation violation. That means a misconfigured constraint can produce a 500 rather than a form error. Guard dynamic code paths and add a test that asserts the exception for missing configuration.
  • Unsupported country codes throw, they do not fail validation. If your country list is user-driven, pre-check against the supported set. Symfony\Component\Intl\Countries::exists($code) confirms ISO validity but not that this package has a pattern; inspect the validator's table or maintain an allow-list.
  • Use ISO 3166-1 alpha-2, uppercase. UK is not the official code (GB), and EL is not (GR). Lowercase input will miss the lookup table. Normalize with strtoupper() before constructing the constraint.
  • Leading zeros are data, not numbers. Store postal codes in VARCHAR, never INT. 01067, 00100, and 01234 all break as integers on the way into validation.
  • Format-only validation. A passing zip code does not mean the address exists or is deliverable. Pair with an address-verification service for checkout fraud/deliverability checks.
  • Regexes can lag postal reforms. Country patterns change (new ranges, new territories). Treat the package's table as a snapshot: verify the pattern for your target countries against the national postal authority, and be prepared to fork or supply a custom validator. With a low-star, low-activity package, budget for owning the pattern table.
  • Whitespace and separator conventions differ per country. NL and GB commonly use a space, CA uses A1A 1A1, BR uses 00000-000, US uses 00000 or 00000-0000. Check whether the shipped regex accepts the hyphen/space variants your users type; otherwise normalize or reject with a clear message.
  • null and empty strings may pass. The validator likely returns early on empty values. Add NotBlank explicitly.
  • Sibling-property limitation. A property-level constraint cannot read $this->country; use a callback, a class-level constraint, or service-level validation instead of duplicating constraints per country.
  • Option typos are easy to miss. Constraint options are untyped public properties/arrays; a misspelled option may be silently ignored or throw depending on Symfony version. Use attributes with named arguments so PHP catches mistakes, and cover the constraint with a test.
  • Message overrides and translations. Set message per locale or route through your translation domain if the default English message is not acceptable. Check the constraint class for the default message key before overriding.
  • Version compatibility is the main upgrade risk. Verify the installed version supports your Symfony major (composer why-not symfony/validator), and note whether the package uses attributes, annotations, or both. Legacy bundle registration may be required on older skeletons.
  • Long-running workers are fine. The constraint is stateless and cheap to instantiate; no caching or reset logic is needed. Build the constraint once per country in hot loops rather than per row if profiling shows overhead.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package