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

Nip Validator Bundle Laravel Package

kreyu/nip-validator-bundle

A Symfony bundle that adds validation for Polish NIP tax identification numbers. It integrates with Symfony’s Validator component, providing a constraint and validator to verify NIP format and checksum in forms and entities.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require kreyu/nip-validator-bundle:^2.1

Pin ^2.1 specifically if you intend to use the constraint as a PHP attribute — that support is the headline change in v2.1.0. On earlier tags the class is a plain Constraint and #[Nip] will blow up at class-load time (see Gotchas).

The bundle targets Symfony ≥ 6.1. If there is no Flex recipe for it (likely given the package's size), register it manually in config/bundles.php:

return [
    // ...
    Kreyu\Bundle\NipValidatorBundle\KreyuNipValidatorBundle::class => ['all' => true],
];

If Symfony Flex auto-registered it, this entry will already exist — don't add it twice.

Where to look first

  • src/Validator/Nip.php — the constraint. As of 2.1.0 it carries an #[Attribute(...)] declaration, which is what makes #[Nip] legal. Check the declared targets before putting it on a method or a promoted constructor argument.
  • src/Validator/NipValidator.php — the checksum logic itself; a few lines, worth reading once so you know whether it normalizes separators.
  • src/Resources/translations/ — the error message IDs, so you know what to override or translate.
  • composer.json — confirm the PHP and symfony/validator ranges against your project. Attribute support implies PHP ≥ 8.0.

First use case

Validate a Polish NIP on a DTO used by a form:

use Kreyu\Bundle\NipValidatorBundle\Validator\Nip;
use Symfony\Component\Validator\Constraints as Assert;

final class CompanyDto
{
    #[Assert\NotBlank]
    #[Nip]
    public ?string $nip = null;
}

Attributes work on typed properties and on constructor-promoted properties. Then either let Symfony Forms handle it (constraints on the form field, or data_class + valid), or run it through the validator directly:

$violations = $this->validator->validate($dto);

If count($violations) > 0, render the message from the bundle's translation catalogue.

Implementation Patterns

Constraint on an entity property

The simplest day-to-day pattern. Annotate the persisted field and rely on Doctrine lifecycle validation (or Symfony's Valid in forms):

#[ORM\Column(length: 10, nullable: true)]
#[Assert\NotBlank(groups: ['registration'])]
#[Nip(groups: ['registration'])]
private ?string $nip = null;

Named arguments for options

Attributes are instantiated via reflection, so the constraint's constructor is the API. Pass options by name to stay readable and resilient to signature changes:

#[Nip(message: 'company.nip.invalid', groups: ['billing'])]
public ?string $nip = null;

Positional usage (#[Nip('company.nip.invalid')]) works if $message is the first parameter, but it silently breaks if the constructor is reordered in a future release.

DTO + Symfony Form

Keep constraints on the DTO, not the form type, so validation works in controllers, CLI commands, and messenger handlers that never touch Form:

$form = $this->createForm(CompanyType::class, $dto);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
    // $dto->nip is a valid NIP
}

Form types should only define widgets (TextType with a null transformer) — let the validator own the rule.

Standalone validator service

For imports, seeders, or API payloads outside a form — the pattern that mattered most before 2.1.0, and still the right one for non-object input:

if (count($this->validator->validate($nip, new Nip())) > 0) {
    // reject
}

Validation groups

Register the constraint only in the groups where it matters (e.g., registration, billing) so partial updates or PATCH endpoints don't fail on missing NIPs:

#[Nip(groups: ['billing'])]

Remember: Symfony defaults to the Default group, so if you set explicit groups you must call validate($object, null, ['billing']) or configure the form's validation_groups. Attribute support does not change group resolution.

Reusable form type

Bundle the field + constraint in one form type to avoid drift across forms:

final class NipType extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'constraints' => [new Assert\NotBlank(), new Nip()],
        ]);
    }

    public function getParent(): string
    {
        return TextType::class;
    }
}

Testing

Unit-test your domain logic with the real validator, not by mocking it:

$validator = Validation::createValidator();
$violations = $validator->validate('1234563218', new Nip());
self::assertCount(0, $violations);

Since attributes are now supported, you can also assert mapping directly without booting a kernel:

