😄Package spatie/menu full phần 1 (ok)

https://spatie.be/docs/menu/v3/introduction

Ví dụ để tham khảo full https://github.com/spatie/spatie.be

Ví dụ 1: Chủ yếu biết cách sử dụng cơ bản

C:\xampp82\htdocs\lva1\routes\web.php

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('/about', [App\Http\Controllers\HomeController::class, 'index'])->name('about');
Route::get('/contact', [App\Http\Controllers\HomeController::class, 'index'])->name('contact');

C:\xampp82\htdocs\lva1\app\Providers\AppServiceProvider.php

public function boot(): void
  {
    Menu::macro('main', function () {
      return Menu::new()
        ->route('home', 'Home')
        ->route('about', 'About')
        ->route('contact', 'Contact');
    });
  }

C:\xampp82\htdocs\lva1\resources\views\welcome.blade.php

<header>
    {!! Menu::main() !!}
</header>

Ví dụ 2: Ta đi nghiên cứu dùng cách xác thực kết hợp với menu

Ví dụ 2.1: Kết hợp bảng role_user, roles, users

C:\xampp82\htdocs\lva3\database\migrations\2017_12_27_235046_create_roles_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRolesTable extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('roles', function (Blueprint $table) {
      $table->increments('id');
      $table->string('name')->unique();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('roles');
  }
}

C:\xampp82\htdocs\lva3\database\migrations\2014_10_12_000000_create_users_table.php

<?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();
      $table->string('deleted_at')->nullable();
    });
  }
  /**
   * Reverse the migrations.
   */
  public function down(): void
  {
    Schema::dropIfExists('users');
  }
};

C:\xampp82\htdocs\lva3\database\migrations\2017_12_27_235904_create_role_user_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRoleUserTable extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('role_user', function (Blueprint $table) {
      $table->unsignedInteger('role_id');
      $table->unsignedInteger('user_id');
      $table->primary(['role_id', 'user_id']);
      // $table->foreign('role_id')
      //   ->references('id')
      //   ->on('roles')
      //   ->onDelete('cascade');
      // $table->foreign('user_id')
      //   ->references('id')
      //   ->on('users')
      //   ->onDelete('cascade');
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('role_user');
  }
}

C:\xampp82\htdocs\lva3\database\factories\UserFactory.php

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

C:\xampp82\htdocs\lva3\database\factories\RoleFactory.php

<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
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
   */
  public function definition()
  {
    return [
      'slug' => $this->faker->unique()->word(),
    ];
  }
}

C:\xampp82\htdocs\lva3\database\seeders\DatabaseSeeder.php

<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
  /**
   * Seed the application's database.
   */
  public function run(): void
  {
    $this->call(RolesTableSeeder::class);
    $this->call(UsersTableSeeder::class);
  }
}

C:\xampp82\htdocs\lva3\database\seeders\UsersTableSeeder.php

<?php
namespace Database\Seeders;
use App\Models\Role;
use App\Models\User;
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
  /**
   * Run the database seeds.
   *
   * @return void
   */
  public function run()
  {
    $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',
    ]);
  }
}

C:\xampp82\htdocs\lva3\database\seeders\RolesTableSeeder.php

<?php
namespace Database\Seeders;
use App\Models\Role;
use Illuminate\Database\Seeder;
class RolesTableSeeder extends Seeder
{
  protected $roles = [
    'admin',
    'curator',
  ];
  /**
   * Run the database seeds.
   *
   * @return void
   */
  public function run()
  {
    foreach ($this->roles as $role) {
      Role::firstOrCreate(['name' => $role]);
    }
  }
}

C:\xampp82\htdocs\lva3\app\Models\User.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;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Concerns\HasRoles;
class User extends Authenticatable
{
  use HasApiTokens, HasFactory, Notifiable, SoftDeletes, 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',
  ];
}

C:\xampp82\htdocs\lva3\app\Models\Role.php

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

C:\xampp82\htdocs\lva3\app\Concerns\HasRoles.php

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

Ví dụ 2.2: Áp dụng role_user, roles, users

Ví dụ 2.3: Tạo tài khoản và xác thực 😀

C:\xampp82\htdocs\lva7\config\livewire.php

'layout' => 'layouts.app',

