⚠️ Documentation Under Review: This documentation is currently being updated and verified against the actual implementation. Some information may be incorrect or incomplete. Please verify all code examples against the actual source code before use.
The Neuron CMS provides comprehensive user management capabilities for creating, updating, and deleting user accounts. Management is available through both CLI commands and a web-based administrative interface. The system supports role-based access control, account status management, and email verification.
User management consists of the following classes:
Neuron\Cms\Models\User): User entity with properties and validationNeuron\Cms\Repositories\IUserRepository): Repository interface for data persistenceNeuron\Cms\Repositories\DatabaseUserRepository): PDO-based repository implementationNeuron\Cms\Services\User\*): Business logic for create/update/delete operationsNeuron\Cms\Cli\Commands\User\*): Command-line user managementNeuron\Cms\Controllers\Admin\Users): Web interface for user managementThe CMS uses an event-driven architecture built on the Neuron Events component. Events allow decoupled communication between different parts of the system.
User lifecycle events are located in Neuron\Cms\Events\User\:
user.created): Fired when new user is created
User $user, array $datauser.updated): Fired when user is updated
User $user, array $changesuser.deleted): Fired when user is deleted
User $useruser.login): Fired on successful login
User $user, bool $rememberuser.logout): Fired on logout
User $useruser.login.failed): Fired on failed login attempt
string $username, string $reasonCreate a listener class that implements IListener:
use Neuron\Events\IListener;
use Neuron\Events\IEvent;
use Neuron\Cms\Events\User\UserCreatedEvent;
class UserCreatedListener implements IListener
{
private $mailer;
private $logger;
public function __construct( $mailer, $logger )
{
$this->mailer = $mailer;
$this->logger = $logger;
}
public function event( IEvent $event )
{
if( $event instanceof UserCreatedEvent )
{
// Send welcome email
$this->mailer->send( $event->user->getEmail(), 'Welcome!' );
// Log user creation
$this->logger->info( 'New user created: ' . $event->user->getUsername() );
}
}
}
Register listeners using the EventEmitter facade:
use Neuron\Application\Facades\EventEmitter;
use Neuron\Events\Broadcasters\Generic;
$emitter = new EventEmitter();
$emitter->registerBroadcaster( new Generic() );
// Register listener by class name
$emitter->registerListener( UserCreatedEvent::class,
UserCreatedListener::class );
// Or register multiple listeners at once
$emitter->registerListeners( [
UserCreatedEvent::class => [ UserCreatedListener::class ],
UserUpdatedEvent::class => [ UserUpdatedListener::class ],
] );
Events are automatically emitted by service classes:
// In UserCreator service
$user = $this->repository->create( $userData );
$this->emitter->emit( new UserCreatedEvent( $user, $userData ) );
The User model extends Neuron\Orm\Model and uses PHP 8+ attributes for ORM configuration:
use Neuron\Orm\Model;
use Neuron\Orm\Attributes\Table;
use Neuron\Orm\Attributes\HasMany;
#[Table( 'users' )]
class User extends Model
{
// Model implementation
}
The CMS models use the following Neuron ORM attributes:
#[Table('table_name')]: Specifies the database table name#[BelongsTo(Model::class, 'foreign_key')]: Defines a belongs-to relationship#[HasMany(Model::class, 'foreign_key')]: Defines a has-many relationship#[BelongsToMany(Model::class, 'pivot_table')]: Defines a many-to-many relationshipThe User model contains the following properties:
| Property | Type | Description |
|---|---|---|
id |
int |
User ID (auto-increment) |
username |
string |
Unique username |
email |
string |
Unique email address |
password_hash |
string |
Hashed password (Argon2id/Bcrypt) |
role |
string |
User role (admin, editor, author, subscriber) |
status |
string |
Account status (active, inactive, suspended) |
email_verified |
bool |
Email verification status |
remember_token |
string |
Remember me token (hashed) |
two_factor_secret |
string |
Two-factor authentication secret |
two_factor_recovery_codes |
array |
Recovery codes for 2FA |
failed_login_attempts |
int |
Failed login attempt counter |
locked_until |
DateTimeImmutable |
Account lockout expiration |
created_at |
DateTimeImmutable |
Account creation timestamp |
updated_at |
DateTimeImmutable |
Last update timestamp |
last_login_at |
DateTimeImmutable |
Last successful login |
timezone |
string |
User timezone (default: UTC) |
Four predefined roles are available using the UserRole enum:
use Neuron\Cms\Enums\UserRole;
UserRole::Admin // 'admin' - Full system access
UserRole::Editor // 'editor' - Content management access
UserRole::Author // 'author' - Own content management
UserRole::Subscriber // 'subscriber' - Read-only access
Role hierarchy (from highest to lowest):
Three account statuses are available using the UserStatus enum:
use Neuron\Cms\Enums\UserStatus;
UserStatus::Active // 'active' - Normal access
UserStatus::Inactive // 'inactive' - No access (soft disable)
UserStatus::Suspended // 'suspended' - Temporarily blocked
// Identity
$user->getId(): ?int
$user->getUsername(): string
$user->getEmail(): string
// Authentication
$user->getPasswordHash(): string
$user->setPasswordHash( string $hash ): self
// Role checking
$user->getRole(): string
$user->setRole( string $role ): self
$user->isAdmin(): bool
$user->isEditor(): bool
$user->isAuthor(): bool
// Status checking
$user->getStatus(): string
$user->setStatus( string $status ): self
$user->isActive(): bool
$user->isSuspended(): bool
// Email verification
$user->isEmailVerified(): bool
$user->setEmailVerified( bool $verified ): self
// Account lockout
$user->isLockedOut(): bool
$user->getLockedUntil(): ?DateTimeImmutable
$user->setLockedUntil( ?DateTimeImmutable $lockedUntil ): self
$user->getFailedLoginAttempts(): int
$user->incrementFailedLoginAttempts(): self
$user->resetFailedLoginAttempts(): self
// Two-factor authentication
$user->hasTwoFactorEnabled(): bool
$user->getTwoFactorSecret(): ?string
$user->setTwoFactorSecret( ?string $secret ): self
$user->getTwoFactorRecoveryCodes(): ?array
// Timestamps
$user->getCreatedAt(): ?DateTimeImmutable
$user->getUpdatedAt(): ?DateTimeImmutable
$user->getLastLoginAt(): ?DateTimeImmutable
// Timezone
$user->getTimezone(): string
$user->setTimezone( string $timezone ): self
// Conversion
$user->toArray(): array
User::fromArray( array $data ): User
The repository interface defines standard data access methods:
interface IUserRepository
{
public function findById( int $id ): ?User;
public function findByUsername( string $username ): ?User;
public function findByEmail( string $email ): ?User;
public function findByRememberToken( string $token ): ?User;
public function create( User $user ): User;
public function update( User $user ): bool;
public function delete( int $id ): bool;
public function all(): array;
public function count(): int;
}
PDO-based repository implementation supporting SQLite, MySQL, and PostgreSQL:
use Neuron\Cms\Repositories\DatabaseUserRepository;
use Neuron\Data\Settings\SettingManager;
$settings = Registry::getInstance()->get( 'Settings' );
$repository = new DatabaseUserRepository( $settings );
// Find users
$user = $repository->findById( 1 );
$user = $repository->findByUsername( 'admin' );
$user = $repository->findByEmail( '[email protected]' );
// List users
$users = $repository->all(); // Returns array of User objects, ordered by created_at DESC
$count = $repository->count();
// Create user
$user = new User();
$user->setUsername( 'newuser' );
$user->setEmail( '[email protected]' );
$user->setPasswordHash( $hasher->hash('password123' ));
$user->setRole( UserRole::Author );
$user = $repository->create( $user ); // Returns User with ID set
// Update user
$user->setEmail( '[email protected]' );
$repository->update( $user );
// Delete user
$repository->delete( 1 );
Duplicate Prevention:
The repository automatically checks for duplicate usernames and emails:
try {
$repository->create( $user );
} catch( Exception $e )
{
// Throws: 'Username already exists' or 'Email already exists'
}
Creates new users with password validation and hashing:
use Neuron\Cms\Services\User\Creator;
use Neuron\Cms\Repositories\DatabaseUserRepository;
use Neuron\Cms\Auth\PasswordHasher;
$creator = new Creator( $repository, $passwordHasher );
try {
$user = $creator->create( username: 'johndoe',
email: '[email protected]',
password: 'SecurePass123',
role: User::ROLE_AUTHOR );
// User created successfully
// - Password validated against requirements
// - Password hashed with Argon2id/Bcrypt
// - Status set to active
// - Email verified set to true
// - UserCreatedEvent emitted
} catch( Exception $e )
{
// Password does not meet requirements: Must contain uppercase letter, Must contain number
}
Automatic Actions:
activetrueUserCreatedEvent emittedUpdates existing users with optional password change:
use Neuron\Cms\Services\User\Updater;
$updater = new Updater( $repository, $passwordHasher );
try {
// Update without changing password
$user = $updater->update( user: $user,
username: 'johndoe',
email: '[email protected]',
role: UserRole::Editor,
password: null, // No password change
timezone: 'America/New_York' );
// Update with new password
$user = $updater->update( user: $user,
username: 'johndoe',
email: '[email protected]',
role: UserRole::Editor,
password: 'NewSecurePass456', // Password will be validated and hashed
timezone: 'America/New_York' );
// UserUpdatedEvent emitted
} catch( Exception $e )
{
// Password does not meet requirements or duplicate username/email
}
Automatic Actions:
UserUpdatedEvent emittedSafely deletes users:
use Neuron\Cms\Services\User\Deleter;
$deleter = new Deleter( $repository );
try {
$result = $deleter->delete( userId: 5 );
if( $result )
{
// User deleted successfully
// UserDeletedEvent emitted
}
} catch( Exception $e )
{
// User not found
}
Automatic Actions:
UserDeletedEvent emittedCreate new user interactively via command line.
Usage:
./vendor/bin/neuron cms:user:create
Interactive Prompts:
╔═══════════════════════════════════════╗
║ Neuron CMS - Create User ║
╚═══════════════════════════════════════╝
Enter username: johndoe
Enter email: [email protected]
Enter password (min 8 characters): ********
Available roles:
1. Admin (full access)
2. Editor (manage all content)
3. Author (manage own content)
4. Subscriber (read-only)
Select role (1-4) [3]: 3
User created:
ID: 5
Username: johndoe
Email: [email protected]
Role: author
Validation:
Error Handling:
# Duplicate username
User 'johndoe' already exists!
# Invalid email
Valid email is required!
# Weak password
Password does not meet requirements:
- Must contain uppercase letter
- Must contain number
Display all users in tabular format.
Usage:
./vendor/bin/neuron cms:user:list
Output:
────────────────────────────────────────────────────────────────────────────────────────────────────
ID │ Username │ Email │ Role │ Status │ Created
────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ admin │ [email protected] │ admin │ active │ 2025-01-10 14:23:11
2 │ editor │ [email protected] │ editor │ active │ 2025-01-11 09:15:42
3 │ johndoe │ [email protected] │ author │ 🔒 Locked │ 2025-01-12 16:45:23
4 │ subscriber │ [email protected] │ subscriber │ suspended │ 2025-01-13 11:30:15
────────────────────────────────────────────────────────────────────────────────────────────────────
Total users: 4
Columns:
Empty State:
No users found.
Delete user by ID or username with confirmation prompt.
Usage:
./vendor/bin/neuron cms:user:delete <id or username>
Examples:
# Delete by ID
./vendor/bin/neuron cms:user:delete 5
# Delete by username
./vendor/bin/neuron cms:user:delete johndoe
Confirmation Prompt:
You are about to delete the following user:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ID: 5
Username: johndoe
Email: [email protected]
Role: author
Status: active
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Are you sure you want to delete this user? Type 'DELETE' to confirm: DELETE
User deleted successfully.
Safety Features:
Error Handling:
# No identifier provided
Please provide a user ID or username.
Usage: php neuron cms:user:delete <id or username>
# User not found
User 'johndoe' not found.
# Confirmation cancelled
Deletion cancelled.
Reset a user's password via command line. Useful for password recovery when users are locked out or have forgotten their credentials.
Usage:
./vendor/bin/neuron cms:user:reset-password [options]
Options:
--username=<username> or -u: Specify username directly--email=<email> or -e: Specify email directlyExamples:
# Interactive mode - prompts for all inputs
./vendor/bin/neuron cms:user:reset-password
# Reset by username
./vendor/bin/neuron cms:user:reset-password --username=johndoe
# Reset by email
./vendor/bin/neuron cms:user:reset-password [email protected]
# Using short options
./vendor/bin/neuron cms:user:reset-password -u admin
Interactive Flow:
╔═══════════════════════════════════════╗
║ Neuron CMS - Reset User Password ║
╚═══════════════════════════════════════╝
Enter username or email: johndoe
User found:
ID: 5
Username: johndoe
Email: [email protected]
Role: author
Reset password for this user? (yes/no) [no]: yes
Enter new password: ********
Confirm new password: ********
✓ Password reset successfully for user: johndoe
Password Requirements:
Requirements are loaded from your site's password policy configuration (auth.passwords in config/auth.yaml):
Features:
Error Handling:
# User not found
✗ User 'nonexistent' not found!
# Confirmation cancelled
⚠ Password reset cancelled.
# Password too short
✗ Password must be at least 8 characters long!
# Password doesn't meet requirements
✗ Password does not meet requirements:
- Must contain at least one uppercase letter
- Must contain at least one lowercase letter
- Must contain at least one number
# Passwords don't match
✗ Passwords do not match!
# Database update failed
✗ Failed to update password in database
Security Notes:
Common Use Cases:
# Admin locked out after too many failed login attempts
./vendor/bin/neuron cms:user:reset-password -u admin
# User forgot password and email system is unavailable
./vendor/bin/neuron cms:user:reset-password -e [email protected]
# Emergency password reset for compromised account
./vendor/bin/neuron cms:user:reset-password -u compromised_user
# Reset password for testing environment
./vendor/bin/neuron cms:user:reset-password --username=testuser
The admin interface provides full CRUD operations for user management.
Controller: Neuron\Cms\Controllers\Admin\Users
Routes (from CMS default routes):
admin_users:
method: GET
route: /admin/users
controller: Neuron\Cms\Controllers\Admin\Users@index
admin_users_create:
method: GET
route: /admin/users/create
controller: Neuron\Cms\Controllers\Admin\Users@create
admin_users_store:
method: POST
route: /admin/users
controller: Neuron\Cms\Controllers\Admin\Users@store
admin_users_edit:
method: GET
route: /admin/users/{id}/edit
controller: Neuron\Cms\Controllers\Admin\Users@edit
admin_users_update:
method: POST
route: /admin/users/{id}
controller: Neuron\Cms\Controllers\Admin\Users@update
admin_users_destroy:
method: POST
route: /admin/users/{id}/delete
controller: Neuron\Cms\Controllers\Admin\Users@destroy
Route: GET /admin/users
Method: index()
Displays all users in a table with links to edit/delete.
View Data:
users: Array of all User objects (ordered by created_at DESC)User: Current authenticated userSuccess: Flash success messageError: Flash error messageView: cms/resources/views/admin/users/index.php
Route: GET /admin/users/create
Method: create()
Displays user creation form with role selection.
View Data:
roles: Array of available roles (admin, editor, author, subscriber)User: Current authenticated userView: cms/resources/views/admin/users/create.php
Form Submission: POST /admin/users
Method: store()
Required Fields:
username: Unique usernameemail: Valid email address (unique)password: Password meeting requirementsrole: One of the four rolesValidation:
Success: Redirects to user list with success message
Failure: Redirects back to form with error message
Route: GET /admin/users/{id}/edit
Method: edit(int $id)
Displays user edit form with current values.
View Data:
user: User object to editroles: Array of available rolesUser: Current authenticated userView: cms/resources/views/admin/users/edit.php
Form Submission: POST /admin/users/{id}
Method: update(int $id)
Fields:
username: Username (required)email: Email address (required)role: Role selectionpassword: New password (optional, validated if provided)Validation:
Success: Redirects to user list with success message
Failure: Redirects back to form with error message
Route: POST /admin/users/{id}/delete
Method: destroy(int $id)
Deletes user account.
Safety Features:
Success: Redirects to user list with success message
Failure: Redirects to user list with error message
Self-Deletion Prevention:
// In destroy() method
if ($currentUser && $currentUser->getId() === $id) {
$this->redirect( 'admin_users', [], ['error', 'Cannot delete your own account'] );
}
use Neuron\Cms\Models\User;
use Neuron\Cms\Services\User\Creator;
use Neuron\Cms\Repositories\DatabaseUserRepository;
use Neuron\Cms\Auth\PasswordHasher;
// Initialize dependencies
$repository = new DatabaseUserRepository( $settings );
$passwordHasher = new PasswordHasher();
$creator = new Creator( $repository, $passwordHasher );
// Create user via service (recommended)
try {
$user = $creator->create( username: 'newuser',
email: '[email protected]',
password: 'SecurePassword123',
role: User::ROLE_AUTHOR );
echo "User created with ID: " . $user->getId();
} catch( Exception $e )
{
echo "Error: " . $e->getMessage();
}
// Create user directly via repository (not recommended - bypasses validation)
$user = new User();
$user->setUsername( 'newuser' );
$user->setEmail( '[email protected]' );
$user->setPasswordHash( $passwordHasher->hash('password' ));
$user->setRole( UserRole::Author );
$user->setStatus( UserStatus::Active );
$repository->create( $user );
use Neuron\Cms\Services\User\Updater;
$updater = new Updater( $repository, $passwordHasher );
// Fetch user
$user = $repository->findById( 5 );
// Update via service (recommended)
try {
$updater->update( user: $user,
username: 'updateuser',
email: '[email protected]',
role: UserRole::Editor,
password: 'NewPassword456', // Optional
timezone: 'America/Chicago' );
} catch( Exception $e )
{
echo "Error: " . $e->getMessage();
}
// Update directly via repository (not recommended - bypasses validation)
$user->setEmail( '[email protected]' );
$user->setRole( UserRole::Admin );
$repository->update( $user );
use Neuron\Cms\Services\User\Deleter;
$deleter = new Deleter( $repository );
// Delete via service (recommended)
try {
$deleter->delete( userId: 5 );
} catch( Exception $e )
{
echo "Error: " . $e->getMessage();
}
// Delete directly via repository
$repository->delete( 5 );
// Find by ID
$user = $repository->findById( 1 );
// Find by username
$user = $repository->findByUsername( 'admin' );
// Find by email
$user = $repository->findByEmail( '[email protected]' );
// Get all users
$users = $repository->all();
// Count users
$count = $repository->count();
// Check user properties
if( $user )
{
if ($user->isAdmin()) {
// User is administrator
}
if ($user->isActive()) {
// Account is active
}
if ($user->isLockedOut()) {
// Account is locked
}
}
User events are emitted during lifecycle operations and can be captured by event listeners.
Available Events:
Neuron\Cms\Events\UserCreatedEvent // Emitted when user is created
Neuron\Cms\Events\UserUpdatedEvent // Emitted when user is updated
Neuron\Cms\Events\UserDeletedEvent // Emitted when user is deleted
Event Properties:
// UserCreatedEvent
$event->user // User object
// UserUpdatedEvent
$event->user // User object
// UserDeletedEvent
$event->userId // int - ID of deleted user
Example Listener:
Create event listener in config/event-listeners.yaml:
Neuron\Cms\Events\UserCreatedEvent:
- App\Listeners\SendWelcomeEmail
- App\Listeners\LogUserCreation
Neuron\Cms\Events\UserUpdatedEvent:
- App\Listeners\LogUserUpdate
Neuron\Cms\Events\UserDeletedEvent:
- App\Listeners\CleanupUserData
Listener Implementation:
namespace App\Listeners;
use Neuron\Cms\Events\UserCreatedEvent;
class SendWelcomeEmail
{
public function handle( UserCreatedEvent $event )
{
$user = $event->user;
// Send welcome email
mail( $user->getEmail(),
'Welcome to the CMS',
"Welcome {$user->getUsername()}!" );
}
}
Generating Listeners:
Use scaffolding command to generate listener:
./vendor/bin/neuron listener:generate SendWelcomeEmail Neuron\\Cms\\Events\\UserCreatedEvent
See Scaffolding Reference for details.
Roles are assigned during user creation or can be changed via update:
// Via CLI
# Role selection during cms:user:create
// Via service
$creator->create( 'user', '[email protected]', 'password', UserRole::Admin );
$updater->update( $user, 'user', '[email protected]', UserRole::Editor );
// Directly on model
$user->setRole( UserRole::Author );
$repository->update( $user );
// Method approach
if ($user->isAdmin()) {
// Full system access
}
if ($user->isEditor()) {
// Content management access
}
if ($user->isAuthor()) {
// Own content management
}
// Direct comparison
if ($user->getRole() === UserRole::Admin) {
// Admin access
}
// Using helper functions (requires authenticated user)
if (is_admin()) {
// Current user is admin
}
if (has_role( 'editor' )) {
// Current user has editor role
}
While the system does not enforce role hierarchy automatically, the typical permission structure is:
Admin (highest privilege):
Editor:
Author:
Subscriber (lowest privilege):
Implement role-based authorization in controllers:
public function edit( int $postId )
{
$currentUser = auth();
$post = $postRepository->findById( $postId );
// Allow admins and editors to edit any post
if ($currentUser->isAdmin() || $currentUser->isEditor()) {
// Allow
}
// Allow authors to edit their own posts
elseif ($currentUser->isAuthor() && $post->getAuthorId() === $currentUser->getId()) {
// Allow
}
else {
// Deny
return $this->renderError( 403, 'Unauthorized' );
}
}
// Activate account
$user->setStatus( UserStatus::Active );
$repository->update( $user );
// Deactivate account
$user->setStatus( UserStatus::Inactive );
$repository->update( $user );
// Suspend account
$user->setStatus( UserStatus::Suspended );
$repository->update( $user );
if ($user->isActive()) {
// Normal access
}
if ($user->isSuspended()) {
// Account suspended
}
if ($user->getStatus() === UserStatus::Inactive) {
// Account inactive
}
Status checks should be performed during authentication:
// In AuthManager
if (!$user->isActive()) {
return false; // Deny login
}
// Via repository
$user = $repository->findById( 5 );
$user->resetFailedLoginAttempts(); // Sets attempts to 0, clears locked_until
$repository->update( $user );
// Via direct database update
UPDATE users
SET failed_login_attempts = 0, locked_until = NULL
WHERE id = 5;
if ($user->isLockedOut()) {
$lockedUntil = $user->getLockedUntil();
echo "Account locked until: " . $lockedUntil->format( 'Y-m-d H:i:s' );
}
$attempts = $user->getFailedLoginAttempts();
echo "Failed attempts: {$attempts}";
Lockout is automatically handled by AuthManager during login. See Authentication Guide for details.
// Mark email as verified
$user->setEmailVerified( true );
$repository->update( $user );
// Check verification status
if ($user->isEmailVerified()) {
// Email verified
}
Email verification is automatically set to true for users created via CLI or admin interface. For member registration, email verification is handled by the registration system. See Member Registration Guide for details.
Always use service classes instead of direct repository access:
Recommended:
$creator->create( 'user', '[email protected]', 'password', User::ROLE_AUTHOR );
Not Recommended:
$user = new User();
$user->setUsername( 'user' );
$repository->create( $user );
Service classes provide:
Never store plain text passwords:
// Correct
$user->setPasswordHash( $passwordHasher->hash($password ));
// Incorrect
$user->setPasswordHash( $password ); // Never do this
Always validate passwords before hashing:
if (!$passwordHasher->meetsRequirements( $password )) {
$errors = $passwordHasher->getValidationErrors( $password );
throw new Exception( 'Password invalid: ' . implode(', ', $errors ));
}
Check for duplicates before creating users:
// Service classes handle this automatically
try {
$creator->create( $username, $email, $password, $role );
} catch( Exception $e )
{
// Handles: 'Username already exists' or 'Email already exists'
}
// Manual checking
if ($repository->findByUsername( $username )) {
throw new Exception( 'Username already exists' );
}
if ($repository->findByEmail( $email )) {
throw new Exception( 'Email already exists' );
}
Prevent users from deleting their own accounts:
$currentUser = auth();
if ($currentUser && $currentUser->getId() === $userIdToDelete) {
throw new Exception( 'Cannot delete your own account' );
}
$deleter->delete( $userIdToDelete );
Use event listeners for side effects:
// Good: Send welcome email via listener
config/event-listeners.yaml:
Neuron\Cms\Events\UserCreatedEvent:
- App\Listeners\SendWelcomeEmail
// Bad: Send email directly in controller
public function store() {
$user = $creator->create( ... );
mail( $user->getEmail(), 'Welcome', '...'); // Don't do this
}
Event listeners keep controllers clean and enable reusable functionality.
Symptoms: Error when creating user
Solutions:
Check password requirements:
$errors = $passwordHasher->getValidationErrors( $password );
var_dump( $errors );
Check for duplicate username/email:
# Via CLI
./vendor/bin/neuron cms:user:list
# Via database
SELECT username, email FROM users WHERE username = 'johndoe';
Check database connection:
# Verify database settings in config/neuron.yaml
database:
adapter: mysql
host: localhost
name: cms_database
user: cms_user
Symptoms: User created but cannot login
Solutions:
Check account status:
if (!$user->isActive()) {
$user->setStatus( UserStatus::Active );
$repository->update( $user );
}
Check account lockout:
if ($user->isLockedOut()) {
$user->resetFailedLoginAttempts();
$repository->update( $user );
}
Verify password hash:
// Ensure password was hashed correctly
$hasher = new PasswordHasher();
if ($hasher->verify( $password, $user->getPasswordHash())) {
echo "Password correct";
}
Symptoms: Cannot delete own account via admin interface
Solution: This is intentional security feature. Use different admin account to delete user, or delete via database:
DELETE FROM users WHERE id = 5;
Symptoms: Error when creating or updating user with existing username/email
Solutions:
Check existing users:
./vendor/bin/neuron cms:user:list
Find conflicting user:
$existingUser = $repository->findByUsername('johndoe');
$existingUser = $repository->findByEmail('[email protected]');
Delete or rename conflicting user
Symptoms: cms:user:create command not available
Solutions:
Verify CMS installation:
./vendor/bin/neuron cms:install
Check command registration:
// In cms/src/Cms/Cli/Provider.php
// Ensure commands are registered
Reinstall CMS package:
composer remove neuron-php/cms
composer require neuron-php/cms:^0.8