$attributes = (new \ReflectionProperty(CompanyDto::class, 'nip'))
    ->getAttributes(Nip::class);
self::assertCount(1, $attributes);

Keep a small fixture set of valid and invalid NIPs (including dashed and padded versions) so regressions are obvious.

Gotchas and Tips

Attribute support is version-gated

#[Nip] only works on v2.1.0+. On 1.x/2.0 you get a fatal at metadata load — typically Cannot instantiate ... as attribute or Attempting to use a non-attribute class. Before blaming your autoloader, run:

composer show kreyu/nip-validator-bundle

If you can't upgrade, drop back to YAML/XML mapping with the FQCN, or new Nip() in code.

Don't nest it under Assert

The class lives in the bundle's namespace, not Symfony's:

use Kreyu\Bundle\NipValidatorBundle\Validator\Nip; // correct
// use Symfony\Component\Validator\Constraints\Nip; // does not exist

#[Assert\Nip] is a common copy-paste error and fails at container/metadata warmup, not at the call site.

Clear the cache after editing attributes

Constraint metadata is cached. Add or remove an attribute and the old mapping can survive:

php bin/console cache:clear

If a newly added #[Nip] "does nothing," this is the first thing to try.

Normalization is on you unless the bundle says otherwise

Polish NIPs are frequently written as 123-456-32-18 or with spaces. Many small validator bundles do not strip separators — they validate the raw string. Verify NipValidator and, if it doesn't normalize, either:

  • add a Callback/Regex constraint or DataTransformer before the Nip constraint, or
  • normalize in the DTO setter (preg_replace('/\D/', '', $value)).

Don't assume a hyphenated NIP will pass just because the checksum is correct.

Null is not invalid

Like all Symfony constraints, null/empty is skipped by the validator. If the field is required, pair Nip with NotBlank; otherwise an empty NIP passes silently.

Bundle registration failures

If you see "cannot find constraint" or the validator service is missing, first check config/bundles.php and run:

php bin/console debug:container --tag=validator.constraint_validator

If the validator isn't listed, the bundle wasn't registered or its services weren't autoconfigured — still the most common first-run error. Note that attribute support makes mapping errors look different (class-load failure vs. "constraint not run"), so confirm registration before chasing the attribute syntax.

Version and maintenance risk

  • 2.1.0 is the current release and it lifts a real ergonomic limitation, which is a good sign the package is still tended to. It is nonetheless low-traffic: small surface, few dependents. Read NipValidator before adopting it in a payments/tax flow; the logic audits in a few minutes.
  • Pin a version in composer.json and note it in your own docs so a future composer update doesn't surprise you.
  • The fallback is ~20 lines: strip non-digits, require length 10, verify the weighted checksum, weights 6,5,7,2,3,4,5,6,7, last digit must match sum % 11 (and reject % 11 === 10). Keep that escape hatch in mind if the bundle goes quiet again.

Framework scope

This is a Symfony bundle — there's no Laravel service provider or Validator::extend hook. If you're consuming it from Laravel you'd have to pull symfony/validator in alongside it, which rarely pays off; implement the checksum inline instead.

Symfony and PHP version mismatch

The bundle requires Symfony ≥ 6.1, and attribute usage requires PHP ≥ 8.0. On 5.4/6.0, or on PHP 7.4, you'll get resolver/attribute errors:

php bin/console --version
composer show symfony/validator
php -v

Message overrides and localization

Override the bundle's message key in translations/validators.pl.yaml (or your locale) rather than editing vendor files. If the message renders as a raw key, translations weren't loaded — clear cache.

Debugging mapping

When a constraint "doesn't run," inspect the actual metadata:

php bin/console debug:validator 'App\Dto\CompanyDto'

This shows which constraints and groups are attached — the fastest way to distinguish a mapping problem from a checksum failure, and to confirm that an attribute was actually picked up.

Extension points

  • Customize via constraint options (message, groups, payload) rather than subclassing; with attributes, named arguments are the intended configuration surface.
  • If you need other jurisdiction identifiers (REGON, PESEL), compose a separate constraint — there's little here that's reusable across formats, so treat it as a single-rule bundle, not a general tax-ID framework.
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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource