Blade templating
Blade templating is a way to write php code with html in a clean and readable manner. It is heavily supported by Laravel because of its simplicity and easy to read syntax but under the hood the code is being converted to vanilla php code.
$slot
You can think of a way to reserved a slot or certain part of the code. With having that reserved slot, it will make the code more dynamic and become usable on different files.
This is where the concept of component is.
In JS/React it is the same as children
Examples:
<?php
//layout.blade.php
<body>{{$slot}}</body>
//home.blade.php
<x-layout>
<h1>Home</h1>
</x-layout>
Named $slot
While slot can make the component dynamic, named $slot will make the component even more dynamic. The concept of named slot is to have a multiple slot in your component without creating an intersection or conflict with the other slot.
Example:
<?php
//layout.blade.php
<body>
<nav>
<h1>{{$title}}</h1>
</nav>
{{$slot}}
</body>
//home.blade.php
<x-layout>
<x-slot:title>Home</x-slot:title>
<section>
<p>This is Home</p>
</section>
</x-layout>
To resolve a conflict with slot in order to tell blade which slot to use the naming <x-slot:title>
important, this way we can tell blade that the content inside the slot title will be render on where $title
variable is.
$attributes
Is a helper to pass down the props to the component. Meaning, once set in the component you can use the Official attribute of the html tag it was passed to.
Example:
<!-- component -->
<div {{$attributes}}>
{{$slot}}
</div>
<!-- usage -->
<x-component class="text-red-100"></x-component>
-
Merge is a method of
$attributes
to merge your component’s attribute to the attribute on where the component is being use.Example
<!-- component --> <div {{$attributes->merge(['class'=> 'bg-red-500 text-white'])}}>{{$slot}}</div> <!-- usage --> <x-component class='text-blue-500'>foo</x-component> <!-- output --> <div class='bg-red-500 text-blue-500'>foo</div>
-
Get is another method of
$attributes
, it gets the value of the attribute. You can also define a fallback. This is good if you want to create a fallback for components that needs an attributes.Example:
<!-- component --> <div class="{{$attributes->get('class','bg-red-500')}}"> {{$slot}} </div> <!-- usage --> <x-component class="text-blue-500">foo</x-component>
$props
has the same usage as attributes, you can define it inline. If named slot you can define as a children props can define inline. It has the same characteristic with named slot but the usage is the same with $attributes
You can define the props in the component through laravel helper @props()
function.
<!-- component -->
{{@props(['foo'=>true])}} <!--array or assoc array to provide default-->
<div class="{{foo? bg-red-500 : bg-blue-500}}"></div>
:
By Default, if you pass a value through props or attribute it is consider as a string.
To pass the actual data type to the component :
must be used before the prop.
<!-- usage -->
<x-component :foo="true" >bar</x-component>
$props
vs $attributes
props is for custom attributes attrbiutes is the official attributes
Helpers
A helper functions and classes directly from laravel.
Request
Is a helper that returns the current request(http request) instance.
-
is
methodA part of request method that checks the path and return a boolean if it matches the path. Example:
<h1> @if(request()->is('/')) Im home @endif Not Home </h1>
wildcard can also be used to match more/subsequent path.
<?php
request()->is('/dashboard/*');
Eloquent
Laravel ORM. It handles a lot of things when it comes to mapping database to your code such as :
-
migration schema - you can define your tables in Database folder
-
factory - fast setup and scaffold your database data.
-
seed - Best pair with factory and also pairs well with
migrate:fresh --seed
. -
Model
- defining relationship - using Model to define relationship between tables.
- setting up pagination.
- can setup lazy or eager loading.
Terminal Php artisan
A terminal helper that can scaffold a code depending on what you need.
For example it can help create a model which you can also create migration,factory and test for you in a single php artisan make:model
And through this, you can define your table columns through the migration and factory so that you can use the helper again to migrate it for you and seed it as well with a single command php artisan migrate --seed
There are a lot of artisan command but you can define help
before the command of the one that you need a help with to check the available flags and options .
Example:
php artisan help migrate
will give you the flags and options in migrating your schema to the database .
Alternatively, you can run the command php artisan help
to list all the artisan command.
Model
This is where your database connections, authentication , tables relationship like foreign keys relationship and even CRUD operation
It binds your database table throught Eloquent and connect it to your Laravel code so that you can call it to query in your code base instead of querying directly to your database.
It also binds it to other class like policies(authorization), your Auth facade(authentication), or to your controller
You can define the model by using the terminal command php artisan make:model
. Where you’ll be prompted with questions like names
Example model:
<?php
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory;
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'first_name',
'last_name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
This is a model that extends the Authenticatable which is responsible for authentication and the class that connects your User Model to the authentication related helpers provided by laravel, more info About authentication.
Another example that extends the model
<?php
class Job extends Model
{
use HasFactory;
protected $table = 'job_listings';
// protected $fillable = ['salary','title','employer_id','created_at','updated_at'];
// or
protected $guarded = ['id'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<App\Models\Employer,App\Models\Job>
*/
public function employer(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Employer::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<App\Models\Tag,App\Models\Job>
**/
public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Tag::class, foreignPivotKey: "job_listing_id");
}
}
Syntax /method of model class
HasFactory
- is from the
model
extension that binds with the factory class. This is responsible for seeding and the class that connects to the seesing class.
$table
- is the table name from your database. To be able to connect your orm model to the correct database table.
$fillable
- one of the security feature of the
model
class. To protect your database from sql injections you can set what are allowed to be filled in your database and if other field/columns were being stored in your database an execption will be thrown.
guarded
- the same security feature like fillable but the way it define is the opposite. Here you will define what are the columns that must not be query/stored
hidden
-
is another security feature for your model. It is a way to avoid the columns define here from being serialized/access when querying the table. In the example model
User
, it definedpassword
andremember_token
as hidden. This means when the user database is being queried it will not include the password and remember_token.Example:
<?php $user = User::find(1); return response->json();
Will return json without password and remember_token.
This is a useful feature to avoid accidental leaking of sensitive information.
Cast
-
Is a way for laravel/eloquent on how to handle the column when it is being retrieved or stored to the database. Laravel will convert(cast) that data to a certain datatype that you need.
-
Usage create a protected method name
cast
and return anassoc array
where the key is thetable name
and the value is the cast types. Refer in the document for the cast typeslaravel docs about cast system
For example(from the table above):
- Hashing the password, laravel will hash the password when it is being stored to the database
and, for example, the user login with the password laravel will also convert it and with the help of
Auth
facade it will compare both of the password to authenticate the user.
- Hashing the password, laravel will hash the password when it is being stored to the database
and, for example, the user login with the password laravel will also convert it and with the help of
Binding
To bind your route with the model to automatically query your database. This is helpful to lessen the logic since you can use the binded model for your CRUD operation. Most used in a route with a wild card.
- Usage Just need to define your model as a type.
Example:
<?php
//this
public function show(Job $job): View
{
return view('jobs.show', ['job' => $job ]);
}
// instead of this
public function show($job): View
{
$job = Job::query->find($job);
return view('jobs.show', ['job' => $job ]);
}
Authenticaton
The most common process for authentication are as follows:
-
Validation
checking the data/input that was sent to the server.
Example:
<?Php $attributes = request()->validate([ 'email' => ['email','required'], 'password' => ['required',Password::min(6)] ]); // this will check two passwords against each other the input name should be 'password' => ['required',Password::min(6),'confirmed'], // password_confirmation
This will check and validate the data and it returns the validated data or the error message back to its source , then this is where you’ll receive and display the error message.
The pattern is
using assoc array:
"name" => ['rule','rule',...]
or
using regular expression:
"name" => "'rule'|'rule'|rule..."
'confirmed'
is used if you have an input that needs to be repeated for validation.<name>_confirmation
must be the name so that laravel can check and validate both input against each other.- FAILED validation
If the validation fails the user will be redirected to the source page and the message will be flashed to session storage.
Which then you can access with blade helper @error()
e.g.
@error("$name") <p class="text-red-500 font-semibold text-xs line-clamp-1 ">{{ $message }}</p> @enderror
XHR request if the source request is an XHR request, then the validation will redirect and return a HTTP response of 422 status code with the json response containing the validation errors.
This is useful for AJAX requests and this is what inertia uses for validation.
Illuminate Password
Password
came fromuse Illuminate\Validation\Rules\Password;
A helper from laravel that validates the received data for passwords restrictions
- min : character length restriction
- max : character length restrictions
- mixedCase : uppercase and lowercase
- symbols : special characters
- uncompromised : will check if the password is exposed in haveibeenpwned This will check if the data have been leaked in the said website once(or the amount that you put in the argument)
This validation can be chained with other validation rules.
Example:
<?php $attributes = request()->validate([ 'password' => ['required', Password::min(6)->symbols()->mixedCase()->uncompromised(3)] ]); // this will check for minimum 6 character that must have a symbol and a mixed mixedCase // and that wasnt leaked 3 times.
Request validation
Different ways to do validation:
-
Manual validation
Validator::make
full docs for manual validation
This came from
Illuminate\Support\Facades\Validator;
Where you can call and manually setup the validation And you can also set a custom message for the validation
This is good if you want to fine tune the validation.
-
Laravel helper
Request()->validate(['name'=>['rules'...]])
The more straightforward validation where you can either get the validated data or redirected back to source with the flashed validation errors
-
Own class validation (
php artisan make:request
)That will be located in
app/Http/Requests
folder.This folder is where requests take place its normally connected with the controller .
By separating the validation , you have a dedicated class for handling requests like validation. And to use it you just have to bind it to your controller and type hint it.
Customizing validation message
laravel docs for customizing validation message There are different ways to customize the error message, one of which is by creating a
public message
function in your Request class. (Example shown below)This involves by creating a
public message
function in your Request class.Example:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class InfoRequest extends FormRequest { /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { return true; } /** * Get the validation rules that apply to the request. * * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> */ public function rules(): array { return [ 'name' => ['required', 'min:3'], 'email' => ['required', 'email', 'unique:App\\Models\\User'], 'phone' => ['required', 'min:10'], ]; } public function messages(): array { return [ 'required' => ':attribute field is required', 'min' => 'Atleast :min character is required', 'email.email' => 'Please provide a valid email', 'email.unique' => 'Email already taken', ]; } }
Usage example:
<?php namespace App\Http\Controllers; use App\Enums\FormSection; use App\Http\Requests\InfoRequest; use App\Traits\HandlesFormSessions; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; class InfoController extends Controller { use HandlesFormSessions; public function store(InfoRequest $request): RedirectResponse { // this will validate all the inputs and return the value if success, redirect to source if fails $validatedData = $request->validated(); //you can also FILTER the validation data $validatedData = $request->safe()->only(['name', 'email']); // you can also exclude some $validatedData = $request->safe()->except(['name', 'email']); $this->storeFormSessionData(FormSection::INFO, $validatedData); return redirect('/plans'); } }
-
creating and storing user
This is only necessary for user registration.
After validation, we can interact to the User model(for eloquent), to create a user.
The faster/easier way is to get the returned value from the validation and use it to create a user.
<?php
$validatedData = $this->validate($request, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create($validatedData);
-
Attempt to login(Auth facade)
detailed documentation about Authentication
After a successful validation, Auth facade Illuminate\Support\Facades\Auth
can be use to login.
Auth facade is a static class from laravel service container. That can help you interact with the currently Authenticated User.
The recommended process are the following:
-
For registration process:
-
validate the User and store data to database
-
Auth::login($user)
to login the user, where$user
is the user returned data.
-
-
For login process:
⚠️ It is important to set the logout to
POST
method not aGET
method like an anchor tag.-
after validation, you can use attempt to login using
Auth::attempt($attributes)
This attempt will return a booleanhandling failed login
When failed login occurs, like wrong password, email not found, etc,.
Auth::attempt($attributes)
will return false and with this you can throw an exception likethrow ValidationException::withMessages(['email' => 'Invalid Credentials, Please Try again']);
and redirect the user back to login page📝 Note: if you use
ValidationException::withMessages
, it will redirect you back to the source url including the message -
once the user is logged in, it is important to regenerate the session id to avoid session hijacking.
-
then redirect to the desired page.
redirect()->intended();
: home page default
Simple example for login authentication:
<?php public function store(Request $request): RedirectResponse { //validate $attributes = $request->validate([ 'email' => ['email','required'], 'password' => ['required',Password::min(6)] ]); //attempt to login if (! Auth::attempt($attributes)) { //throw exception throw ValidationException::withMessages(['email' => 'Invalid Credentials, Please Try again']); } //regenerate session id $request->session()->regenerate(); //redirect return redirect('/jobs'); }
-
-
For Logout Process:
⚠️ It is important to set the logout to
POST
method not aGET
method like an anchor tag.Logging out is a two step process:
-
first, you need to destroy the session
Auth::logout();
-
then redirect to the desired page.
redirect()->intended();
: home page default
-
For redirect: redirect()→guest(‘login’); redirect()→intended();//home page default
authorization
There are two ways to control authorization in Laravel.
Policies is the way to control authorization that is coupled with a model or resource.
Gates and Policies can worked together.
You can use gates to control authorization that is simple and not models related like viewing certain authorized pages
and use policies to control authorization that is tightly coupled with a model or resource like editing a data in the dashboard or deleting entry.
NOTE 📝
Map only works for the table that has a relationship with the user table. In order for laravel to know and compare the currently authenticated user with the column data . That column data must be connected to user or its related table is connected to a user.
Example of simple Authorization:
<?php
// assuming this in a method in the Controller
public function edit(Job $job){ //binding the job
//will check if the user is logged in
if(Auth::guest()){
return redirect()->guest('login');
}
// the autohorization
// will check if the logged in user is the same to the jobs user(relationship)
if(! $job->user->is(Auth::user())){ // will check if the user column data(e.g. id) will match with Auth::user()
abort(403); // will display a forbidden error page
}
}
As mentioned that this is simple because it only belongs to the controller so if you need to do some logic elsewhere you need to recreate this logic again, so its better to use gate.
Gates
Gates is the simpler way to control authorization. It is mostly used for authorization that is not tightly coupled with a model. Although you can, Policies is probably the best approach for that
Defining gates
Gates can be defined in the app/Providers/AuthServiceProvider
file so that it initializes on boot and be ready to use.
Gate::define() takes two parameters:
- name: the name of the gate to be accessed globally in the application
- a callback that determines whether the action is authorized or not.(will return boolean)
Example:
<?php
public function boot(): void
{
//name |callback User Model , Post Model
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
// or with more arguments for more context on logic
Gate::define('foo', function (User $user, Post $post, bool $bar) {
if($user->id === $post->user_id){
return true;
} if ($bar) {
return false;
}
});
// alternatively you can use class callback array
Gate::define('update-post', [PostPolicy::class, 'update']); // will use the update method from the PostPolicy class
}
- User: the binded model for the logins
- Post: the binded model that you want to check against the user
- Return: bool, the callback will return truthy or falsy depending on the matching logic. or a class callback array where you can use the policy method name instead of a closure.
✅ Good to know .
If you have a policy created for autorization, you can use to define your gates this way you can access it using
Gate::authorize
,Gate::allows
,Gate::denies
and some others.
📝 NOTE
the User in the closure will always check for the authenticated user. Means if the user is not logged in and the gate is called it will not hit the gate logic and will automatically return false ✅ To Fix it you can make the
$user=null
or make it optional?User $user
If the user is not logged in and will try to access the gate it will redirect it to the named login route.
Defining Gates using Responses
This is useful if you want to return a more detailed response then a boolean.
You can use the Response
static class from Illuminate\Auth\Access\Response
Where you can pass a message together with the HTTP status response.
Example:
<?php
use Illuminate\Auth\Access\Response
public function boot(): void
{
//name |callback User Model , Post Model
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id ? Response::allow() : Response::deny('You are not authorized to update this post');
});
}
Alternatively, it is a best security practice to return a 404 instead a 403 to hide the existence of a resource.
Here is why:
-
to avoid enumeration attacks, if the response is 403 it will hint the attacker that the resource exists just dont have enough authorization instead of 404 it will hint that the page doesnt exist even if it may does.
-
for better ux, it will avoid confusion to the user.
laravel provides a helper function to return a 404 response using Response
<?php
use Illuminate\Auth\Access\Response
public function boot(): void
{
//name |callback User Model , Post Model
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id ? Response::allow() : Response::denyWithStatsus(404, "Post not Found"); // you can customize the message too
});
// or laravel helper
Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id ? Response::allow() : Response::denyAsNotFound();
});
}
Intercepting gate checks
laravel docs for Intercepting gate checks
If you need to intercept the authorization before the defined gates you can use:
Gate::before
where you can bypadd the gate checks. This is useful if for admins that needs access to all the routes.
You can define Gate::before
in the app/Providers/AuthServiceProvider
file so that it initializes on boot and can be access globally.
Example:
<?php
public function boot(): void
{
Gate::before(function (User $user,string $ability) {
if ($user->isAdmin()) {
return true;
}
});
}
Alternatively you can use Gate::after
to run after the gate checks. This is useful if you want to do a logging for successful or failed authorization.
Example:
<?php
public function boot(): void
{
Gate::after(function (User $user, string $ability, bool|null $result) {
\Log::info("User {$user->id} attempted {$ability}: " . ($result ? 'Success' : 'Failure'));
});
}
📝 Note:
Both
Gate::before
andGate::after
will be called for all gates.
Accessing the gate
There are several ways to access the gates.
-
Accessing directly in the controller
-
Directly in the controller
<?php public function edit(Post $post){ //gates can be used in the controller if(Authh::user()->cannot('update-post',$post)){ abort(403); } // or Gate::authorize('update-post',$post); Gate::allows('update-post',$post); Gate::denies('update-post',$post); Gate::any(['update-post','foo'],[$post,$bar]); Gate::none(['update-post','foo'],[$post,$bar]); // or accessing gate with multiple arguments Gate::any('foo',[$post,$bar]); Gate::none('foo',[$post,$bar]); }
Gate::authorize - will run the action and will check if the user is authorized for that action. if not it will throw an instance of AuthorizationException from
Illuminate\Auth\Access\AuthorizationException
which will throw an HTTP response of 403(forbidden)Gate::allows - will run the action and will check if the user is authorized for that action. this will return a
boolean
instead ofException
. False if not allowed to the such action else true.Gate::denies - the opposite of Gate::allows
Gate::any - is like Gate::allows but it will check if the user is authorized for any or either of the actions. So it is a gate for multiple actions.
Gate::none - is like Gate::denies but it will check if the user is authorized for none or neither of the actions.
-
using Gate defined through Responses
Accessing using Gate::inspect
<?php public function edit(Post $post){ $result = Gate::inspect('update-post',$post); // will return a boolean if($result->allowed()){ // now authorized do something.. // like updating the db and redirecting } else { echo $result->message(); // inertia you can pass the message as a prop return back()->with([ 'message' => $result->message(), ]); } }
or using Gate::authorize
<?php public function edit(Post $post){ Gate::authorize('update-post',$post); // if fails will throw 403 forbidden with the custom message from the Response->deny() // which is : You are not authorized to update this post }
-
using Can for
Auth::user()
facade<?php public function edit(Post $post){ if(Auth::user()->cannot('update-post',$post)){ abort(403); // or fail the request } }
-
-
using blade
you can use
@can
blade directive to check if the user is authorized for the action.You need to match the gate name (first argument) with the data it needs to check against.
// gates can be used in blade template @can('update-post',$post) <button>Update</button> @endcan
If you have policies, there is no change in the syntax. But you might want to update your
can
directive to match to the policy method name.@can('update',$post) <button>Update</button> @endcan
-
Accessing gate in Route level through middleware
The problem with accessing the gate in controller is that you have to define it in the controller everytime you need it.
Middleware solves this instead of accessing gate in the controller it self you can do a gate check in the route level where controllers are connected.
There are different ways to do it.
<?php Route::get('/edit/{post}', [JobController::class, 'edit'])->middleware(['auth','can:update-post,post']);
the
can:update-post,post
, the post here represents the wildcard from the first argument for Route::getalternatively you can use
can
method for better readability<?php // gate name , wildcard Route::get('/edit/{post}', [JobController::class, 'edit'])->middleware('auth')->can('update-post','post');
using Policy
if youre using policy you can access it through middleware just like gate but with a little different syntax
<?php Route::get('/edit/{post}', [JobController::class, 'edit']) ->middleware('auth') ->can('update','post'); // no more update-post
What this does under the hood is, it will check the JobController and its binded model then will check if a policy exists and finally it will then check and run the update method inside that policy.
That is why it is important to connect the policy to the model.
alternatively, you can group controller together like so:
<?php Route::controller(JobController::class)->group(function () { // index Route::get('/jobs', 'index'); //store Route::post('/jobs', 'store') ->middleware('auth'); //create Route::get('/jobs/create', 'create') ->middleware('auth'); //show Route::get('/jobs/{job}', 'show'); //edit Route::get('/jobs/{job}/edit', 'edit') ->middleware('auth') ->can('edit', 'job'); //update Route::patch('/jobs/{job}', 'update') ->middleware('auth') ->can('edit', 'job'); // delete Route::delete('/jobs/{job}', 'destroy') ->middleware('auth') ->can('edit', 'job'); });
or
<?php Route::middleware(['auth', 'can:edit,job'])->group(function () { Route::get('/jobs/{job}/edit', 'edit'); Route::patch('/jobs/{job}', 'update'); Route::delete('/jobs/{job}', 'destroy'); });
you can also create a middleware that will check for the gates and/or policies.
<?php // In app/Http/Middleware/EnsureJobCanBeEdited.php class EnsureJobCanBeEdited { public function handle($request, Closure $next) { $job = $request->route('job'); if (!Gate::allows('edit', $job)) { abort(403); } return $next($request); } } // Then in routes Route::patch('/jobs/{job}', 'update') ->middleware(['auth', EnsureJobCanBeEdited::class]);
Policies
Policies is the way to control authorization that is tightly coupled with a model or resource.
php artisan make:policy
will scaffold a policy for you. To connect it to your model there will be a prompt that will ask you the model you want to connect it to.
Connecting to the model is important because laravel is going to use it to check for the policy that is connected to your model, especially when run with can
through middleware, there is a demo example here.
-
Accessing policy methods through gates
You can use the policy logic to your gates by adding the policy class instead of callables when defining the gates. example shown here
📝 NOTE
You can use use the logic from defining gates to your policies methods. The logic/idea is just the same where the method from policy just need to return a boolean | Response. same with how you define gates
Policy Responses
just like gate responses you can also use it in Policies method too. You can also create a custom response message.
Example:
<?php
public function update(User $user, Post $post): Response
{
return $user->id === $post->user_id ? Response::allow() : Response::deny('You are not authorized to update this post');
}
or use the denyAsNotFound
helper or denyWithStatus
helper.
Accessing the policy
As what mention above, you have to use the policy class instead of the callable when defining the gates. to be able to use it in through gates but if you’re going to access it through middleware you can use it as it is.
Authorization with Inertia
docs regarding authorization with inertia
you can use the HandleInertiaRequests middleware to use can method for authorization.
example:
<?php
namespace App\Http\Middleware;
use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
// ...
/**
* Define the props that are shared by default.
*
* @return array<string, mixed>
*/
public function share(Request $request)
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user(),
'permissions' => [
'post' => [
// this is where you set the authorization through can.
'create' => $request->user()->can('create', Post::class),
],
],
],
];
}
}
with this you can now access it in your frontend of choice Vue/React/svelte though grobal props.
Example:
<template>
<div>
<h1>Create a Post</h1>
<!-- Conditionally show the create button if the user has permission -->
<div v-if="canCreatePost">
<button @click="createPost">Create New Post</button>
</div>
<div v-else>
<p>You do not have permission to create a post.</p>
</div>
</div>
</template>
<script setup>
import { computed } from "vue"
// Props from Inertia (automatically passed to every page)
defineProps({
auth: Object, // Receiving the shared 'auth' object from Laravel middleware
})
// Computed property to check if the user can create a post
const canCreatePost = computed(() => {
return auth?.permissions?.post?.create ?? false
})
// Function to handle the "Create Post" button click
const createPost = () => {
alert("Post creation logic goes here!")
}
</script>
Another approach
This approach uses the can
method directly at route level.
This is good if you want to only access the autohorization per route and not globally through the middleware.
Steps:
Add can(gate) to as a prop for your frontend to receive or fine grained control (like only for admin)
Things to note:
- to access the gate pass it as a prop
<?php
// pass the prop like this
Route::get('/', function () {
return Inertia::render('Home', [
'name' => 'ting',
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
'can' => [ 'createUser': Auth::user()->email === '[email protected]' ]
]);
});
or create a policy
<?php
// pass the prop like this
'can' => [
'createUser': Auth::user()->can('create', User::class)
// ^ this is the gate from policy
]