Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save eduardopintor/c0dc4bf8e6f859095b9859304d97636c to your computer and use it in GitHub Desktop.
Save eduardopintor/c0dc4bf8e6f859095b9859304d97636c to your computer and use it in GitHub Desktop.
Basic Twill & Laravel Breeze integration

Manage frontend user profiles from Twill

This is a simple solution to allow site administrators to manage frontend user profiles from the CMS. It can be used as a starting point to implement a user approval workflow and content access restrictions.

Objectives:

  • Add a Profiles section to the CMS for site administrators
  • Automatically create and assign Profile records to users upon registration

Requirements:

Versions used at the time of writing:

Version
PHP 7.4
Laravel 8
Laravel Breeze 1.3
Twill 2.4

Create the profiles module

This module will be used to attach extra information to User records:

php artisan twill:make:module profiles

Edit the migration

Update the generated migration to add the required fields:

    public function up()
    {
        Schema::create('profiles', function (Blueprint $table) {
            createDefaultTableFields($table);

            // Rename the 'title' field to 'name'
            $table->string('name', 200)->nullable();

            // Keep the description
            $table->text('description')->nullable();

            // Example boolean field to allow access to special content
            $table->boolean('is_vip')->default(false);

            // Add a foreign key to the `users` table from Laravel Breeze
            $table->foreignId('user_id')
                ->constrained()
                ->onUpdate('cascade')
                ->onDelete('cascade');
        });
    }

Then, run the migration:

php artisan migrate

Edit the Profile model

Edit the fillable fields to match the new schema:

    protected $fillable = [
        'published',
        'name',
        'description',
        'is_vip',
    ];

Then, define the Profile—User relationship:

    public function user()
    {
        return $this->belongsTo(User::class);
    }

Edit the User model

Define the User—Profile relationship:

    public function profile()
    {
        return $this->hasOne(Profile::class);
    }

Use the model's booted() method to hook into the created event. When a user is created through the Laravel Breeze user registration form, automatically create and assign a Profile record:

    protected static function booted()
    {
        static::created(function ($user) {
            // Create the user profile
            $profile = Profile::make();
            $profile->name = $user->name;
            $profile->user_id = $user->id;
            $profile->save();

            // The `name` value is transferred to the `profile` record
            $user->name = '';
            $user->save();
        });
    }

Then, define the name attribute accessor. This will allow existing Laravel Breeze code to access the user name from the attached profile:

    public function getNameAttribute()
    {
        return $this->profile->name;
    }

Edit ProfileController

    // Define our custom `name` column to be used instead of the default `title`
    protected $titleColumnKey = 'name';

    // Prevent administrators from creating and deleting profiles in the CMS
    protected $indexOptions = [
        'create' => false,
        'delete' => false,
    ];

Edit the profile form

    @section('contentFields')
        @formField('input', [
            'type' => 'textarea',
            'name' => 'description',
            'label' => 'Description',
            'maxlength' => 1000,
        ])

        @formField('checkbox', [
            'name' => 'is_vip',
            'label' => 'Can access all VIP content',
        ])
    @stop

Edit ProfileRequest

    public function rulesForUpdate()
    {
        return ['name' => 'required'];
    }

Finishing touches

Add the module to your twill-navigation.php and to your admin.php routes — and you are done!

Where to go from here?

Site administrators now have access to a Profiles section in the CMS, to edit basic user information.

Within your site's views and controllers, you can access the profile information for the current user via Auth::user()->profile.

User approval & restricted content

Upon registration, a user profile is created with a draft status (ie. not published). This can be used to implement a user approval workflow:

    @if (Auth::user()->profile->published)
        {{-- Account has been approved, show the dashboard --}}
    @else
        {{-- Account has not yet been approved, show "pending approval" message --}}
    @endif

The same technique also applies for granular access control (e.g. a VIP section with additional content).

Frontend profile editing

A frontend form can be added to allow users to edit their descriptions. As always, make sure to sanitize user input before storing it in the database.

Complete user management from the CMS

For simplicity, this implementation prevents site administrators from creating and deleting users from the CMS.

A few methods from ModuleRepository can be extended in ProfileRepository to implement the feature:



Thanks for reading and have fun :)
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateProfilesTables extends Migration
{
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
createDefaultTableFields($table);
$table->string('name', 200)->nullable();
$table->text('description')->nullable();
$table->boolean('is_vip')->default(false);
$table->foreignId('user_id')
->constrained()
->onUpdate('cascade')
->onDelete('cascade');
});
}
public function down()
{
Schema::dropIfExists('profiles');
}
}
@extends('twill::layouts.form')
@section('contentFields')
@formField('input', [
'type' => 'textarea',
'name' => 'description',
'label' => 'Description',
'maxlength' => 1000,
])
@formField('checkbox', [
'name' => 'is_vip',
'label' => 'Can access all VIP content',
])
@stop
<?php
namespace App\Models;
use A17\Twill\Models\Model;
class Profile extends Model
{
protected $fillable = [
'published',
'name',
'description',
'is_vip',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
<?php
namespace App\Http\Controllers\Admin;
use A17\Twill\Http\Controllers\Admin\ModuleController;
class ProfileController extends ModuleController
{
protected $moduleName = 'profiles';
protected $titleColumnKey = 'name';
protected $indexOptions = [
'create' => false,
'delete' => false,
];
}
<?php
namespace App\Repositories;
use A17\Twill\Repositories\ModuleRepository;
use App\Models\Profile;
class ProfileRepository extends ModuleRepository
{
public function __construct(Profile $model)
{
$this->model = $model;
}
}
<?php
namespace App\Http\Requests\Admin;
use A17\Twill\Http\Requests\Admin\Request;
class ProfileRequest extends Request
{
public function rulesForCreate()
{
return [];
}
public function rulesForUpdate()
{
return [
'name' => 'required'
];
}
}
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
public function profile()
{
return $this->hasOne(Profile::class);
}
protected static function booted()
{
static::created(function ($user) {
$profile = Profile::make();
$profile->name = $user->name;
$profile->user_id = $user->id;
$profile->save();
$user->name = '';
$user->save();
});
}
public function getNameAttribute()
{
return $this->profile->name;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment