Skip to main content

Overview

LaraCMS provides comprehensive user profile management with avatar uploads using Spatie Media Library, profile information updates, and email verification. Users can manage their profiles through dedicated Livewire components.

User Model Configuration

The User model integrates media handling at app/Models/User.php:17:
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class User extends Authenticatable implements MustVerifyEmail, HasMedia
{
    use HasFactory, HasRoles, Notifiable, InteractsWithMedia;
}
The InteractsWithMedia trait enables avatar uploads and media management.

Profile Attributes

Fillable Fields

protected $fillable = [
    'name',
    'email',
    'password',
];

Hidden Fields

protected $hidden = [
    'password',
    'remember_token',
];
Password and remember_token are always hidden from JSON serialization to prevent security leaks.

Field Casts

protected function casts(): array
{
    return [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];
}
Passwords are automatically hashed when assigned to the model.

Avatar Management

LaraCMS uses Spatie Media Library for avatar uploads with automatic image optimization.

Media Collection Configuration

Avatars are defined as a single-file collection at app/Models/User.php:68:
public function registerMediaCollections(): void
{
    $this->addMediaCollection('avatars')->singleFile();
}
The singleFile() method ensures only one avatar per user - uploading a new avatar automatically deletes the previous one.

Image Conversions

Avatars are automatically resized for optimal performance at app/Models/User.php:74:
use Spatie\Image\Enums\Fit;

public function registerMediaConversions(?Media $media = null): void
{
    $this
        ->addMediaConversion('preview')
        ->fit(Fit::Contain, 300, 300)
        ->nonQueued();
}
This creates a 300x300px preview version while maintaining aspect ratio.
The nonQueued() method processes images synchronously for immediate availability. For large-scale applications, remove this to queue image processing.

Uploading Avatars

// Add avatar
$user->addMedia($request->file('avatar'))
    ->toMediaCollection('avatars');

// Get avatar URL
$avatarUrl = $user->getFirstMediaUrl('avatars');

// Get preview conversion URL
$previewUrl = $user->getFirstMediaUrl('avatars', 'preview');

// Check if user has avatar
if ($user->hasMedia('avatars')) {
    // User has uploaded an avatar
}

// Delete avatar
$user->clearMediaCollection('avatars');

Avatar Display in Blade

@if($user->hasMedia('avatars'))
    <img src="{{ $user->getFirstMediaUrl('avatars', 'preview') }}" 
         alt="{{ $user->name }}">
@else
    <div class="avatar-initials">
        {{ $user->initials() }}
    </div>
@endif

User Initials

For users without avatars, LaraCMS generates initials at app/Models/User.php:59:
public function initials(): string
{
    return Str::of($this->name)
        ->explode(' ')
        ->map(fn(string $name) => Str::of($name)->substr(0, 1))
        ->implode('');
}
This method extracts the first letter of each word in the user’s name.

Examples

  • “John Doe” → “JD”
  • “Jane Smith Johnson” → “JSJ”
  • “Alice” → “A”
Use initials as a fallback when avatars aren’t uploaded to provide a personalized user experience.

Profile Updates

Users can update their profile information through the Settings profile component.

Profile Update Component

The update logic is in app/Livewire/Settings/Profile.php:29:
public function updateProfileInformation(): void
{
    $user = Auth::user();

    $validated = $this->validate([
        'name' => ['required', 'string', 'max:255'],
        'email' => [
            'required',
            'string',
            'lowercase',
            'email',
            'max:255',
            Rule::unique(User::class)->ignore($user->id),
        ],
    ]);

    $user->fill($validated);

    if ($user->isDirty('email')) {
        $user->email_verified_at = null;
    }

    $user->save();

    $this->dispatch('profile-updated', name: $user->name);
}

Email Change Handling

When a user changes their email address, email_verified_at is set to null, requiring them to verify their new email address. This security measure prevents account takeover via email changes.

Re-verification Trigger

public function resendVerificationNotification(): void
{
    $user = Auth::user();

    if ($user->hasVerifiedEmail()) {
        $this->redirectIntended(default: route('profile', absolute: false));
        return;
    }

    $user->sendEmailVerificationNotification();

    Session::flash('status', 'verification-link-sent');
}

Profile Routes

Profile access is protected by authentication middleware:
Route::prefix('profile')
    ->middleware('auth', 'auth.session')
    ->group(function () {
        Route::get('/', [ProfileController::class, 'profile'])->name('profile');
    });

Middleware Protection

  • auth - Ensures user is authenticated
  • auth.session - Validates session integrity

Frontend Profile Component

The frontend profile display uses app/Livewire/Frontend/Profile.php:9:
class Profile extends Component
{
    public $user;
    public $activeTab = 'profile';

    public function mount()
    {
        $this->user = Auth::user();
    }

    public function setTab($tab)
    {
        $this->activeTab = $tab;
    }

    public function render()
    {
        return view('livewire.frontend.profile', [
            'user' => $this->user,
        ]);
    }
}
This component supports tabbed navigation for different profile sections.

