User Management Guide

⚠️ 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.

Overview

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.

Architecture

Core Components

User management consists of the following classes:

User Events

The CMS uses an event-driven architecture built on the Neuron Events component. Events allow decoupled communication between different parts of the system.

Event Classes

User lifecycle events are located in Neuron\Cms\Events\User\:

Listening to Events

Create 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 ],
] );

Emitting Events

Events are automatically emitted by service classes:

// In UserCreator service
$user = $this->repository->create( $userData );
$this->emitter->emit( new UserCreatedEvent( $user, $userData ) );

User Model

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
}

ORM Attributes

The CMS models use the following Neuron ORM attributes:

User Properties

The 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)

User Roles

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):

  1. Admin
  2. Editor
  3. Author
  4. Subscriber

User Statuses

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

User Model Methods

// 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

User Repository

IUserRepository Interface

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;
}

DatabaseUserRepository

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'
}

User Services

Creator Service

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:

  1. Password validation (checks all configured requirements)
  2. Password hashing (uses Argon2id if available, otherwise Bcrypt)
  3. Status set to active
  4. Email verified set to true
  5. Created timestamp set
  6. UserCreatedEvent emitted

Updater Service

Updates 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:

  1. Password validation if provided (checks all configured requirements)
  2. Password hashing if provided
  3. Timezone update if provided
  4. Updated timestamp set
  5. UserUpdatedEvent emitted

Deleter Service

Safely 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:

  1. User existence verification
  2. User deletion from database
  3. UserDeletedEvent emitted

CLI User Management

cms:user:create

Create 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

cms:user:list

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.

cms:user:delete

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.

cms:user:reset-password

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:

Examples:

# 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

Web-Based User Management

Admin Users Controller

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

List Users

Route: GET /admin/users

Method: index()

Displays all users in a table with links to edit/delete.

View Data:

View: cms/resources/views/admin/users/index.php

Create User

Route: GET /admin/users/create

Method: create()

Displays user creation form with role selection.

View Data:

View: cms/resources/views/admin/users/create.php

Form Submission: POST /admin/users

Method: store()

Required Fields:

Validation:

Success: Redirects to user list with success message

Failure: Redirects back to form with error message

Edit User

Route: GET /admin/users/{id}/edit

Method: edit(int $id)

Displays user edit form with current values.

View Data:

View: cms/resources/views/admin/users/edit.php

Form Submission: POST /admin/users/{id}

Method: update(int $id)

Fields:

Validation:

Success: Redirects to user list with success message

Failure: Redirects back to form with error message

Delete User

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'] );
}

Programmatic User Management

Creating Users

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 );

Updating Users

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 );

Deleting Users

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 );

Querying Users

// 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

Listening to User Events

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.

Role Management

Assigning Roles

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 );

Checking Roles

// 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
}

Role Hierarchy

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' );
    }
}

Status Management

Setting Account Status

// 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 );

Checking Status

if ($user->isActive()) {
    // Normal access
}

if ($user->isSuspended()) {
    // Account suspended
}

if ($user->getStatus() === UserStatus::Inactive) {
    // Account inactive
}

Status Enforcement

Status checks should be performed during authentication:

// In AuthManager
if (!$user->isActive()) {
    return false; // Deny login
}

Account Lockout Management

Manual Unlock

// 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;

Check Lockout Status

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.

Email Verification Management

Setting Verification Status

// 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.

Best Practices

Service Layer Usage

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:

Password Management

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 ));
}

Duplicate Prevention

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' );
}

Self-Deletion Prevention

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 );

Event Listeners

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.

Troubleshooting

User Creation Fails

Symptoms: Error when creating user

Solutions:

  1. Check password requirements:

    $errors = $passwordHasher->getValidationErrors( $password );
    var_dump( $errors );
    
  2. Check for duplicate username/email:

    # Via CLI
    ./vendor/bin/neuron cms:user:list
    
    # Via database
    SELECT username, email FROM users WHERE username = 'johndoe';
    
  3. Check database connection:

    # Verify database settings in config/neuron.yaml
    database:
      adapter: mysql
      host: localhost
      name: cms_database
      user: cms_user
    

Cannot Login After Creation

Symptoms: User created but cannot login

Solutions:

  1. Check account status:

    if (!$user->isActive()) {
        $user->setStatus( UserStatus::Active );
        $repository->update( $user );
    }
    
  2. Check account lockout:

    if ($user->isLockedOut()) {
        $user->resetFailedLoginAttempts();
        $repository->update( $user );
    }
    
  3. Verify password hash:

    // Ensure password was hashed correctly
    $hasher = new PasswordHasher();
    if ($hasher->verify( $password, $user->getPasswordHash())) {
        echo "Password correct";
    }
    

Self-Deletion Error

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;

Duplicate Username/Email

Symptoms: Error when creating or updating user with existing username/email

Solutions:

  1. Check existing users:

    ./vendor/bin/neuron cms:user:list
    
  2. Find conflicting user:

    $existingUser = $repository->findByUsername('johndoe');
    $existingUser = $repository->findByEmail('[email protected]');
    
  3. Delete or rename conflicting user

CLI Command Not Found

Symptoms: cms:user:create command not available

Solutions:

  1. Verify CMS installation:

    ./vendor/bin/neuron cms:install
    
  2. Check command registration:

    // In cms/src/Cms/Cli/Provider.php
    // Ensure commands are registered
    
  3. Reinstall CMS package:

    composer remove neuron-php/cms
    composer require neuron-php/cms:^0.8
    

Additional Resources