Can anyone help me with understanding when/how to use each of these?
I wondered about this as well, and after some reading, here's what I've concluded:
Typically runs on a route (but you can also run it on controller methods) and can be used to filter or inspect incoming requests.
One example would be auth
, which determines if the person trying to request a particular route is authenticated (logged in) to the system. Another example would be to check that a request has a specific header (e.g. if you want to check that the app is sending a X-MYAPP-CUSTOMHEADER
header or something)
As mentioned, middleware can be defined on a route (e.g. in web.php
or api.php
) or in a controller.
An example in web.php:
// Get all the posts, but only if the user has signed in to the website
Route::get('/posts', [PostController::class, 'index'])->middleware('auth');
An example in PostController.php
:
public function __construct() {
// Apply the `auth` middleware to every function in this controller
$this->middleware('auth');
// Apply the `signed` middleware, but only to the functions `download` and `delete`
$this->middleware('signed', ['only' => ['download', 'delete']]);
}
Gates are functions defined in your AuthServiceProvider.php
file (in the App\Providers
folder) and specify what a user is allowed to do and what they're not allowed to do. For example:
Gate::define('delete-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
Then in your PostController
's delete
method:
public function delete(Request $request, Post $post)
{
if (Gate::denies('delete-post', $post)) { // Returns true if `$post->user_id` is not the same as `$user->id` (a.k.a the user is not allowed to delete this post)
abort(403);
}
$post->delete();
}
You can also use some helper methods in your blade templates:
@can('delete-post', $post)
<!-- Show a link to the delete page here -->
@endcan
(I've expanded on this below)
Guards are a way to specify how users are authenticated for requests.
In a project I'm working on, I have a jwt
(JSON Web Token) guard on my API routes. This means that when I do something like auth()->attempt(['username' => 'test', 'password' => 'test']);
, the auth()
function will try and authenticate me using the jwt
guard.
Which guard to use is specified in auth.php
. Mine currently looks like this:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users'
],
],
So in this case, requests coming in via the browser will have a session assigned when they log in, and requests coming in via an API call will need to get / provide a token to authenticate. Both will look up the users
table to check the username / password when authenticating.
You can specify which guard to use with the auth()
call:
auth('api')->attempt(['api_key' => 'abc1234'])
(and side note, I got caught up with this, as I was wondering why auth()->user()
wasn't returning the current user, and that's because web
was the default guard, and I was trying to get the user who had authenticated via api
, so be sure to be explicit about which guard!)
Policies work similarly to gates, but just apply to a specific model and are stored in their own file (in App\Policies
)
You can create one by using php artisan make:policy PostPolicy -m Post
. It'll create a file called PostPolicy.php
which will create a bunch of functions:
viewAny // Can the user even look at this model? If no, they'll be denied for all the below methods
view // Can the user look at the specified Post?
create // Can they create a new Post
update // Can they edit / update the specified Post?
delete // Can they (soft) delete the specified Post?
restore // Can they restore the specified (soft) deleted Post?
forceDelete // Can they force delete the specified Post?
Typical function(s) would look like this:
public function viewAny(User $user)
{
// If this returns true, then the other methods will be evaluated.
return $user->can_view_posts;
}
public function forceDelete(User $user, Post $post)
{
// If this returns true, then the user can force delete the post.
// This depends on viewAny being true
return $post->user_id == $user->id;
}
One thing to note, is that viewAny
is NOT the same as "view all"! Think of viewAny
as the front door to a building. If viewAny
is true, you can go into the lobby, but you can't look inside any of the rooms unless view
for a particular room is also true.
I believe you can also use the @can
in blade templates with policies:
@can('forceDelete', $post)
<!-- Show button to force delete a post here -->
@endcan
And the inverse too:
@cannot('view', $post)
<!-- Show a greyed out button or something -->
@cannot
As well as the @canany
for checking multiple permissions:
@canany(['edit', 'delete'])
<!-- Show something if the user can edit OR delete -->
@endcanany
I hope this has been useful. I certainly learned a lot while reading up on this. There's way more to this than I thought, so it's worth checking out the Laravel documentation because I may be right in some things, but I may be waaay off in other things.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With