C:\xampp82\htdocs\lva7\resources\views\layouts\app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- CSRF Token -->
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <title>{{ config('app.name', 'Laravel') }}</title>
  <!-- Fonts -->
  <link rel="dns-prefetch" href="//fonts.gstatic.com">
  <!-- Scripts -->
  @vite(['resources/sass/app.scss', 'resources/js/app.js'])
</head>
<body>
  <div id="app">
    <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
      <div class="container">
        {!! Menu::app() !!}
        <a class="navbar-brand" href="{{ url('/') }}">
          {{ config('app.name', 'Laravel') }}
        </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
          aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <!-- Left Side Of Navbar -->
          <ul class="navbar-nav me-auto">
          </ul>
          <!-- Right Side Of Navbar -->
          <ul class="navbar-nav ms-auto">
            <!-- Authentication Links -->
            @guest
            @if (Route::has('login'))
            <li class="nav-item">
              <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
            </li>
            @endif
            @if (Route::has('register'))
            <li class="nav-item">
              <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
            </li>
            @endif
            @else
            <li class="nav-item dropdown">
              <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
                aria-haspopup="true" aria-expanded="false" v-pre>
                {{ Auth::user()->name }}
              </a>
              <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                  {{ __('Logout') }}
                </a>
                <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                  @csrf
                </form>
              </div>
            </li>
            @endguest
          </ul>
        </div>
      </div>
    </nav>
    <main class="py-4">
      @yield('content')
      {{ $slot }}
    </main>
  </div>
</body>
</html>

C:\xampp82\htdocs\lva7\database\factories\ArticleFactory.php

<?php
namespace Database\Factories;
use App\Models\Article;
use Illuminate\Database\Eloquent\Factories\Factory;
class ArticleFactory extends Factory
{
  /**
   * The name of the factory's corresponding model.
   *
   * @var string
   */
  protected $model = Article::class;
  /**
   * Define the model's default state.
   *
   * @return array
   */
  public function definition()
  {
    return [
      'title' => fake()->text(60),
      'body' => fake()->text(1500),
      'description' => fake()->text(150),
    ];
  }
}

C:\xampp82\htdocs\lva7\database\factories\CommentFactory.php

<?php
namespace Database\Factories;
use App\Models\Comment;
use Illuminate\Database\Eloquent\Factories\Factory;
class CommentFactory extends Factory
{
  /**
   * The name of the factory's corresponding model.
   *
   * @var string
   */
  protected $model = Comment::class;
  /**
   * Define the model's default state.
   *
   * @return array
   */
  public function definition()
  {
    return [
      'body' => fake()->text(),
      'article_id' => fake()->randomElement(range(1, 50)),
      'user_id' => fake()->randomElement(range(1, 10)),
    ];
  }
}

C:\xampp82\htdocs\lva7\database\factories\TagFactory.php

<?php
namespace Database\Factories;
use App\Models\Tag;
use Illuminate\Database\Eloquent\Factories\Factory;
class TagFactory extends Factory
{
  /**
   * The name of the factory's corresponding model.
   *
   * @var string
   */
  protected $model = Tag::class;
  /**
   * Define the model's default state.
   *
   * @return array
   */
  public function definition()
  {
    return [
      'name' => fake()->unique()->lexify('?????'),
    ];
  }
}

C:\xampp82\htdocs\lva7\database\factories\UserFactory.php

<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
  /**
   * The name of the factory's corresponding model.
   *
   * @var string
   */
  protected $model = User::class;
  /**
   * Define the model's default state.
   *
   * @return array
   */
  public function definition()
  {
    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),
      'username' => fake()->unique()->userName,
      'image' => fake()->imageUrl(),
      'bio' => fake()->text(),
    ];
  }
  /**
   * Indicate that the model's email address should be unverified.
   *
   * @return \Illuminate\Database\Eloquent\Factories\Factory
   */
  public function unverified()
  {
    return $this->state(function (array $attributes) {
      return [
        'email_verified_at' => null,
      ];
    });
  }
}

C:\xampp82\htdocs\lva7\database\migrations\2014_10_12_000000_create_users_table.php

<?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->string('username')->unique();
      $table->text('bio')->nullable();
      $table->text('image')->nullable();
      $table->rememberToken();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   */
  public function down(): void
  {
    Schema::dropIfExists('users');
  }
};

C:\xampp82\htdocs\lva7\database\migrations\2021_05_18_103101_create_articles_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('articles', function (Blueprint $table) {
      $table->id();
      $table->string('title');
      $table->string('slug')->nullable();
      $table->text('description')->nullable();
      $table->text('body');
      $table->foreignId('user_id')->constrained();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('articles');
  }
};

C:\xampp82\htdocs\lva7\database\migrations\2021_05_18_103506_create_tags_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('tags', function (Blueprint $table) {
      $table->id();
      $table->string('name');
      $table->string('slug')->nullable();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('tags');
  }
};

C:\xampp82\htdocs\lva7\database\migrations\2021_05_18_103540_create_comments_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('comments', function (Blueprint $table) {
      $table->id();
      $table->text('body');
      $table->foreignId('article_id')->constrained();
      $table->foreignId('user_id')->constrained();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('comments');
  }
};

C:\xampp82\htdocs\lva7\database\migrations\2021_05_18_103859_create_article_tag_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('article_tag', function (Blueprint $table) {
      $table->id();
      $table->foreignId('article_id')->constrained();
      $table->foreignId('tag_id')->constrained();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('article_tag');
  }
};

C:\xampp82\htdocs\lva7\database\seeders\ArticleSeeder.php

<?php
namespace Database\Seeders;
use App\Models\Article;
use App\Models\User;
use Illuminate\Database\Seeder;
class ArticleSeeder extends Seeder
{
  /**
   * Run the database seeds.
   *
   * @return void
   */
  public function run()
  {
    User::factory(10)->has(Article::factory()->count(10))->create();
  }
}

C:\xampp82\htdocs\lva7\database\seeders\ArticleTagSeeder.php

<?php
namespace Database\Seeders;
use App\Models\Article;
use App\Models\Tag;
use Illuminate\Database\Seeder;
class ArticleTagSeeder extends Seeder
{
  /**
   * Run the database seeds.
   *
   * @return void
   */
  public function run()
  {
    foreach (Article::all() as $article) {
      $article->tags()->attach(Tag::all()->random());
    }
  }
}

C:\xampp82\htdocs\lva7\database\seeders\CommentSeeder.php

<?php
namespace Database\Seeders;
use App\Models\Comment;
use Illuminate\Database\Seeder;
class CommentSeeder extends Seeder
{
  public function run()
  {
    Comment::factory(50)->create();
  }
}

C:\xampp82\htdocs\lva7\database\seeders\TagSeeder.php

<?php
namespace Database\Seeders;
use App\Models\Tag;
use Illuminate\Database\Seeder;
class TagSeeder extends Seeder
{
  /**
   * Run the database seeds.
   *
   * @return void
   */
  public function run()
  {
    Tag::create(['name' => 'Graphic Design']);
    Tag::create(['name' => 'Programming']);
    Tag::create(['name' => 'Laravel']);
    Tag::create(['name' => 'PHP']);
  }
}

C:\xampp82\htdocs\lva7\database\seeders\DatabaseSeeder.php

<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
  /**
   * Seed the application's database.
   */
  public function run(): void
  {
    $this
      ->call(ArticleSeeder::class)
      ->call(CommentSeeder::class)
      ->call(TagSeeder::class)
      ->call(ArticleTagSeeder::class);
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\App\Setting.php

<?php
namespace App\Livewire\App;
// use Artesaos\SEOTools\Facades\SEOTools;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password;
use Livewire\Component;
class Setting extends Component
{
  public $user;
  public function mount()
  {
    $userId = auth()->user()->getAuthIdentifier();
    $this->user = User::find($userId)->toArray();
    // SEOTools::setTitle('My setting', false);
  }
  protected function rules()
  {
    return [
      'user.name' => ['required'],
      'user.username' => [
        'required',
        Rule::unique('users', 'username')->ignore(auth()->user()->getAuthIdentifier()),
      ],
      'user.email' => [
        'required',
        Rule::unique('users', 'email')->ignore(auth()->user()->getAuthIdentifier()),
      ],
      'user.password' => ['sometimes', Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised()],
      'user.image' => ['required'],
      'user.bio' => ['string'],
    ];
  }
  public function render()
  {
    return view('livewire.app.setting');
  }
  public function saveSetting()
  {
    $this->validate();
    $userId = auth()->user()->getAuthIdentifier();
    $user = User::find($userId);
    $user->name = $this->user['name'];
    $user->username = $this->user['username'];
    $user->bio = $this->user['bio'];
    $user->image = $this->user['image'];
    if (array_key_exists('password', $this->user)) {
      $user->password = Hash::make($this->user['password']);
    }
    $user->save();
    $this->user = $user->toArray();
    session()->flash('flash.banner', 'Your settings has been saved');
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\App\Register.php

<?php
namespace App\Livewire\App;
// use Artesaos\SEOTools\Facades\SEOTools;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rules\Password;
use Livewire\Component;
class Register extends Component
{
  public $credentials = [
    'name' => '',
    'username' => '',
    'password' => '',
    'password_confirmation' => '',
  ];
  protected function rules()
  {
    return [
      'credentials.name' => ['required', 'string'],
      'credentials.email' => ['required', 'email', 'unique:users,email'],
      'credentials.username' => ['required', 'email', 'unique:users,username'],
      'credentials.password' => ['required', 'confirmed', Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised()]
    ];
  }
  public function mount()
  {
    // SEOTools::setTitle('Sign Up', false);
  }
  public function render()
  {
    return view('livewire.app.register');
  }
  public function register()
  {
    $this->validate();
    $user = \App\Models\User::create();
    $user->name = $this->credentials['name'];
    $user->username = $this->credentials['username'];
    $user->password = $this->credentials['password'];
    $user->email = $this->credentials['email'];
    $user->save();
    Auth::loginUsingId($user->id);
    return redirect()->route('front.index');
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\App\Login.php

<?php
namespace App\Livewire\App;
// use Artesaos\SEOTools\Facades\SEOTools;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class Login extends Component
{
  public $credentials = [
    'email' => '',
    'password' => ''
  ];
  protected $rules = [
    'credentials.email' => ['required', 'email'],
    'credentials.password' => ['required'],
  ];
  public function mount()
  {
    // SEOTools::setTitle('Login', false);
  }
  public function render()
  {
    return view('livewire.app.login');
  }
  public function login()
  {
    $this->validate();
    if (Auth::attempt($this->credentials)) {
      return redirect()->intended(route('front.index'));
    }
    $this->addError('error', 'Wrong email or password');
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\App\Article\Edit.php

<?php
namespace App\Livewire\App\Article;
// use Artesaos\SEOTools\Facades\SEOTools;
use App\Models\Article;
use App\Models\Tag;
use Livewire\Component;
use Illuminate\Support\Str;
class Edit extends Component
{
  public Article $article;
  public $tag;
  public $article_tags = [];
  public function mount(Article $article)
  {
    $this->article = $article;
    $this->article_tags = $article->tags->map(function ($tag) {
      return $tag->id;
    });
    // SEOTools::setTitle('Edit article', false);
    // SEOTools::setDescription('Article is being edited.');
  }
  protected $rules = [
    'article.title' => ['required', 'string'],
    'article.body' => ['required', 'string'],
    'article.description' => ['string'],
  ];
  public function render()
  {
    return view('livewire.app.article.edit', [
      'tags' => Tag::all()
    ]);
  }
  public function saveArticle()
  {
    $this->validate();
    $this->article->save();
    $this->article->tags()->sync($this->article_tags);
    session()->flash('flash.banner', 'Your article has been saved!');
    return redirect()->route('app.article.edit', ['article' => $this->article->id]);
  }
  public function deleteArticle()
  {
    $this->article->delete();
    return redirect()->route('front.index');
  }
  public function createTag()
  {
    $slug = Str::slug($this->tag);
    $tag = Tag::where('slug', '=', $slug)->first();
    if ($tag) {
      session()->flash('message-tag', 'Tag has existed.');
      return;
    }
    $this->validate(['tag' => ['required']]);
    if (!empty($this->tag)) {
      Tag::create(['name' => $this->tag]);
      $this->reset('tag');
      session()->flash('message-tag', 'Tag has been created');
    }
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\App\Article\Create.php

<?php
namespace App\Livewire\App\Article;
// use Artesaos\SEOTools\Facades\SEOTools;
use App\Models\Article;
use App\Models\Tag;
use Livewire\Component;
use Illuminate\Support\Str;
class Create extends Component
{
  public Article $article;
  public $tag;
  public $article_tags = [];
  public function mount()
  {
    $this->article = new Article();
    // SEOTools::setTitle('Create new article', false);
    // SEOTools::setDescription('New article created here.');
  }
  protected $rules = [
    'article.title' => ['required', 'string'],
    'article.body' => ['required', 'string'],
    'article.description' => ['string'],
  ];
  public function render()
  {
    return view('livewire.app.article.create', [
      'tags' => Tag::all()
    ]);
  }
  public function saveArticle()
  {
    $this->validate();
    $this->article->user_id = auth()->id();
    $this->article->save();
    $this->article->tags()->sync($this->article_tags);
    session()->flash('flash.banner', 'Your article has been published!');
    return redirect()->route('app.article.edit', ['article' => $this->article->id]);
  }
  public function createTag()
  {
    $slug = Str::slug($this->tag);
    $tag = Tag::where('slug', '=', $slug)->first();
    if ($tag) {
      session()->flash('message-tag', 'Tag has existed.');
      return;
    }
    $this->validate(['tag' => ['required']]);
    if (!empty($this->tag)) {
      Tag::create(['name' => $this->tag]);
      $this->reset('tag');
      session()->flash('message-tag', 'Tag has been created');
    }
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\Front\Article\Show.php

<?php
namespace App\Livewire\Front\Article;
// use Artesaos\SEOTools\Facades\SEOTools;
use App\Models\Article;
use App\Models\Comment;
use App\Models\User;
use Livewire\Component;
class Show extends Component
{
  public $article;
  public $user;
  public $comment = '';
  protected $rules = [
    'comment' => ['required', 'string']
  ];
  public function mount(Article $article)
  {
    $article->load(['author', 'comments']);
    $this->article = $article;
    if (auth()->check()) {
      $this->user = User::find(auth()->id());
    }
    // SEOTools::setTitle("{$article->title} | Conduit X Ricardo Sawir", false);
    // SEOTools::setDescription($article->description);
  }
  public function render()
  {
    return view('livewire.front.article.show');
  }
  public function saveComment()
  {
    $this->validate();
    $commenter = User::find(auth()->id());
    $comment = new Comment();
    $comment->article_id = $this->article->id;
    $comment->user_id = $commenter->id;
    $comment->body = $this->comment;
    $comment->save();
    $this->article = Article::find($this->article->id);
    $this->comment = '';
  }
  public function deleteComment($id)
  {
    $comment = Comment::findOrFail($id);
    $comment->delete();
    $this->article = Article::find($this->article->id);
    session()->flash('flash.banner', 'Successfully deleted your comment.');
  }
  public function followAuthor()
  {
    $user = User::find(auth()->id());
    $user->toggleFollow($this->article->author);
    $this->article = Article::find($this->article->id);
  }
  public function favoriteArticle()
  {
    $user = User::find(auth()->id());
    $user->toggleFavorite($this->article);
    $this->article = Article::find($this->article->id);
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\Front\Tag\Show.php

<?php
namespace App\Livewire\Front\Tag;
// use Artesaos\SEOTools\Facades\SEOTools;
use App\Models\Tag;
use Livewire\Component;
class Show extends Component
{
  public Tag $tag;
  public function mount(Tag $tag)
  {
    $tag->load(['articles']);
    $this->tag = $tag;
    // SEOTools::setTitle("{$tag->name} | Conduit X Ricardo Sawir", false);
    // SEOTools::setDescription($tag->name);
  }
  public function render()
  {
    return view('livewire.front.tag.show', [
      'tags' => Tag::all()
    ]);
  }
}

C:\xampp82\htdocs\lva7\app\Livewire\Front\User\Show.php

<?php
namespace App\Livewire\Front\User;
// use Artesaos\SEOTools\Facades\SEOTools;
use App\Models\Article;
use App\Models\User;
use Livewire\Component;
class Show extends Component
{
  public $user;
  public $articles;
  public $loggedInUser;
  public $viewingFavoriteArticles = false;
  public function updatedViewingFavoriteArticles()
  {
    if ($this->viewingFavoriteArticles) {
      $this->articles = $this->user->favorites(Article::class)->with(['author'])->get();
    }
    if (!$this->viewingFavoriteArticles) {
      $this->articles = Article::where('user_id', '=', $this->user->id)->with(['author'])->get();
    }
  }
  public function mount(User $user)
  {
    $this->articles = Article::where('user_id', '=', $this->user->id)->with(['author'])->get();
    $this->user = $user;
    $this->loggedInUser = User::find(auth()->id());
    // SEOTools::setTitle($this->user['name'], false);
    // SEOTools::setDescription($this->user['bio']);
  }
  public function render()
  {
    return view('livewire.front.user.show');
  }
  public function followUser()
  {
    $this->loggedInUser = User::find(auth()->id());
    $this->loggedInUser->toggleFollow($this->user);
    $this->user = User::find($this->user->id);
  }
}

C:\xampp82\htdocs\lva7\app\Models\Article.php

<?php
namespace App\Models;
// use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
// use Multicaret\Acquaintances\Traits\CanBeFavorited;
class Article extends Model
{
  use HasFactory;
  // use Sluggable;
  // use CanBeFavorited;
  public function sluggable(): array
  {
    return [
      'slug' => [
        'source' => 'title'
      ]
    ];
  }
  public function comments()
  {
    return $this->hasMany(Comment::class);
  }
  public function tags()
  {
    return $this->belongsToMany(Tag::class);
  }
  public function author()
  {
    return $this->belongsTo(User::class, 'user_id');
  }
}

C:\xampp82\htdocs\lva7\app\Models\Comment.php

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
  use HasFactory;
  public function author()
  {
    return $this->belongsTo(User::class, 'user_id');
  }
  public function article()
  {
    return $this->belongsTo(Article::class);
  }
}

C:\xampp82\htdocs\lva7\app\Models\Tag.php

<?php
namespace App\Models;
// use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
  use HasFactory;
  // use Sluggable;
  protected $fillable = ['name'];
  public function sluggable(): array
  {
    return [
      'slug' => [
        'source' => 'name'
      ]
    ];
  }
  public function articles()
  {
    return $this->belongsToMany(Article::class);
  }
}

C:\xampp82\htdocs\lva7\app\Models\User.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',
  ];
  public function articles()
  {
    return $this->hasMany(Article::class);
  }
  public function comments()
  {
    return $this->hasMany(Comment::class);
  }
}

C:\xampp82\htdocs\lva7\app\Providers\AppServiceProvider.php

<?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();
  }
  public function bootMenu()
  {
    Menu::macro('app', function () {
      return Menu::new()
        ->addClass('nav navbar-nav pull-xs-right')
        ->add(Link::to(route('front.index'), 'Home'))
        ->addIf(
          auth()->check(),
          Link::to(
            route('app.article.create'),
            Html::raw('<i class="ion-compose"></i>&nbsp;New Post')->render()
          )
        )
        ->addIf(
          auth()->check(),
          Link::to(
            route('app.setting'),
            Html::raw('<i class="ion-gear-a"></i>&nbsp;Settings')->render()
          )
        )
        ->addIf(
          auth()->check(),
          Html::raw('<form id="logout" method="POST" action="' . route('logout') . '"><a href="#" class="nav-link" onclick="event.preventDefault();this.closest(`form`).submit()">Logout</a>' . csrf_field() . '</form>')->addParentClass('nav-item')
        )
        ->addIf(
          !auth()->check(),
          Link::to(route('app.login'), 'Sign In')
        )
        ->addIf(
          !auth()->check(),
          Link::to(route('app.register'), 'Sign Up')
        )
        ->each(function (Link $link) {
          return $link->addParentClass('nav-item');
        })
        ->each(function (Link $link) {
          return $link->addClass('nav-link');
        })
        ->setActiveClassOnLink('active')
        ->setActiveFromRequest();
    });
  }
}

Ví dụ 3: Ta đi nghiên cứu cách dùng phần kết hợp với menu

Đã có 1 bài viết sử dụng https://c-i-ph-n-m-m-tr-n-ubuntu-c-n-thi.gitbook.io/learn-lavarel/use-package-lavary-laravel-menu-create-menu-have-role-and-permission-ok rất dễ dàng rồi nhưng đó là với gói

lavary/laravel-menu

Last updated