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

User Bundle Laravel Package

msgphp/user-bundle

Symfony bundle for user management with registration, login, password reset, and profile handling. It provides Doctrine-backed user entities and integrates with Symfony Security, with extensible design for custom user models and fields.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. msgphp/user-bundle is a 2-star, 0-dependent package from the MSG PHP family (Roland Franssen's message-driven PHP experiment). It is best treated as a reference implementation of a user/identity domain modelled with commands + events + EAV, not as a drop-in auth bundle. Budget a fork, not a composer require.

Install

composer require msgphp/user-bundle

You will also pull in msgphp/user, msgphp/domain, msgphp/eav and msgphp/doctrine-bridge (EAV is used for user attributes). You need Doctrine ORM and a message bus (the package was designed against SimpleBus/Symfony Messenger via msgphp/domain's DomainMessage conventions).

Register the bundle (pre-Symfony 5.3 style — no recipe, no flex config):

// config/bundles.php
MsgPhp\UserBundle\MsgPhpUserBundle::class => ['all' => true],

Minimal config — the key thing to understand is that you own the entity classes and map them by FQCN:

# config/packages/msgphp_user.yaml
msgphp_user:
    class:
        user: App\Entity\User
        role: App\Entity\Role
        user_role: App\Entity\UserRole
        credential: App\Entity\Credential
        user_attribute: App\Entity\UserAttribute
    doctrine:
        orm:
            enabled: true

Where to look first, in this order:

  1. vendor/msgphp/user/src/Command/ — CreateUser, UpdateUser, ChangeUserCredential, ChangeUserRole, DeleteUser. This is the public API surface; read the command list before reading anything else.
  2. vendor/msgphp/user/src/Entity/User.php — getUsername(), getRoles(), getCredentials(), getAttributes().
  3. vendor/msgphp/user-bundle/src/DependencyInjection/Configuration.php — the real source of truth for config keys.
  4. vendor/msgphp/user-bundle/Resources/config/*.xml — the service IDs you'll be aliasing/decorating.
  5. The docs/ folder in the repo (not the README) — the published docs live on msgphp.github.io, and they're the only explanation of the EAV/class-mapping model.

First use case — register a user from a controller:

$bus->dispatch(new CreateUser($userId, $username));
$bus->dispatch(new ChangeUserCredential($userId, 'password', $hashedPassword));

Note there is no registration form, no login controller, no firewall config, no password hasher shipped. You dispatch commands, you hash, you wire Symfony Security yourself.

Implementation Patterns

Keep the controller thin and let the bus do the work. The whole point of the package is CQS separation: controllers/APIs dispatch Command objects, and Event objects (UserCreated, UserCredentialChanged, UserRoleChanged, UserDeleted) fan out to listeners. A typical controller:

#[Route('/users', methods: ['POST'])]
public function create(Request $request, MessageBusInterface $bus): Response
{
    $bus->dispatch(new CreateUser($id = Uuid::uuid4(), $request->get('username')));

    return $this->json(['id' => $id], 201);
}

Subclass the entities, don't fight them. Add your domain fields to App\Entity\User extends MsgPhp\User\Entity\User, and put genuinely optional profile data into UserAttribute (EAV) so you don't need a migration per field:

class User extends \MsgPhp\User\Entity\User
{
    private ?string $displayName = null;
    // getters/setters, plus a Doctrine mapping override
}

Use EAV for things like "marketing opt-in", "timezone", "avatar URL" — things you read on a profile page but rarely filter on.

Bridge to Symfony Security, carefully. Implement Symfony\Component\Security\Core\User\UserInterface on your User subclass, and a UserProvider backed by the repository service:

class MsgPhpUserProvider implements UserProviderInterface
{
    public function __construct(private UserRepositoryInterface $users) {}

    public function loadUserByIdentifier(string $identifier): UserInterface
    {
        return $this->users->findByUsername($identifier)
            ?? throw new UserNotFoundException();
    }

    public function upgradePassword(PasswordAuthenticatedUserInterface $u, string $hash): void
    {
        // dispatch ChangeUserCredential via the bus
    }
}

This adapter is ~40 lines and is the single most important piece of glue you'll write. There is no shipped security.user_providers config for this bundle.

Credentials are plural by design. A user holds a set of credentials, each typed (password, token, email, etc.). If you only ever want one password, enforce that in your command handler — the domain will happily let you append a second one and then getCredentials() order becomes ambiguous.

Roles live in the database, not security.yaml. Use the ChangeUserRole command with your Role entity. Keep security.yaml role_hierarchy for the semantics of inheritance, and the DB for the assignment. Don't try to use DB roles as role_hierarchy keys.

Extension points worth knowing:

  • Decorate MsgPhp\User\UserRepositoryInterface (alias of the Doctrine repo) to add tenant scoping or caching.
  • Decorate UserFactoryInterface if you need constructor invariants on a custom User.
  • Listen on UserCreated for verification email / welcome flow; it fires in the same unit of work as the insert, so queue it rather than sending synchronously.
  • Override service aliases in your own services.yaml rather than editing vendor XML.

Testing pattern: because writes go through commands, tests become "dispatch, then assert on the repository" — much cleaner than entity-manager poking:

$bus->dispatch(new CreateUser($id, 'alice'));
self::assertNotNull($users->findByUsername('alice'));

Gotchas and Tips

The maintenance risk is the headline. 2 stars and 0 dependents is the honest signal: no security review, no CVE feed, no community. The code targets PHP 7.2–7.4 and Symfony 4.4/5.x. On PHP 8.1+ expect deprecations, and on Symfony 6 UserInterface::getUsername() was removed — you'll be patching a fork before long.

Vendor it instead of requiring it. Clone into src/ or use a VCS repository entry with your own fork. You will need to touch the entity mappings and the security bridge; editing vendor/ is not viable long-term.

EAV will bite you on hydration. UserAttribute means loading a user can trigger extra queries per attribute. If you list 50 users on an admin page, eager-load attributes (fetch="EAGER" or a fetch-join in a custom repository method) or your page will issue 50+N queries.

Class mapping errors are opaque. If msgphp_user.class.user points at a class that doesn't extend MsgPhp\User\Entity\User, you get a generic failure from the DomainObjectFactory ("unable to create domain object") with no mention of the config key. Debug with:

bin/console debug:config msgphp_user
bin/console debug:container --tag=msgphp.user_repository

Role changes don't invalidate open sessions. Symfony serializes roles into the session token, so after ChangeUserRole the user keeps their old authorities until re-login. Either force re-authentication on role changes (UserRoleChanged listener → invalidate session / refresh token) or implement EquatableInterface on your User so the token refreshes when roles differ.

Don't let this own authentication. There is no login, no CSRF, no lockout, no password-policy, no rate limiting. Keep Symfony's form_login/json_login, PasswordHasher, and LoginThrottling in charge, and treat this bundle as the persistence + domain events layer underneath. Mixing the two mental models is where people get lost.

Migrations are yours. The bundle registers Doctrine mappings for the concrete classes you name, but doesn't ship schema migrations. Generate them after the first config change, and remember the EAV table (user_attribute) has a value column whose type/width you may need to tune per-attribute (long text for bios vs. varchar for flags) — EAV doesn't do that for you.

Read the sibling packages for the "why." msgphp/domain (message/identity/factory abstractions) and msgphp/eav are documented better than the user bundle itself. If you only want a sanity check on your own architecture, 30 minutes reading those is a better use of time than integrating the bundle — and is the most likely outcome of a serious evaluation.

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.
skyraptor/chatter
edlugz/sasapay
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