tourze/user-id-email-bundle
A lightweight Symfony bundle from Tourze that connects user IDs with email addresses, making it easy to store, look up, and resolve user identifiers across your application. Simple service integration and minimal configuration for Doctrine-backed projects.
tourze/user-id-email-bundle 0.0.1 is a scaffold release: the only published artifact is a compare link, so there are no documented features, BC promises, or migration notes. Read the source before installing — treat the repository as the spec.
What it appears to be: a tiny Symfony bundle supplying a Doctrine entity plus repository that stores a userId → email mapping. It is not a user/auth system; it is a lookup table for correlating an opaque user identifier (SSO subject, legacy ID, external IdP ID) with an email address.
Minimal steps in a Symfony app:
composer require tourze/user-id-email-bundle:0.0.1 — pin the exact version, the API has no stability guarantee at 0.0.x.config/bundles.php (Tourze\UserIdEmailBundle\UserIdEmailBundle::class => ['all' => true]).DependencyInjection/ extension or AbstractBundle::loadExtension for the mapping directory).doctrine:schema:update for a first install you intend to keep.src/Entity/UserIdEmail.php and src/Repository/UserIdEmailRepository.php first — they define the entire usable surface.First use case: you have a user record keyed by an opaque ID (OIDC sub, a UUID from another service, a legacy integer) but downstream systems — notification queues, audit logs, exports — need an email address. Instead of joining across services, you persist the pair here and read it by ID.
Laravel note: despite the assessment brief, this is a Symfony bundle. Laravel's package discovery will not register it and there are no publishable config or migration stubs. In Laravel you either (a) skip the bundle and copy the entity into an Eloquent model, or (b) install it and write a thin service provider that binds the repository and registers a Schema::create migration mirroring the Doctrine mapping. Option (a) is usually cheaper for a two-column table.
Upsert on user lifecycle events. This table is a projection, not a source of truth. Wire it to the event that already knows both values:
postPersist/postUpdate listener on your User entity, or an event subscriber on your domain's UserCreated/UserEmailChanged events.saved/created.The write is always the same shape — look up by userId, update the email if the row exists, insert otherwise. Do not blind-insert; you will accumulate duplicate rows that silently shadow each other on read.
Read with a single-row helper, then guard. The repository is a ServiceEntityRepository, so autowiring works once the bundle is registered. Wrap findOneBy(['userId' => $id]) in a small domain service returning ?string so callers never touch the entity — that boundary is what lets you swap in caching or a different store later.
Batch resolution to avoid N+1. For lists (dashboards, exports, admin tables) fetch once and hydrate:
$rows = $repo->createQueryBuilder('e')
->where('e.userId IN (:ids)')
->setParameter('ids', $ids)
->getQuery()
->getResult();
$map = array_column($rows, 'email', 'userId');
Backfill as an idempotent command. Write a console command that pages through your existing users in chunks of ~500 and calls the same upsert path. Because it is idempotent you can re-run it after any incident or after fixing a normalisation bug.
Testing. SQLite in-memory with the bundle's mapping loaded, plus one test asserting that upserting the same userId twice leaves exactly one row and updates the email.
vendor:publish, no migrations shipped. Any install in Laravel is a manual bridge; budget for a service provider and a migration rather than expecting it to "just work".0.0.1 exactly, diff the source when bumping, and keep a local note of the entity fields you depend on.userId → email table is only correct with a unique index on userId. Check whether the entity declares one; if not, add it in your own migration. Under concurrency the read-then-write upsert can race — catch the unique-violation and retry as an update.userId means the previous address is gone. If you need an audit trail (email change history is a compliance concern in most systems), log changes yourself or model the row as append-only with a current flag.utf8mb4_0900_ai_ci vs. Postgres) make "same" emails compare unequal across environments and produce duplicate rows that look identical to a human.Class ... is not a valid entity or mapped super class, which almost always means the bundle is not registered in config/bundles.php or the extension is not loading the mapping directory. Verify registration before touching the entity annotations.Resources/config/services.yaml for an existing service alias first — redeclaring the repository without overriding that alias gives you a silently unused class.How can I help you explore Laravel packages today?