Build a Simple - Yet Extendable CMS with Laravel & Backpack

In this tutorial (or series) depending on how much I cover, we will install Laravel, Backpack & setup a simple CMS system to manage your content.

I am not one to drabble on so lets get to the meat of it:
Laravel has a few ways of being deployed on your development machine: and we will leave it to you to pick your method: - see here for documentation on this

Under the assumption that you are using Sail

curl -s "https://laravel.build/lcms" | bash
cd example-app
./vendor/bin/sail up

Now once it is up let us install some of the tools we want to use

sail composer require spatie/laravel-permission laravel/jetstream backpack/crud 
sail composer require --dev barryvdh/laravel-ide-helper nunomaduro/phpinsights backpack/generators laracasts/generators jeroen-g/laravel-packager
sail artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
sail artisan jetstream:install livewire
sail artisan vendor:publish --tag=jetstream-views
sail artisan backpack:install
npm install && npm run dev

This will install permission, authentication and backpack for the admin panel.

next add to your composer.json file the following in scripts

"scripts": {
    "post-update-cmd": [
        "Illuminate\\Foundation\\ComposerScripts::postUpdate",
        "@php artisan ide-helper:generate",
        "@php artisan ide-helper:meta"
    ]
},

Next let us create a seeder for our roles:

first edit app/Models/User and add

<?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;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles; //--> this line

class User extends Authenticatable
{
    use HasApiTokens;
    use HasFactory;
    use HasProfilePhoto;
    use Notifiable;
    use TwoFactorAuthenticatable;
    use HasRoles; //-> and this line

then add a new file at database/seeders/RolesSeeder.php with the following content:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\PermissionRegistrar;

class RolesPermissionsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        app()[PermissionRegistrar::class]->forgetCachedPermissions();

        Permission::create(['name' => 'users']);

        Role::create(['name' => 'Developer']);
        Role::create(['name' => 'Admin'])->givePermissionTo(['users']);
        Role::create(['name' => 'User']);
    }
}

Next lets override the artisan command for backpack to create our user.

add a new file at app/Console/Commands/CreateBackpackUser.php

<?php

namespace App\Console\Commands;

use Backpack\CRUD\app\Console\Commands\CreateUser;

class CreateBackpackUser extends CreateUser
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'backpack:user
                            {--N|name= : The name of the new user}
                            {--E|email= : The user\'s email address}
                            {--P|password= : User\'s password}
                            {--encrypt=true : Encrypt user\'s password if it\'s plain text ( true by default )}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a new user';

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->info('Creating a new user');

        if (! $name = $this->option('name')) {
            $name = $this->ask('Name');
        }

        if (! $email = $this->option('email')) {
            $email = $this->ask('Email');
        }

        if (! $password = $this->option('password')) {
            $password = $this->secret('Password');
        }

        if ($this->option('encrypt')) {
            $password = bcrypt($password);
        }

        $auth = config('backpack.base.user_model_fqn', 'App\User');
        $user = new $auth();
        $user->name = $name;
        $user->email = $email;
        $user->password = $password;

        $user->assignRole("Admin");

        if ($user->save()) {
            $this->info('Successfully created new user');
        } else {
            $this->error('Something went wrong trying to save your user');
        }
    }
}

and lets configure backpack to use our admin user:

edit app/Http/Middleware/CheckIfAdmin.php and adjust the function checkIfUserIsAdmin as follows

private function checkIfUserIsAdmin($user)
    {
        return $user->hasAnyRole('Admin', 'Developer');
    }

You should now be able to get started and create a user by running

sail artisan migrate:fresh --seed
sail artisan backpack:user

and now you should be able to view the site at http://localhost and http://localhost/admin to login to the admin panel.

Backpack has an addons that we can use to manage users & roles - add it by doing the following:

sail composer require backpack/permissionmanager

edit your User model:

<?php

namespace App\Models;

use Backpack\CRUD\app\Models\Traits\CrudTrait; //-->add this
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasApiTokens;
    use HasFactory;
    use HasProfilePhoto;
    use Notifiable;
    use TwoFactorAuthenticatable;
    use HasRoles;
    use CrudTrait;//-->add this

then edit resources/views/vendor/backpack/base/inc/sidebar_content.blade.php and add the following

<!-- Users, Roles, Permissions -->
@if(backpack_user()->hasanyrole('Admin|Developer'))
<li class="nav-item nav-dropdown">
    <a class="nav-link nav-dropdown-toggle" href="#"><i class="nav-icon la la-users"></i> Authentication</a>
    <ul class="nav-dropdown-items">
        <li class="nav-item"><a class="nav-link" href="{{ backpack_url('user') }}"><i class="nav-icon la la-user"></i>
                <span>Users</span></a></li>
        <li class="nav-item"><a class="nav-link" href="{{ backpack_url('role') }}"><i
                    class="nav-icon la la-id-badge"></i> <span>Roles</span></a></li>
        <li class="nav-item"><a class="nav-link" href="{{ backpack_url('permission') }}"><i
                    class="nav-icon la la-key"></i> <span>Permissions</span></a></li>
    </ul>
</li>
@endif

we will most likely re-address this later to use the @can permission instead.

Now with the system running lets start with our Addon - See Part 2