Package spatie/menu full phần 2 tách ra để nghiên cứu (ok)
https://github.com/sawirricardo/realworld-tall-app/tree/main
Last updated
https://github.com/sawirricardo/realworld-tall-app/tree/main
Last updated
C:\xampp82\htdocs\lva8\app\Models\Post.php
<?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\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',
];
}
C:\xampp82\htdocs\lva8\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();
}
private function bootMenu(): void
{
Menu::macro('app', function () {
return Menu::new()
->addClass('nav navbar-nav pull-xs-right')
->add(Link::to(route('home'), 'Home'));
});
}
}
C:\xampp82\htdocs\lva8\database\factories\PostFactory.php
<?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
];
}
}
C:\xampp82\htdocs\lva8\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\lva8\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();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
C:\xampp82\htdocs\lva8\database\migrations\2023_10_04_062832_create_posts_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('posts', function (Blueprint $table) {
$table->id();
$table->string('detail');
$table->string('slug');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
C:\xampp82\htdocs\lva8\database\seeders\DatabaseSeeder.php
<?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);
}
}
C:\xampp82\htdocs\lva8\database\seeders\PostSeeder.php
<?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();
}
}
C:\xampp82\htdocs\lva8\resources\views\welcome.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">
<title>Laravel</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,600&display=swap" rel="stylesheet" />
<!-- Styles -->
<style>
/* ! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com */
*,
::after,
::before {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb
}
::after,
::before {
--tw-content: ''
}
html {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
tab-size: 4;
font-family: Figtree, sans-serif;
font-feature-settings: normal
}
body {
margin: 0;
line-height: inherit
}
hr {
height: 0;
color: inherit;
border-top-width: 1px
}
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit
}
a {
color: inherit;
text-decoration: inherit
}
b,
strong {
font-weight: bolder
}
code,
kbd,
pre,
samp {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em
}
small {
font-size: 80%
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline
}
sub {
bottom: -.25em
}
sup {
top: -.5em
}
table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse
}
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
font-size: 100%;
font-weight: inherit;
line-height: inherit;
color: inherit;
margin: 0;
padding: 0
}
button,
select {
text-transform: none
}
[type=button],
[type=reset],
[type=submit],
button {
-webkit-appearance: button;
background-color: transparent;
background-image: none
}
:-moz-focusring {
outline: auto
}
:-moz-ui-invalid {
box-shadow: none
}
progress {
vertical-align: baseline
}
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px
}
::-webkit-search-decoration {
-webkit-appearance: none
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit
}
summary {
display: list-item
}
blockquote,
dd,
dl,
figure,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
pre {
margin: 0
}
fieldset {
margin: 0;
padding: 0
}
legend {
padding: 0
}
menu,
ol,
ul {
list-style: none;
margin: 0;
padding: 0
}
textarea {
resize: vertical
}
input::placeholder,
textarea::placeholder {
opacity: 1;
color: #9ca3af
}
[role=button],
button {
cursor: pointer
}
:disabled {
cursor: default
}
audio,
canvas,
embed,
iframe,
img,
object,
svg,
video {
display: block;
vertical-align: middle
}
img,
video {
max-width: 100%;
height: auto
}
[hidden] {
display: none
}
*,
::before,
::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
::-webkit-backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
.relative {
position: relative
}
.mx-auto {
margin-left: auto;
margin-right: auto
}
.mx-6 {
margin-left: 1.5rem;
margin-right: 1.5rem
}
.ml-4 {
margin-left: 1rem
}
.mt-16 {
margin-top: 4rem
}
.mt-6 {
margin-top: 1.5rem
}
.mt-4 {
margin-top: 1rem
}
.-mt-px {
margin-top: -1px
}
.mr-1 {
margin-right: 0.25rem
}
.flex {
display: flex
}
.inline-flex {
display: inline-flex
}
.grid {
display: grid
}
.h-16 {
height: 4rem
}
.h-7 {
height: 1.75rem
}
.h-6 {
height: 1.5rem
}
.h-5 {
height: 1.25rem
}
.min-h-screen {
min-height: 100vh
}
.w-auto {
width: auto
}
.w-16 {
width: 4rem
}
.w-7 {
width: 1.75rem
}
.w-6 {
width: 1.5rem
}
.w-5 {
width: 1.25rem
}
.max-w-7xl {
max-width: 80rem
}
.shrink-0 {
flex-shrink: 0
}
.scale-100 {
--tw-scale-x: 1;
--tw-scale-y: 1;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr))
}
.items-center {
align-items: center
}
.justify-center {
justify-content: center
}
.gap-6 {
gap: 1.5rem
}
.gap-4 {
gap: 1rem
}
.self-center {
align-self: center
}
.rounded-lg {
border-radius: 0.5rem
}
.rounded-full {
border-radius: 9999px
}
.bg-gray-100 {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity))
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity))
}
.bg-red-50 {
--tw-bg-opacity: 1;
background-color: rgb(254 242 242 / var(--tw-bg-opacity))
}
.bg-dots-darker {
background-image: url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.22676 0C1.91374 0 2.45351 0.539773 2.45351 1.22676C2.45351 1.91374 1.91374 2.45351 1.22676 2.45351C0.539773 2.45351 0 1.91374 0 1.22676C0 0.539773 0.539773 0 1.22676 0Z' fill='rgba(0,0,0,0.07)'/%3E%3C/svg%3E")
}
.from-gray-700\/50 {
--tw-gradient-from: rgb(55 65 81 / 0.5);
--tw-gradient-to: rgb(55 65 81 / 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)
}
.via-transparent {
--tw-gradient-to: rgb(0 0 0 / 0);
--tw-gradient-stops: var(--tw-gradient-from), transparent, var(--tw-gradient-to)
}
.bg-center {
background-position: center
}
.stroke-red-500 {
stroke: #ef4444
}
.stroke-gray-400 {
stroke: #9ca3af
}
.p-6 {
padding: 1.5rem
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem
}
.text-center {
text-align: center
}
.text-right {
text-align: right
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem
}
.font-semibold {
font-weight: 600
}
.leading-relaxed {
line-height: 1.625
}
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity))
}
.text-gray-900 {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity))
}
.text-gray-500 {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity))
}
.underline {
-webkit-text-decoration-line: underline;
text-decoration-line: underline
}
.antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
}
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
}
.shadow-gray-500\/20 {
--tw-shadow-color: rgb(107 114 128 / 0.2);
--tw-shadow: var(--tw-shadow-colored)
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms
}
.selection\:bg-red-500 *::selection {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
}
.selection\:text-white *::selection {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.selection\:bg-red-500::selection {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
}
.selection\:text-white::selection {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.hover\:text-gray-900:hover {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity))
}
.hover\:text-gray-700:hover {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity))
}
.focus\:rounded-sm:focus {
border-radius: 0.125rem
}
.focus\:outline:focus {
outline-style: solid
}
.focus\:outline-2:focus {
outline-width: 2px
}
.focus\:outline-red-500:focus {
outline-color: #ef4444
}
.group:hover .group-hover\:stroke-gray-600 {
stroke: #4b5563
}
.z-10 {
z-index: 10
}
@media (prefers-reduced-motion: no-preference) {
.motion-safe\:hover\:scale-\[1\.01\]:hover {
--tw-scale-x: 1.01;
--tw-scale-y: 1.01;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
}
}
@media (prefers-color-scheme: dark) {
.dark\:bg-gray-900 {
--tw-bg-opacity: 1;
background-color: rgb(17 24 39 / var(--tw-bg-opacity))
}
.dark\:bg-gray-800\/50 {
background-color: rgb(31 41 55 / 0.5)
}
.dark\:bg-red-800\/20 {
background-color: rgb(153 27 27 / 0.2)
}
.dark\:bg-dots-lighter {
background-image: url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.22676 0C1.91374 0 2.45351 0.539773 2.45351 1.22676C2.45351 1.91374 1.91374 2.45351 1.22676 2.45351C0.539773 2.45351 0 1.91374 0 1.22676C0 0.539773 0.539773 0 1.22676 0Z' fill='rgba(255,255,255,0.07)'/%3E%3C/svg%3E")
}
.dark\:bg-gradient-to-bl {
background-image: linear-gradient(to bottom left, var(--tw-gradient-stops))
}
.dark\:stroke-gray-600 {
stroke: #4b5563
}
.dark\:text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity))
}
.dark\:text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.dark\:shadow-none {
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
}
.dark\:ring-1 {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000)
}
.dark\:ring-inset {
--tw-ring-inset: inset
}
.dark\:ring-white\/5 {
--tw-ring-color: rgb(255 255 255 / 0.05)
}
.dark\:hover\:text-white:hover {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.group:hover .dark\:group-hover\:stroke-gray-400 {
stroke: #9ca3af
}
}
@media (min-width: 640px) {
.sm\:fixed {
position: fixed
}
.sm\:top-0 {
top: 0px
}
.sm\:right-0 {
right: 0px
}
.sm\:ml-0 {
margin-left: 0px
}
.sm\:flex {
display: flex
}
.sm\:items-center {
align-items: center
}
.sm\:justify-center {
justify-content: center
}
.sm\:justify-between {
justify-content: space-between
}
.sm\:text-left {
text-align: left
}
.sm\:text-right {
text-align: right
}
}
@media (min-width: 768px) {
.md\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr))
}
}
@media (min-width: 1024px) {
.lg\:gap-8 {
gap: 2rem
}
.lg\:p-8 {
padding: 2rem
}
}
</style>
</head>
<body class="antialiased">
<div
class="relative sm:flex sm:justify-center sm:items-center min-h-screen bg-dots-darker bg-center bg-gray-100 dark:bg-dots-lighter dark:bg-gray-900 selection:bg-red-500 selection:text-white">
@if (Route::has('login'))
<div class="sm:fixed sm:top-0 sm:right-0 p-6 text-right z-10">
@auth
<a href="{{ url('/home') }}"
class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Home</a>
@else
<a href="{{ route('login') }}"
class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Log
in</a>
@if (Route::has('register'))
<a href="{{ route('register') }}"
class="ml-4 font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Register</a>
@endif
@endauth
</div>
@endif
<div class="container">
<div class="row">
<div class="col-lg-12">
{!! Menu::app() !!}
</div>
</div>
</div>
</div>
</body>
</html>
C:\xampp82\htdocs\lva8\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();
}
private function bootMenu(): void
{
Menu::macro('app', function () {
return Menu::new()
->addClass('nav navbar-nav pull-xs-right')
->add(Link::to(route('home'), 'Home'))
->addIf(
auth()->check(),
Link::to(
route('post'),
Html::raw('<i class="ion-compose"></i> Posts')->render()
)
);
});
}
}
Nếu có đăng nhập rồi thì sẽ thấy link này
C:\xampp82\htdocs\lva8\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();
}
private function bootMenu(): void
{
Menu::macro('app', function () {
return Menu::new()
->addClass('nav navbar-nav pull-xs-right')
->add(Link::to(route('home'), 'Home'))
->addIf(
auth()->check(),
Link::to(
route('post'),
Html::raw('<i class="ion-compose"></i> Posts')->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')
);
});
}
}
C:\xampp82\htdocs\lva8\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();
}
private function bootMenu(): void
{
Menu::macro('app', function () {
return Menu::new()
->addClass('nav navbar-nav pull-xs-right')
->add(Link::to(route('home'), 'Home'))
->addIf(
auth()->check(),
Link::to(
route('post'),
Html::raw('<i class="ion-compose"></i> Posts')->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')
)
->each(function (Link $link) {
return $link->addParentClass('nav-item');
});
});
}
}
https://github.com/spatie/laravel-menu/issues/39
C:\xampp82\htdocs\lva8\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();
}
private function bootMenu(): void
{
Menu::macro('app', function () {
return Menu::new()
->addClass('nav navbar-nav pull-xs-right')
->add(Link::to(route('home'), 'Home'))
->addIf(
auth()->check(),
Link::to(
route('post'),
Html::raw('<i class="ion-compose"></i> Posts')->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')
)
->each(function (Link $link) {
return $link->addParentClass('nav-item');
})
->each(function (Link $link) {
return $link->addClass('nav-link');
});
});
}
}
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> New Post')->render()
)
)
->addIf(
auth()->check(),
Link::to(
route('app.setting'),
Html::raw('<i class="ion-gear-a"></i> 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();
});
}
}
Hay dùng thêm với:
->addIf(
!auth()->check(),
Link::to(route('login'), 'Login')
)
Chưa sử dụng cái này trong trường hợp cụ thể.
C:\xampp82\htdocs\lva8\app\Providers\ViewServiceProvider.php
<?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')
->add(Link::to(route('home'), 'Home'));
});
}
}
C:\xampp82\htdocs\lva8\config\app.php
/**
* Dev customization
*/
App\Providers\ViewServiceProvider::class,
C:\xampp82\htdocs\lva8\resources\views\welcome.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">
<title>Laravel</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,600&display=swap" rel="stylesheet" />
<!-- Styles -->
<style>
/* ! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com */
*,
::after,
::before {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb
}
::after,
::before {
--tw-content: ''
}
html {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
tab-size: 4;
font-family: Figtree, sans-serif;
font-feature-settings: normal
}
body {
margin: 0;
line-height: inherit
}
hr {
height: 0;
color: inherit;
border-top-width: 1px
}
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit
}
a {
color: inherit;
text-decoration: inherit
}
b,
strong {
font-weight: bolder
}
code,
kbd,
pre,
samp {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em
}
small {
font-size: 80%
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline
}
sub {
bottom: -.25em
}
sup {
top: -.5em
}
table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse
}
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
font-size: 100%;
font-weight: inherit;
line-height: inherit;
color: inherit;
margin: 0;
padding: 0
}
button,
select {
text-transform: none
}
[type=button],
[type=reset],
[type=submit],
button {
-webkit-appearance: button;
background-color: transparent;
background-image: none
}
:-moz-focusring {
outline: auto
}
:-moz-ui-invalid {
box-shadow: none
}
progress {
vertical-align: baseline
}
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px
}
::-webkit-search-decoration {
-webkit-appearance: none
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit
}
summary {
display: list-item
}
blockquote,
dd,
dl,
figure,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
pre {
margin: 0
}
fieldset {
margin: 0;
padding: 0
}
legend {
padding: 0
}
menu,
ol,
ul {
list-style: none;
margin: 0;
padding: 0
}
textarea {
resize: vertical
}
input::placeholder,
textarea::placeholder {
opacity: 1;
color: #9ca3af
}
[role=button],
button {
cursor: pointer
}
:disabled {
cursor: default
}
audio,
canvas,
embed,
iframe,
img,
object,
svg,
video {
display: block;
vertical-align: middle
}
img,
video {
max-width: 100%;
height: auto
}
[hidden] {
display: none
}
*,
::before,
::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
::-webkit-backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
.relative {
position: relative
}
.mx-auto {
margin-left: auto;
margin-right: auto
}
.mx-6 {
margin-left: 1.5rem;
margin-right: 1.5rem
}
.ml-4 {
margin-left: 1rem
}
.mt-16 {
margin-top: 4rem
}
.mt-6 {
margin-top: 1.5rem
}
.mt-4 {
margin-top: 1rem
}
.-mt-px {
margin-top: -1px
}
.mr-1 {
margin-right: 0.25rem
}
.flex {
display: flex
}
.inline-flex {
display: inline-flex
}
.grid {
display: grid
}
.h-16 {
height: 4rem
}
.h-7 {
height: 1.75rem
}
.h-6 {
height: 1.5rem
}
.h-5 {
height: 1.25rem
}
.min-h-screen {
min-height: 100vh
}
.w-auto {
width: auto
}
.w-16 {
width: 4rem
}
.w-7 {
width: 1.75rem
}
.w-6 {
width: 1.5rem
}
.w-5 {
width: 1.25rem
}
.max-w-7xl {
max-width: 80rem
}
.shrink-0 {
flex-shrink: 0
}
.scale-100 {
--tw-scale-x: 1;
--tw-scale-y: 1;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr))
}
.items-center {
align-items: center
}
.justify-center {
justify-content: center
}
.gap-6 {
gap: 1.5rem
}
.gap-4 {
gap: 1rem
}
.self-center {
align-self: center
}
.rounded-lg {
border-radius: 0.5rem
}
.rounded-full {
border-radius: 9999px
}
.bg-gray-100 {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity))
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity))
}
.bg-red-50 {
--tw-bg-opacity: 1;
background-color: rgb(254 242 242 / var(--tw-bg-opacity))
}
.bg-dots-darker {
background-image: url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.22676 0C1.91374 0 2.45351 0.539773 2.45351 1.22676C2.45351 1.91374 1.91374 2.45351 1.22676 2.45351C0.539773 2.45351 0 1.91374 0 1.22676C0 0.539773 0.539773 0 1.22676 0Z' fill='rgba(0,0,0,0.07)'/%3E%3C/svg%3E")
}
.from-gray-700\/50 {
--tw-gradient-from: rgb(55 65 81 / 0.5);
--tw-gradient-to: rgb(55 65 81 / 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)
}
.via-transparent {
--tw-gradient-to: rgb(0 0 0 / 0);
--tw-gradient-stops: var(--tw-gradient-from), transparent, var(--tw-gradient-to)
}
.bg-center {
background-position: center
}
.stroke-red-500 {
stroke: #ef4444
}
.stroke-gray-400 {
stroke: #9ca3af
}
.p-6 {
padding: 1.5rem
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem
}
.text-center {
text-align: center
}
.text-right {
text-align: right
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem
}
.font-semibold {
font-weight: 600
}
.leading-relaxed {
line-height: 1.625
}
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity))
}
.text-gray-900 {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity))
}
.text-gray-500 {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity))
}
.underline {
-webkit-text-decoration-line: underline;
text-decoration-line: underline
}
.antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
}
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
}
.shadow-gray-500\/20 {
--tw-shadow-color: rgb(107 114 128 / 0.2);
--tw-shadow: var(--tw-shadow-colored)
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms
}
.selection\:bg-red-500 *::selection {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
}
.selection\:text-white *::selection {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.selection\:bg-red-500::selection {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
}
.selection\:text-white::selection {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.hover\:text-gray-900:hover {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity))
}
.hover\:text-gray-700:hover {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity))
}
.focus\:rounded-sm:focus {
border-radius: 0.125rem
}
.focus\:outline:focus {
outline-style: solid
}
.focus\:outline-2:focus {
outline-width: 2px
}
.focus\:outline-red-500:focus {
outline-color: #ef4444
}
.group:hover .group-hover\:stroke-gray-600 {
stroke: #4b5563
}
.z-10 {
z-index: 10
}
@media (prefers-reduced-motion: no-preference) {
.motion-safe\:hover\:scale-\[1\.01\]:hover {
--tw-scale-x: 1.01;
--tw-scale-y: 1.01;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
}
}
@media (prefers-color-scheme: dark) {
.dark\:bg-gray-900 {
--tw-bg-opacity: 1;
background-color: rgb(17 24 39 / var(--tw-bg-opacity))
}
.dark\:bg-gray-800\/50 {
background-color: rgb(31 41 55 / 0.5)
}
.dark\:bg-red-800\/20 {
background-color: rgb(153 27 27 / 0.2)
}
.dark\:bg-dots-lighter {
background-image: url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.22676 0C1.91374 0 2.45351 0.539773 2.45351 1.22676C2.45351 1.91374 1.91374 2.45351 1.22676 2.45351C0.539773 2.45351 0 1.91374 0 1.22676C0 0.539773 0.539773 0 1.22676 0Z' fill='rgba(255,255,255,0.07)'/%3E%3C/svg%3E")
}
.dark\:bg-gradient-to-bl {
background-image: linear-gradient(to bottom left, var(--tw-gradient-stops))
}
.dark\:stroke-gray-600 {
stroke: #4b5563
}
.dark\:text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity))
}
.dark\:text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.dark\:shadow-none {
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
}
.dark\:ring-1 {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000)
}
.dark\:ring-inset {
--tw-ring-inset: inset
}
.dark\:ring-white\/5 {
--tw-ring-color: rgb(255 255 255 / 0.05)
}
.dark\:hover\:text-white:hover {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity))
}
.group:hover .dark\:group-hover\:stroke-gray-400 {
stroke: #9ca3af
}
}
@media (min-width: 640px) {
.sm\:fixed {
position: fixed
}
.sm\:top-0 {
top: 0px
}
.sm\:right-0 {
right: 0px
}
.sm\:ml-0 {
margin-left: 0px
}
.sm\:flex {
display: flex
}
.sm\:items-center {
align-items: center
}
.sm\:justify-center {
justify-content: center
}
.sm\:justify-between {
justify-content: space-between
}
.sm\:text-left {
text-align: left
}
.sm\:text-right {
text-align: right
}
}
@media (min-width: 768px) {
.md\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr))
}
}
@media (min-width: 1024px) {
.lg\:gap-8 {
gap: 2rem
}
.lg\:p-8 {
padding: 2rem
}
}
</style>
</head>
<body class="antialiased">
<div
class="relative sm:flex sm:justify-center sm:items-center min-h-screen bg-dots-darker bg-center bg-gray-100 dark:bg-dots-lighter dark:bg-gray-900 selection:bg-red-500 selection:text-white">
@if (Route::has('login'))
<div class="sm:fixed sm:top-0 sm:right-0 p-6 text-right z-10">
@auth
<a href="{{ url('/home') }}"
class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Home</a>
@else
<a href="{{ route('login') }}"
class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Log
in</a>
@if (Route::has('register'))
<a href="{{ route('register') }}"
class="ml-4 font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Register</a>
@endif
@endauth
</div>
@endif
<div class="container">
<div class="row">
<div class="col-lg-12">
{{-- {!! Menu::app() !!} --}}
{!! Menu::sidebar() !!}
</div>
</div>
</div>
</div>
</body>
</html>
C:\xampp82\htdocs\lva8\app\Providers\ViewServiceProvider.php
<?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'));
});
}
}
https://spatie.be/docs/menu/v3/basic-usage/adding-items
C:\xampp82\htdocs\lva8\app\Providers\ViewServiceProvider.php
<?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()
);
});
}
}
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
{!! Menu::sidebar() !!}
C:\xampp82\htdocs\lva8\routes\web.php
<?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');
});
});
C:\xampp82\htdocs\lva8\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');
}
}
C:\xampp82\htdocs\lva8\app\Http\Controllers\Backend\DashboardController.php
<?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()
{
}
}
C:\xampp82\htdocs\lva8\app\Http\Controllers\HomeController.php
<?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');
}
}
C:\xampp82\htdocs\lva8\app\Http\Controllers\PostController.php
<?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()
{
}
}
C:\xampp82\htdocs\lva8\app\Models\Post.php
<?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'
];
}
C:\xampp82\htdocs\lva8\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\lva8\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 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',
];
}
C:\xampp82\htdocs\lva8\app\Providers\ViewServiceProvider.php
<?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()
);
});
}
}
C:\xampp82\htdocs\lva8\config\app.php
/**
* Dev customization
*/
App\Providers\ViewServiceProvider::class,
C:\xampp82\htdocs\lva8\database\factories\PostFactory.php
<?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
];
}
}
C:\xampp82\htdocs\lva8\database\factories\RoleFactory.php
<?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(),
];
}
}
C:\xampp82\htdocs\lva8\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\lva8\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();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
C:\xampp82\htdocs\lva8\database\migrations\2023_10_04_062832_create_posts_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('posts', function (Blueprint $table) {
$table->id();
$table->string('detail');
$table->string('slug');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
C:\xampp82\htdocs\lva8\database\migrations\2023_10_04_080352_create_roles_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('roles', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('roles');
}
};
C:\xampp82\htdocs\lva8\database\migrations\2023_10_04_080922_create_role_user_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('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');
}
};
C:\xampp82\htdocs\lva8\database\seeders\DatabaseSeeder.php
<?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);
}
}
C:\xampp82\htdocs\lva8\database\seeders\PostSeeder.php
<?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();
}
}
C:\xampp82\htdocs\lva8\database\seeders\RoleSeeder.php
<?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]);
}
}
}
C:\xampp82\htdocs\lva8\database\seeders\UserSeeder.php
<?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',
]);
}
}
C:\xampp82\htdocs\lva8\resources\views\backend\index.blade.php
@extends('layouts.dashboard')
@section('content')
<div class="level box">
<div class="level-item has-text-centered">
<div>
<p class="heading">{{ __('dashboard.my_field_observations') }}</p>
<p class="title">observationCount</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">{{ __('dashboard.approved') }}</p>
<p class="title">approvedObservationCount</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">{{ __('dashboard.unidentifiable') }}</p>
<p class="title">unidentifiableObservationCount</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">{{ __('dashboard.pending') }}</p>
<p class="title">pendingObservationCount</p>
</div>
</div>
</div>
@endsection
@section('breadcrumbs')
<div class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li class="is-active"><a>{{ __('navigation.dashboard') }}</a></li>
</ul>
</div>
@endsection
C:\xampp82\htdocs\lva8\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.bunny.net">
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
<!-- 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">
<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')
</main>
</div>
</body>
</html>
C:\xampp82\htdocs\lva8\resources\views\home.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection
C:\xampp82\htdocs\lva8\resources\views\welcome.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection
Đăng nhập với vai trò là full quyền.
Đăng nhập với vai trò không có quyền gì.