User Relationships

Links/URLs

Users can have multiple shortened URLs at app/Models/User.php:83:
public function links()
{
    return $this->hasMany(Link::class);
}
This enables user-specific URL shortening and tracking.
// Get all user links
$links = $user->links;

// Get links count
$linkCount = $user->links()->count();

// Get recent links
$recentLinks = $user->links()->latest()->take(5)->get();

Profile Data Access

In Livewire Components

use Illuminate\Support\Facades\Auth;

public function mount()
{
    $this->name = Auth::user()->name;
    $this->email = Auth::user()->email;
}

In Blade Templates

@auth
    <p>Welcome, {{ auth()->user()->name }}!</p>
    <p>Email: {{ auth()->user()->email }}</p>
    
    @if(auth()->user()->hasVerifiedEmail())
        <span class="badge badge-success">Verified</span>
    @else
        <span class="badge badge-warning">Not Verified</span>
    @endif
@endauth

In Controllers

public function show()
{
    $user = auth()->user();
    
    return view('profile', [
        'user' => $user,
        'avatarUrl' => $user->getFirstMediaUrl('avatars', 'preview'),
        'initials' => $user->initials(),
    ]);
}

Complete Profile Example

Here’s a complete example of profile management:
use App\Models\User;
use Illuminate\Support\Facades\Auth;

class ProfileController
{
    public function update(Request $request)
    {
        $user = Auth::user();
        
        // Validate input
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email,' . $user->id,
            'avatar' => 'nullable|image|max:2048', // 2MB max
        ]);
        
        // Update profile
        $user->update([
            'name' => $validated['name'],
            'email' => $validated['email'],
        ]);
        
        // Handle email change
        if ($user->wasChanged('email')) {
            $user->email_verified_at = null;
            $user->save();
            $user->sendEmailVerificationNotification();
        }
        
        // Handle avatar upload
        if ($request->hasFile('avatar')) {
            $user->addMedia($request->file('avatar'))
                ->toMediaCollection('avatars');
        }
        
        return redirect()->route('profile')
            ->with('success', 'Profile updated successfully!');
    }
    
    public function deleteAvatar()
    {
        $user = Auth::user();
        $user->clearMediaCollection('avatars');
        
        return back()->with('success', 'Avatar deleted successfully!');
    }
}

Validation Rules

Name Validation

'name' => ['required', 'string', 'max:255']

Email Validation

'email' => [
    'required',
    'string',
    'lowercase', // Ensures email is stored in lowercase
    'email',
    'max:255',
    Rule::unique(User::class)->ignore($user->id), // Allows user to keep their current email
]

Avatar Validation

'avatar' => [
    'nullable',
    'image', // Must be image file
    'mimes:jpeg,jpg,png,gif', // Allowed formats
    'max:2048', // Max 2MB
    'dimensions:min_width=100,min_height=100', // Minimum dimensions
]
File Upload Security:
  • Always validate file types and sizes
  • Store uploaded files outside the public directory
  • Use Spatie Media Library for automatic security measures
  • Set maximum file sizes to prevent storage abuse

Media Library Configuration

Spatie Media Library handles file storage, organization, and conversions automatically.

Storage Organization

Media files are stored with the following structure:
storage/app/public/
└── [user_id]/
    └── avatars/
        ├── original.jpg
        └── conversions/
            └── preview.jpg

Accessing Media

// Get media model
$media = $user->getFirstMedia('avatars');

// Get file information
$fileName = $media->file_name;
$fileSize = $media->size; // in bytes
$mimeType = $media->mime_type;

// Get URLs
$url = $media->getUrl();
$previewUrl = $media->getUrl('preview');

// Download
$path = $media->getPath();
$content = file_get_contents($path);

Events and Notifications

Profile Updated Event

The profile component dispatches an event after updates:
$this->dispatch('profile-updated', name: $user->name);
Listen for this event in other Livewire components:
protected $listeners = ['profile-updated' => 'handleProfileUpdate'];

public function handleProfileUpdate($name)
{
    // Update UI with new name
    $this->userName = $name;
}

Email Verification Notification

When email changes, verification notification is sent:
$user->sendEmailVerificationNotification();

Best Practices

Profile Management Tips:
  • Always require email verification after email changes
  • Use image conversions for different display sizes
  • Provide fallback initials when avatars aren’t uploaded
  • Validate and sanitize all user input
  • Use single-file media collections for avatars to prevent storage bloat
  • Process large images asynchronously with queues
  • Implement rate limiting for profile updates
Security Considerations:
  • Never expose sensitive fields (password, remember_token) in API responses
  • Require current password verification before allowing email changes
  • Implement CSRF protection on all profile update forms
  • Sanitize file names and validate file types
  • Set appropriate file size limits
  • Store uploaded files outside the web root

Next Steps

Authentication

Learn about user authentication and registration flows

Roles & Permissions

Understand role-based access control with Spatie Permission

Build docs developers (and LLMs) love