Summer Sale! All accounts are 50% off this week.

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Is it a good practice to extend Spatie Permission models in a Laravel application?

Hi everyone, I’m using the Spatie Laravel Permission package in my project, and I’m wondering if it’s considered a best practice to extend the default Role and Permission models and place them inside my app’s Models folder, rather than using the package’s built-in ones directly.

Would this approach make sense for better maintainability or customization, or is it generally better to stick with the package defaults unless I really need custom behavior?

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Best way to systematically determine all permissions needed for my Laravel ticketing system app?

What do you mean by reguler web app do you mean app that uses blade or react ?

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Best way to systematically determine all permissions needed for my Laravel ticketing system app?

Best systematic way to plan all permissions for Laravel 12 ticketing system API with Spatie?

I'm building a comprehensive ticketing system API using Laravel 12 with Spatie Laravel-Permission for role-based authorization (roles like admin, manager, agent, customer).

I've implemented controllers for:

  • Tickets (CRUD)
  • Roles/permissions management (assign/revoke/view for roles)
  • User search/filtering

Current permissions (snake_case pattern): tickets: view_any_ticket, view_ticket, create_ticket, update_ticket, delete_ticket, assign_ticket roles: view_role_permissions, assign_role_permissions, revoke_role_permissions, update_role_permissions permissions: view_any_permission, create_permission, update_permission, delete_permission

My approach:

  • Map permissions to controller actions (create_ticket, update_ticket_status)
  • Policies + Gates (TicketPolicy::viewAny() checks $user->can('view_any_ticket'))
  • Artisan command to seed permissions

Code example RolesPermissionsController@assign:

public function assign(Request $request, Role $role)
{
    Gate::authorize('assign_permission_to_role', Permission::class);
    $role = RoleService::assignRole($role, $request->permissions);
    return PermissionsResource::collection(PermissionService::getPermissionsOfRole($role));
}

Struggling to identify ALL permissions without missing edge cases or over-engineering.

Questions:

What's the best systematic process to discover/plan permissions for ticketing system? (routes analysis, user stories, audit logs?)

Common permission patterns for ticketing apps?

    Ticket assignment

    Status changes

    Comments/attachments

    Escalations

    Reports/SLAs?

Granularity level?

    update_ticket (all fields)

    OR update_ticket_priority, update_ticket_status, update_ticket_assignee?

Tools/scripts to auto-generate permission seeds from routes/controllers?

Role/permission management auth?

    Direct Spatie: $user->can('view_role_permissions')

    OR Policies wrapping Spatie checks?
minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Does Gate::before bypass a policy if the callback returns true for a given permission?

Hi @tykus,

Thanks for your reply. In this case, will the before callback run by default, or do I need to explicitly call it via Gate::authorize in the controller?

 Gate::athorize("before" , Tickit::class)
minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

How does Laravel determine route specificity and matching priority?

I'm trying to get a solid grasp on how Laravel handles route specificity when multiple routes could potentially match a given URI. From what I've read, routes are matched based on the order they're defined, with more specific (static) routes taking precedence over parameterized ones. But I'd love some clarification and examples from experienced devs.

For instance:

  • If I have Route::get('/foo/bar', 'ControllerA') defined before Route::get('/foo/{slug}', 'ControllerB'), does / foo/bar always hit ControllerA first due to specificity?

  • What if both are parameterized, like /foo/{id} vs /foo/{slug}?

  • Any gotchas with route groups, middleware, or API routes?

Also, best practices for ordering routes in routes/web.php or routes/api.php to avoid surprises? php artisan route:list helps verify, but understanding the rules upfront would save headaches in larger apps.

minaremonshaker's avatar

minaremonshaker liked a comment+100 XP

5mos ago

Does Gate::before bypass a policy if the callback returns true for a given permission?

Yes, the Gate::before callback will bypass your TicketPolicy::view() method if it returns a non-null value.

Here's how it works:

  • When an ability is being checked (e.g. Gate::authorize('view', $ticket);), Laravel will first run any Gate::before callbacks.
  • If your Gate::before returns a non-null value (true or false), that value is immediately considered the final answer for the authorization check, and no further policy methods will be called for that ability check.
  • If your Gate::before returns null, Laravel falls through to the associated policy method (like view) and evaluates that as normal.

So, with your code:

Gate::before(function (User $user, $ability) {
    if ($user->hasAnyPermission(['view_any_ticket'])) {
        return true;
    }
    return null;
});

If the user has the view_any_ticket permission, Gate::before returns true, and Laravel grants access for any ability (including 'view'), without ever calling your policy's view method.

To sum up:
If Gate::before returns true, the TicketPolicy::view() method is not called and access is granted immediately.

Reference:
The official Laravel documentation on Gates says:

"If the before callback returns a non-null result that result will be considered the result of the authorization check."

So in your scenario, yes, the global Gate::before will bypass the policy method if it returns true.

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Does Gate::before bypass a policy if the callback returns true for a given permission?

I’m defining a global Gate::before check in my AppServiceProvider like this:

Gate::before(function (User $user, $ability) {
    if ($user->hasAnyPermission(['view_any_ticket'])) {
        return true;
    }
    return null;
});

In my TicketPolicy, the view method looks like this:

public function view(User $user, Ticket $ticket): bool
{
    if ($user->hasPermissionTo('view_ticket')) {
        return $user->id === $ticket->user_id;
    }
    return false;
}

Then, in my controller, I authorize like this:

Gate::authorize('view', $ticket);

If a user has the view_any_ticket permission through a “moderator” role, will the global Gate::before return true and completely bypass the TicketPolicy::view() method?

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Is it a good practice to assign direct permissions to users when using the Spatie permissions package?

hi i am using spaite permissions for authorization , I want to ask what are the situation where i can assign direct permissions to users

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Spatie Permissions + Laravel Policies: Best Practice for Permission/Role Controllers?

hi thank you for the answer , i will do that of course but i want to ask you about lary`s answer what do you think about it ?

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Spatie Permissions + Laravel Policies: Best Practice for Permission/Role Controllers?

hi thank you for the answer ,what i want is to make access control even for the permissions model and roles model where admin user can do every thing and manger can do spacific things in the admin panel

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Spatie Permissions + Laravel Policies: Best Practice for Permission/Role Controllers?

I'm building an admin panel using Spatie Laravel Permission and I'm confused about the "right way" to handle authorization in Permission/Role controllers.

Option A (Direct Spatie check):

if (!auth()->user()->hasPermissionTo('view_any_permission')) {
    abort(403);
}

Option B (Laravel Policy):

Gate::authorize('viewAny', Permission::class);
// Policy just wraps: return $user->hasPermissionTo('view_any_permission');

I've seen both approaches but get mixed advice:

  • Spatie docs seem to favor direct checks - Permission/Role models are "internal admin tools"

  • Laravel docs push policies - Consistent authorization pattern across app

  • My current setup: Already created PermissionPolicy ✅ registered ✅ working

Questions:

  • Is PermissionPolicy truly "over-engineering" for admin screens?

  • When Spatie already integrates with Gates ($user->can()), what's the value of wrapping it in a policy?

  • Community standard: Direct Spatie checks vs Policies for Permission/Role management?

Context:

 -  Laravel 12, Spatie v6

 -  Using policies successfully for business models (PostPolicy, UserPolicy, TicketPolicy)

 -  Permissions follow snake_case convention (view_any_permission)

Current working code:

Gate::authorize('viewAny', Permission::class);

// Policy
public function viewAny(User $user): bool
{
    return $user->hasPermissionTo('view_any_permission');
}

Should I keep the policy pattern for consistency, or switch Permission/Role controllers to direct Spatie checks for simplicity?

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

How should I authorize viewing a user’s tickets (collection) in an index endpoint?

Hi @glukinho, do you mean I should create a field called public and include it in my policy as well?

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

How should I authorize viewing a user’s tickets (collection) in an index endpoint?

In a Laravel API controller, I return a collection of tickets that belong to a specific user (“author”). The authenticated user may have multiple tickets, and I’m unsure what the best-practice authorization approach is for this index endpoint.

Goal: Allow the request only if the authenticated user has the view_ticket permission and is the same user as the $author route parameter.

What I tried:

  • Looping through each ticket and calling authorize() / Gate::authorize() per ticket.

  • Creating a dedicated gate (show-users-tickets) that receives the $author and checks permission + ownership.

class AuthorTicketController extends Controller
{
    public function index(Request $request, User $author)
    {
        $filters = $request->only(['per_page']);

        Gate::authorize('show-users-tickets', $author);

        $tickets = TicketsService::getAuthorTickets($author, $filters);

        return TicketResource::collection($tickets);
    }
}
Gate::define('show-users-tickets', function (User $user, User $author) {
    if ($user->hasPermissionTo('view_ticket')) {
        return $user->id === $author->id;
    }

    return false;
});

Question: Is this gate-based approach correct for authorizing access to a list of tickets, or should I be using a policy (e.g., TicketPolicy@viewAny / view) and/or authorizing at the query level instead? (Laravel documentation describes policies as a way to organize authorization logic around a particular model or resource.)

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Authorization Exception Not Triggered When Admin Has No Tickets

Hi, I want to prevent non-admin users from accessing other users' tickets. Each logged-in user should only see their own tickets.

For example, when I make a request to http://localhost:8000/api/authors/1/tickets, the 1 parameter represents an admin user. If this isn't the currently logged-in user, I expect to receive an unauthorized exception.

However, the issue is:

  • If user with ID 1 has no tickets, I get an empty array instead of an unauthorized exception

  • If user with ID 1 has tickets, the authorization works correctly and I get the unauthorized exception

I hope that clarifies my question.

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Authorization Exception Not Triggered When Admin Has No Tickets

hi i am doing this because i am using spaite query builder in my service to for searching and feltering

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Authorization Exception Not Triggered When Admin Has No Tickets

In the controller method below, I'm experiencing an authorization issue. When an admin has no tickets, I receive an empty array [] instead of an authorization exception. However, when tickets exist for the admin, I correctly get the unauthorized exception. The expected behavior is that I should receive an authorization exception whenever I try to access admin tickets, regardless of whether tickets exist or not.

NOTE: i have sloved this by creating a gate in the AppServiceProvider in the boot method as below but i don`t know if that is correct or not

      Gate::define('showUserTicketList', function (User $user ,User $author) {

           if($user->hasPermissionTo('ShowOwnTicket')) {
                return $user->id === $author->id;
           }

          return false;
      });
  • controller method :

     public function index(Request $request, User $author)
    {
    
        // $filters = $request->only(['per_page']);
    
        $tickets = TicketsService::findTicketsByUserId($author);
    
        foreach ($tickets as $ticket) {
            Gate::authorize('view', [Ticket::class, $ticket]);
        }
    
        return TicketResource::collection($tickets);
    
    }
    
  • Service method:

    public static function findTicketsByUserId(User $author)
    {
        return QueryBuilder::for(Ticket::class)
            ->whereUserId($author->id)
            ->paginate();
    }
    
  • Policy: (TicketPolicy)

          public function viewAny(User $user): bool
      {
          if ($user->hasPermissionTo('ShowTicket')) {
              return true;
          }
    
          return false;
      }
    
minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Validating Role Assignments via Form Request

Hi, Thank you for your reply. I just wanted to let you know that I’m currently working only on the API and haven’t created the UI yet. But you’re right — I can use a checkbox group when I build it.

minaremonshaker's avatar

minaremonshaker liked a comment+100 XP

5mos ago

Validating Role Assignments via Form Request

Use a multi select dropdown for the roles, or use a checkbox group. They both give you an array of roles.

minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Validating Role Assignments via Form Request

the contains dose not works as expected , i solved the problem by adding :input to the custom message , the :input gets the value as expected

    public function messages(): array
    {
        return [
            "roles.*.exists" => "the role :input dose not exists in our database" ,
        ];
    }
minaremonshaker's avatar

minaremonshaker wrote a reply+100 XP

5mos ago

Validating Role Assignments via Form Request

Hi, I followed your suggestion and am now sending the roles as a JSON array. However, I am facing another issue related to customizing the validation messages. I need to include the actual invalid values from the submitted array in the error message. When one of the array values fails validation, the error message I receive looks like the default one, but I would like to override it using the value itself.

From the documentation, I found several approaches, such as using the index or position placeholders, or using Rule::forEach, but they still rely on indexes or positions. Is there a better approach to achieve this?

https://laravel.com/docs/12.x/validation#accessing-nested-array-data

https://laravel.com/docs/12.x/validation#error-message-indexes-and-positions

data sent :

{
	"roles": [
		"admin",
		"user",
		"manager"
	]

}

request :

class AssignRoleToUserRequest extends FormRequest
{

    public function rules(): array
    {

        return [
            "roles" => ['required', 'array'],
            "roles.*" => ['exists:roles,name']
        ];
    }

    public function messages(): array
    {
        return [
            "roles.*.exists" => "the roles array contains :attribute that are not exists in our database" ,
        ];
    }


    public function authorize(): bool
    {
        return true;
    }
}

validation error message

{
	"message": "the roles array contains roles.2 that are not exists in our database",
	"errors": {
		"roles.2": [
			"the roles array contains roles.2 that are not exists in our database"
		]
	}
}
minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Validating Role Assignments via Form Request

I have a controller method that assigns roles to users. The request includes a string containing the roles to assign, and I need to validate that each specified role exists in the roles table using a form request. Currently, I created a custom validation rule that converts the string into an array, loops through each role, and checks if it exists in the database—failing if any do not. I’m wondering if there’s an alternative approach to achieve this validation without creating a custom rule, or if that’s the only viable solution.

  • validation custom rule
class UserHasRole implements ValidationRule
{


    public function __construct(protected int $user_id){}
    /**
     * Run the validation rule.
     *
     * @param Closure(string, ?string=): PotentiallyTranslatedString $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
         $roles = explode(",", $value);

         $user = \App\Models\User::find($this->user_id);

         foreach ($roles as $role){
             if($user->hasRole($role)){
                 $fail("user has role $role");
             }
         }

    }
}
  • form request
class AssignRoleToUserRequest extends FormRequest
{
    public function rules(): array
    {
        $user_id = $this->route()->parameter('user');

        return [
            "roles" => ['required', 'string', new UserHasRole($user_id),]
        ];
    }

    public function authorize(): bool
    {
        return true;
    }
}
  • controller method
    public function assign(AssignRoleToUserRequest $request, int $user_id)
    {

    }
minaremonshaker's avatar

minaremonshaker liked a comment+100 XP

5mos ago

minaremonshaker's avatar

minaremonshaker started a new conversation+100 XP

5mos ago

Organizing API Routes into Separate Files

Hi, my routes/api.php file has become quite large, so I’d like to split the routes into separate PHP files inside the routes directory. I don’t want to register them manually in app.php—I just plan to include those files directly within api.php. Is that a good approach, or are there better alternatives?