Chú ý: Để không bị bắt lỗi addIf của menu thì chúng ta tạo điều kiện để người dùng yêu cầu cần phải đăng nhập và xác thực trước khi có thể sử dụng Spatie\Menu\Menu::addIf(): Argument #1 ($condition) must be of type callable|bool, null given, called in C:\xampp82\htdocs\lva8\app\Providers\ViewServiceProvider.php on line 45
Bỏ mã này đi: C:\xampp82\htdocs\lva8\resources\views\layouts\app.blade.php
<?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\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Spatie\Menu\Html;
use Spatie\Menu\Laravel\Menu;
use Spatie\Menu\Link;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->bootMenu();
}
private function bootMenu(): void
{
Menu::macro('app', function () {
return Menu::new()
->addClass('nav navbar-nav pull-xs-right')
->add(Link::to(route('home'), 'Home'));
});
}
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Model>
*/
class PostFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$detail = $this->faker->text(15);
// $slug = str_slug($detail, '-');
$slug = Str::slug($detail, '-');
return [
'detail' => $detail,
'slug' => $slug
];
}
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('detail');
$table->string('slug');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use App\Models\User;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
User::factory(10)->create();
$this->call(PostSeeder::class);
}
}
<?php
namespace Database\Seeders;
use App\Models\Post;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class PostSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Post::factory()->count(50)->create();
}
}
<?php
namespace App\Providers;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Spatie\Menu\Laravel\Facades\Menu;
use Spatie\Menu\Link;
class ViewServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
Blade::if('role', function ($roles) {
return auth()->user()->hasAnyRole(Arr::wrap($roles));
});
Blade::if('roles', function ($roles) {
return auth()->user()->hasAllRole(Arr::wrap($roles));
});
$this->buildSidebarMenu();
}
/**
* Build sidebar menu using Spatie Menu.
*
* @return \Spatie\Menu\Laravel\Menu
*/
private function buildSidebarMenu()
{
return Menu::macro('sidebar', function () {
return Menu::new()
->setWrapperTag('aside')
->withoutParentTag()
->add(Link::to(route('home'), 'Home'));
});
}
}
<?php
namespace App\Providers;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Spatie\Menu\Laravel\Facades\Menu;
use Spatie\Menu\Link;
class ViewServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
Blade::if('role', function ($roles) {
return auth()->user()->hasAnyRole(Arr::wrap($roles));
});
Blade::if('roles', function ($roles) {
return auth()->user()->hasAllRole(Arr::wrap($roles));
});
$this->buildSidebarMenu();
}
/**
* Build sidebar menu using Spatie Menu.
*
* @return \Spatie\Menu\Laravel\Menu
*/
private function buildSidebarMenu()
{
return Menu::macro('sidebar', function () {
return Menu::new()
->setWrapperTag('aside')
->withoutParentTag()
->addClass('menu')
->add(Link::to(route('home'), 'Home'))
->add(
Menu::new()
->prepend('<p class="menu-label">Public 1</p>')
->addClass('menu-list')
->route('home', 'Public 2')
->route('post', 'Public 3')
->setActiveClass('is-active')
->setActiveClassOnLink()
->setActiveFromRequest()
);
});
}
}
{!! Menu::sidebar() !!}
<?php
use App\Http\Controllers\HomeController;
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Backend\DashboardController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', [HomeController::class, 'index'])->name('home');
Route::middleware(['auth'])->group(function () {
Route::prefix('backend')->name('backend.')->group(function () {
Route::get('/', [DashboardController::class, 'index'])->name('index');
Route::get('/post', [DashboardController::class, 'post'])->name('post');
});
});
<?php
namespace App\Concerns;
use App\Models\Role;
trait HasRoles
{
/**
* Relation to roles that the user has.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
/**
* Scope the query to get only users with role of curator.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeAdmins($query)
{
return $query->whereHas('roles', function ($query) {
return $query->where('name', 'admin');
});
}
/**
* Scope the query to get only users with role of curator.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeCurators($query)
{
return $query->whereHas('roles', function ($query) {
return $query->where('name', 'curator');
});
}
/**
* Get names of roles the user has.
*
* @return \Illuminate\Support\Collection
*/
protected function roleNames()
{
return $this->roles->pluck('name');
}
/**
* Check if user has given role.
*
* @param string $role
* @return bool
*/
public function hasRole($role)
{
return $this->roleNames()->contains($role);
}
/**
* Check if user has one or more of the given roles.
*
* @param array $roles
* @return bool
*/
public function hasAnyRole(array $roles)
{
return $this->roleNames()->intersect($roles)->isNotEmpty();
}
/**
* Check if user has all the given roles.
*
* @param array $roles
* @return bool
*/
public function hasAllRoles(array $roles)
{
return count($roles) === $this->roleNames()->intersect($roles)->count();
}
/**
* Assign given roles to user.
*
* @param array|string $roles
* @return $this
*/
public function assignRoles($roles)
{
if (is_string($roles)) {
$roles = func_get_args();
}
$this->roles()->attach(Role::findAllByName($roles));
return $this->load('roles');
}
/**
* Revoke given roles to user.
*
* @param array|string $roles
* @return $this
*/
public function revokeRoles($roles)
{
if (is_string($roles)) {
$roles = func_get_args();
}
$this->roles()->detach(Role::findAllByName($roles));
return $this->load('roles');
}
}
<?php
namespace App\Http\Controllers\Backend;
class DashboardController
{
/**
* Show dashboard.
*
* @return \Illuminate\View\View
*/
public function index()
{
$user = auth()->user();
return view('backend.index', [
'variable' => $user
]);
}
public function post()
{
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
}
public function post()
{
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'detail',
'slug'
];
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
/**
* Find role by it's name.
*
* @param string $name
* @return self
*/
public static function findByName($name)
{
return static::where('name', $name)->first();
}
/**
* Find all roles that have given names.
*
* @param array|string $names
* @return \Illuminate\Database\Eloquent\Collection
*/
public static function findAllByName($names)
{
return static::whereIn('name', $names)->get();
}
}
<?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\Sanctum\HasApiTokens;
use App\Concerns\HasRoles;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
<?php
namespace App\Providers;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Spatie\Menu\Laravel\Facades\Menu;
use Spatie\Menu\Link;
class ViewServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
// dd(auth()->user());
// Cần xử lý bước vào đây cần phải đăng nhập
$this->buildSidebarMenu();
}
/**
* Build sidebar menu using Spatie Menu.
*
* @return \Spatie\Menu\Laravel\Menu
*/
private function buildSidebarMenu()
{
return Menu::macro('sidebar', function () {
return Menu::new()
->setWrapperTag('aside')
->withoutParentTag()
->addClass('menu')
->add(Link::to(route('home'), 'Home'))
->add(
Menu::new()
->prepend('<p class="menu-label">Backend</p>')
->addClass('menu-list')
->route('backend.index', 'Backend index')
->route('backend.post', 'Backend post')
->setActiveClass('is-active')
->setActiveClassOnLink()
->setActiveFromRequest()
)
->addIf(
optional(auth()->user())->hasAnyRole(['admin', 'curator']),
Menu::new()
->prepend('<p class="menu-label">Test</p>')
->addClass('menu-list')
->setActiveClass('is-active')
->setActiveClassOnLink()
->setActiveFromRequest()
);
});
}
}
/**
* Dev customization
*/
App\Providers\ViewServiceProvider::class,
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Model>
*/
class PostFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$detail = $this->faker->text(15);
// $slug = str_slug($detail, '-');
$slug = Str::slug($detail, '-');
return [
'detail' => $detail,
'slug' => $slug
];
}
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Role>
*/
class RoleFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Role::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'slug' => $this->faker->unique()->word(),
];
}
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('detail');
$table->string('slug');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('roles');
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('role_user', function (Blueprint $table) {
$table->unsignedInteger('role_id');
$table->unsignedInteger('user_id');
$table->primary(['role_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('role_user');
}
};
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use App\Models\User;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
// $this->call(PostSeeder::class);
$this->call(RoleSeeder::class);
$this->call(UserSeeder::class);
}
}
<?php
namespace Database\Seeders;
use App\Models\Post;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class PostSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Post::factory()->count(50)->create();
}
}
<?php
namespace Database\Seeders;
use App\Models\Role;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class RoleSeeder extends Seeder
{
protected $roles = [
'admin',
'curator',
];
/**
* Run the database seeds.
*/
public function run(): void
{
foreach ($this->roles as $role) {
Role::firstOrCreate(['name' => $role]);
}
}
}
<?php
namespace Database\Seeders;
use App\Models\Role;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$admin = Role::where('name', 'admin')->first();
$curator = Role::where('name', 'curator')->first();
User::factory()->create([
'email' => 'nen.zivanovic@gmail.com',
])->roles()->sync([$admin->id, $curator->id]);
User::factory()->create([
'email' => 'admin@example.com',
])->roles()->sync([$admin->id]);
User::factory()->create([
'email' => 'curator@example.com',
])->roles()->sync([$curator->id]);
User::factory()->create([
'email' => 'member@example.com',
]);
}
}