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

Users Module Laravel Package

anomaly/users-module

Anomaly Users Module adds complete user management to Laravel and Streams Platform applications: authentication, registration, profiles, roles, permissions, password resets, and an admin-ready CRUD interface for managing accounts and access control.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require anomaly/users-module

The module only makes sense inside the Streams Platform ecosystem (PyroCMS 3.x or a hand-rolled Streams app), so the first real prerequisite is that anomaly/streams-platform is already installed and bootstrapped. If you are on a stock PyroCMS distribution, the module normally ships already — check composer show anomaly/users-module before adding anything.

Boot it

Streams addons do not use Laravel package auto-discovery. The service provider is declared in the addon manifest (src/UsersModuleServiceProvider.php registered via module.json / resources/config/module.php). Then:

php artisan addon:install anomaly.module.users
# which internally runs
php artisan users:install      # creates users/roles/groups/permissions streams + seeds admin

Run php artisan migrate afterwards only if you have your own migrations referencing the users tables — the addon install handles the module's own schema.

Where to look first

  • src/User/ — the UserModel, UserRepository, UserPresenter, UserCriteria, and the HasRoles/HasPermissions traits. This is 80% of what you will touch.
  • src/Auth/ — controllers for login, logout, register, activate, forgot/reset password.
  • src/Role/, src/Group/, src/Permission/ — same repository/model/presenter shape.
  • src/Command/ — the artisan commands (users:install, users:seed, users:cleanup).
  • resources/views/ — the actual forms; override these by copying into your theme, not by editing vendor.
  • resources/config/ — feature toggles (registration, activation requirements, default role).

First use case

Guard a route and check a role in a controller or Blade view:

use Anomaly\UsersModule\User\Contract\UserRepositoryInterface;

$user = app(UserRepositoryInterface::class)->findByEmail($email);

if ($user && $user->hasRole('admin')) { /* ... */ }
@if(auth()->check() && auth()->user()->hasPermission('anomaly.module.pages::pages.manage'))
    <a href="{{ route('admin.pages.index') }}">Pages</a>
@endif

Implementation Patterns

Resolve through contracts, not models. Every entity has an interface (UserInterface, RoleInterface, PermissionInterface) bound to a repository. Injecting UserRepositoryInterface gives you findByEmail, findByUsername, findBySlug, and criteria chaining (->active()->role('admin')->get()), and it keeps you insulated from schema changes across module updates.

Middleware aliases. The module registers route middleware under the Streams :: convention:

Route::group(['middleware' => 'anomaly.module.users::authenticate'], function () { ... });
Route::get('/reports', 'ReportController@index')->middleware('anomaly.module.users::can:anomaly.module.reports::reports.view');

Use anomaly.module.users::guest on login/register routes so logged-in users are bounced to the redirect target rather than seeing the form again.

Register your own permissions. Add them to the module service provider's permissions array (or the module class), using the canonical slug format vendor.module::section.action. The admin UI permission matrix reads these directly, so anything you register becomes assignable to roles without extra UI work. Use the Streams translator for labels: 'anomaly.module.reports::permission.view'.

Hook the lifecycle with events. The module fires domain events you should listen to instead of overriding controllers:

  • UserWasRegistered / UserHasRegistered
  • UserWasActivated
  • UserHasLoggedIn / UserHasLoggedOut
  • UserPasswordWasReset

Typical pattern: listen for UserWasRegistered to push the user into a CRM, assign an onboarding role, or send a Slack ping. Register listeners in your own module's service provider — do not edit the users module's event map.

Extend the user profile via Streams fields. Users is a Streams entity; add profile columns by adding fields/assignments in a migration, not by editing the module's migrations:

$stream->addField('company', ['type' => 'anomaly.field_type.text']);

The user form and presenter pick these up automatically, and $user->company works through the Streams attribute accessor.

Custom auth flows. If you need a bespoke login endpoint (API token, SSO), reuse the module's AuthService/LoginFormHandler rather than calling Auth::attempt yourself — it routes through activation checks, throttling, and the remember-me/redirect logic. Bind your controller to the existing form handler and override only the response.

Scheduled housekeeping. php artisan users:cleanup clears expired activation codes and stale reset tokens. Schedule it hourly in app/Console/Kernel.php — the module does not schedule it for you.

Gotchas and Tips

No Laravel auto-discovery. Adding the composer package alone does nothing. If routes 404 and auth()->user() returns null, the addon provider was never booted. Confirm with php artisan addon:list and php artisan route:list | grep users.

Config keys use the :: namespace. config('anomaly.module.users::config.registration.enabled'), not config('users.registration'). Reading the wrong key silently returns null and defaults look like "feature is off." The module's resources/config/ is the source of truth — read it before assuming a setting doesn't exist.

Activation is a footgun in local/dev. New users may be unable to log in until activated (email or admin approval), and mail is often unconfigured locally. Either set the activation toggle in the users config to disabled for local environments, or run users:cleanup plus manually activate via the admin. When a login "fails silently," check $user->isActivated() before debugging credentials.

The users tables are Streams-managed. Don't hand-write migrations that ALTER TABLE users. Add Streams fields/assignments; otherwise the Streams compile step will fight you and the admin form won't know about your column. If you do need raw SQL, mirror it in a Streams field definition.

Version lock-in. This module is built against the Laravel 5.4–8 era of Streams Platform. Dropping it into a Laravel 10/11 app is not supported — the contracts, service provider signatures, and Auth internals have moved. Verify composer why anomaly/streams-platform before upgrading Laravel.

Presenter vs. model. In Blade you usually want $user->presenter() for display name, avatar, and role badges; the raw model returns IDs and Streams entry objects that render poorly. Calling $user->display_name on the model works but bypasses formatting.

Permission slugs are exact-match strings. A typo in a slug means the check returns false with no error — no exception, no log line. When a permission "doesn't work," dump $user->getPermissions() (or the role's permission array) and diff against the string in your middleware. Slugs are case-sensitive.

Override views by publishing, not patching. Copy resources/views/ into your theme's override path. Editing vendor/anomaly/users-module/resources/views/ survives exactly one composer update.

Repository criteria are lazy. UserRepositoryInterface returns a query-like object; forgetting ->get() or ->first() returns a criteria instance, not a user, and you'll get confusing "call to undefined method" errors downstream. When in doubt, var_dump(get_class($result)).

Extension points if you outgrow it: bind your own implementation of UserRepositoryInterface in a service provider, or extend UserModel and rebind UserInterface. Both are supported; subclassing the controller is not — controller actions are wired to routes via the addon's route config.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin