tourze/user-id-idcard-bundle
Symfony bundle for Chinese resident ID card (shenfenzheng) support: validate 18-digit numbers, derive birth date, gender and region from the code, and attach ID data to your user entities. Simple bundle configuration and Doctrine-friendly helpers keep registration and profile forms consistent.
What this package is. tourze/user-id-idcard-bundle is a small Symfony bundle from the Tourze family of micro-packages. Its purpose is the Chinese resident identity card (身份证 / shenfenzheng) — parsing and validating the 18-character ID string, and exposing the derived data encoded inside it (region code, birth date, gender, checksum). v0.0.1 is the initial tagged release, so treat the surface as provisional.
First steps for a Laravel developer:
composer require tourze/user-id-idcard-bundle:0.0.1 — pin the exact version. Pre-1.0 packages in this ecosystem routinely reshuffle namespaces between tags.config/app.php providers. You are using its plain PHP classes directly, not its Symfony wiring.vendor/tourze/user-id-idcard-bundle/src/ first — with no release notes, the source is the documentation. Look for the value object / parser class (typically named around IdCard, IdCardInfo, or UserIdentity) and any Validator or Constraints folder.php artisan tinker before writing any code:$info = (new \Tourze\UserIdIdCardBundle\...\IdCardParser())->parse('11010519491231002X');
// inspect: birthdate, gender, region, checksum validity
The first real use case is almost always registration/profile validation: accept an ID number on a Chinese-facing signup form, reject bad checksums, and derive date of birth and gender instead of asking the user twice.
Wrap it in a Laravel validation rule. The natural integration point is a custom rule rather than a Symfony constraint:
class ChineseIdCard implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$result = app(IdCardParser::class)->parse((string) $value);
if (! $result->isValid()) {
$fail('The :attribute must be a valid Chinese ID card number.');
}
}
}
Register a Validator::extend('id_card', ...) alias too if you prefer the string syntax 'id_card' in form requests — it reads better across dozens of form classes.
Cast to a value object on the model. If the package ships an immutable value object, use a custom cast so the raw string never leaks into your domain code:
protected function casts(): array
{
return ['id_card' => IdCardCast::class];
}
Store the canonical 18-character uppercase form in the database; normalize on the way in, not on the way out.
Derive and persist, don't re-parse. Compute birthday, gender, and region once at write time into real columns (birth_date, gender, region_code). These are queryable, indexable, and survive a package swap. Re-parsing on every read couples your listing pages to a 0.0.1 dependency.
Pair with a checksum-only fast path. For high-traffic forms, run the (cheap) checksum test before instantiating the full parser, so obviously malformed input fails fast.
Use it in imports and backfills, not just forms. The most valuable day-to-day use is a queued import job: validate each row, collect failures into a report, and hydrate the derived columns in bulk. Do this in chunks with lazyById(); a 50k-row CSV will otherwise hammer memory.
Region codes are data, not logic. If the package exposes a region lookup, wrap it in your own RegionRepository interface with a database-backed implementation. Chinese administrative divisions get merged and renamed; you want to update a table, not a Composer dependency.
0.0.1 exactly, and re-run that test before any upgrade.services.yaml, DependencyInjection/, or *Extension class is dead weight in Laravel. Avoid pulling in symfony/framework-bundle semantics; construct the classes yourself or bind them in a small IdCardServiceProvider of your own.X casing. The final checksum character can be X or x. Normalize with strtoupper(trim($value)) before validating, or you will reject legitimate cards — and users paste from documents with full-width characters and stray spaces. Strip non-alphanumerics first.Carbon::parse(...)->isBefore(now()) to reject future dates — a checksum-valid but future-dated card is a data-entry or fraud signal, not a parse error.110105********002X). Consider encryption-at-rest via a cast for the raw column, and keep the derived columns unencrypted so they stay queryable.IdCardValidator and IdCardInfo DTOs wrapping whatever this package exposes. If Tourze abandons the bundle at 0.0.2 — a real risk for micro-packages — you swap one adapter class instead of hunting down call sites across the app.Assessment based on v0.0.1. No functional changes are documented in this release beyond the initial tag, so this evaluation reflects the package's structure and stated domain rather than a verified feature list — confirm the actual class names against vendor/ before committing to the patterns above.
How can I help you explore Laravel packages today?