serendipity_hq/bundle-users
A Symfony bundle for managing users in your application. It provides user entities, authentication and account management features, with Doctrine integration and configurable setup for adding user accounts to Symfony projects.
This is a Symfony bundle, not a Laravel package, so the mental model is different from what you're used to: there is no service provider auto-discovery via composer.json extra.laravel.providers. You register it in config/bundles.php and configure it under config/packages/. Composer install first, then:
composer require serendipity_hq/bundle-users
// config/bundles.php
return [
// ...
SerendipityHQ\Bundle\UsersBundle\UsersBundle::class => ['all' => true],
];
Where to look first, in this order:
README.md in the repository — the bundle's own documentation is the authoritative source for the entity model and required configuration.src/ — specifically the Entity/, Manager/, Factory/ (or Model/) and DependencyInjection/Configuration.php directories. The Configuration.php file is the definitive list of every option the bundle accepts; the README may lag behind it.Resources/config/ (or config/) — the service definitions tell you which services are public, which are tagged, and what interfaces you can decorate or alias.tests/ — in a low-adoption bundle this is the most reliable documentation of intended usage.First use case: create your application's concrete User entity by extending the bundle's base/mapped-superclass user, then let the bundle's manager or factory handle persistence so its lifecycle events and password handling fire. Follow the README's entity example verbatim for the first pass; deviate only after you've confirmed the flow works end to end with bin/console doctrine:schema:validate and a single functional test that creates a user.
Before you invest, do a spike: install, extend the base user, run bin/console doctrine:mapping:info, and confirm a user can be persisted and loaded. This bundle has 3 stars and 0 dependents, so treat the first hour as a feasibility check rather than a commitment.
Extend, don't annotate the base. The bundle almost certainly ships mapped superclasses or abstract entities. Your concrete entity extends them and adds #[ORM\Entity] plus a table name; the parent stays abstract. Duplicating fields the base already declares is the most common source of "column already defined" mapping errors.
Wrap persistence in the bundle's manager. Use the provided user manager/factory rather than calling EntityManager::persist() directly. The manager is where events (user created, password changed) and any hashing listener are dispatched. If you bypass it, you get a user row with a plaintext password and no events — a silent failure that only shows up at login.
Wire Symfony Security explicitly. Your concrete user must implement Symfony\Component\Security\Core\User\UserInterface and, on Symfony 5.3+, PasswordAuthenticatedUserInterface. Configure a provider in config/packages/security.yaml pointing at your entity class and a property: email (or username) loader. The bundle gives you the domain model; it does not replace the security firewall configuration.
Hash with UserPasswordHasherInterface. On Symfony 5.4+ the legacy PasswordEncoderInterface is deprecated. If the bundle's internal code still references encoders on the version you install, inject the hasher in your own registration service and pass an already-hashed password into the manager instead of letting the bundle hash it.
Back getRoles() with the bundle's role/permission model. If the bundle exposes roles, groups and permissions as separate entities, resolve them in your user's getRoles() and always append ROLE_USER. Cache the resolved array on the instance for the request — a naive implementation triggers a query per isGranted() call.
Use events for side effects, not the manager. Registration emails, audit logs and welcome notifications belong in listeners on the bundle's dispatched events. That keeps the write path transactional and lets you disable side effects in tests.
Version-pin deliberately. The repository's own CI matrix spans Symfony 5.4, 6.4, 7.x and 8.0 with PHP 8.2+. If you're on Symfony 6.4, add "symfony/*": "^5.4 || ^6.4"-style constraints only if you're forking; as a consumer, just pin the bundle to a tag and let Composer resolve. Test the actual combination you deploy — the low adoption means edge versions are less exercised in the wild than in CI.
extra.symfony.require is a dev artifact, not a consumer setting. The project's agent instructions explicitly warn never to commit that Flex key. If you fork or vendor the bundle, strip it from composer.json before committing, or Flex will silently pin your Symfony version.
Do not copy the Docker workflow into your app. make start 8.3 and make sf 7.0 are for developing the bundle itself. In your application, use your existing test setup; borrow the CI matrix only as a compatibility reference.
Expect API churn and pin exact versions. Zero dependents and three stars means there is no large user base stress-testing upgrades. Use composer require serendipity_hq/bundle-users:1.2.3 (exact) rather than ^1.2, and read the diff before bumping. The 2026-04-03 release date with no dependents suggests a maintained but unproven package.
Duplicate Doctrine mappings. If the bundle auto-registers its entity mappings and you also map the parent class in your doctrine.yaml, you'll get "The class X was not found in the chain configured namespaces" or duplicate-mapping errors. Restrict your mappings config to your src/Entity directory and let the bundle manage its own.
Debugging toolkit. Use bin/console debug:container --tag=doctrine.event_listener to see which listeners the bundle registers, bin/console debug:event-dispatcher to confirm its custom events exist on your version, and bin/console doctrine:mapping:info to verify every entity resolved. When a user creation works but login fails, check password hashing and the getRoles() return value before anything else.
Extension points. Override a bundle service by redefining its ID in config/services.yaml, or decorate its manager interface if one exists — decoration is safer across upgrades than replacing the service alias outright. Prefer listening to the bundle's events over subclassing its managers; subclassing couples you to internals the maintainer is free to change.
Guard against empty role collections. A user with no roles will throw at authentication time in newer Symfony versions (getRoles() must return at least one role). Enforce a default in the entity constructor, not in the form handler, so fixtures and console-created users get it too.
Read Configuration.php before the README. With a project this small, the config tree is more current than the prose. Every option name, default and cannotBeEmpty() rule lives there.
How can I help you explore Laravel packages today?