minaremonshaker started a new conversation+100 XP
2mos ago
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 wrote a reply+100 XP
2mos ago
minaremonshaker started a new conversation+100 XP
2mos ago
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 wrote a reply+100 XP
2mos ago
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 started a new conversation+100 XP
2mos ago
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 liked a comment+100 XP
2mos ago
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 anyGate::beforecallbacks. - If your
Gate::beforereturns a non-null value (trueorfalse), 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::beforereturnsnull, Laravel falls through to the associated policy method (likeview) 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 started a new conversation+100 XP
2mos ago
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 started a new conversation+100 XP
2mos ago
minaremonshaker wrote a reply+100 XP
2mos ago
minaremonshaker wrote a reply+100 XP
2mos ago
minaremonshaker started a new conversation+100 XP
2mos ago
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 wrote a reply+100 XP
2mos ago
Hi @glukinho, do you mean I should create a field called public and include it in my policy as well?
minaremonshaker started a new conversation+100 XP
2mos ago
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 wrote a reply+100 XP
3mos ago
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 wrote a reply+100 XP
3mos ago
minaremonshaker started a new conversation+100 XP
3mos ago
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 wrote a reply+100 XP
3mos ago
minaremonshaker liked a comment+100 XP
3mos ago
minaremonshaker wrote a reply+100 XP
3mos ago
minaremonshaker wrote a reply+100 XP
3mos ago
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 started a new conversation+100 XP
3mos ago
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 liked a comment+100 XP
3mos ago
minaremonshaker started a new conversation+100 XP
3mos ago
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?
minaremonshaker wrote a reply+100 XP
3mos ago
Hi, I’ve created a new version where I need to sort and search the results. Of course, I can normally handle this using the built-in Eloquent model — that’s the current case.
note: this is not the last version i still making updates to it
- this from the service
public static function getPermissionsOfRolesName(string $role, ?array $filters)
{
extract($filters, EXTR_IF_EXISTS);
return QueryBuilder::for(Role::findByName($role)->permissions())
->defaultSort('id')
->allowedSorts([
'name',
AllowedSort::field('created', 'created_at'),
AllowedSort::field('updated', 'updated_at'),
])
->paginate(10);
}
minaremonshaker wrote a reply+100 XP
3mos ago
i solved the problem by reading this part of spite permission docs https://spatie.be/docs/laravel-query-builder/v6/advanced-usage/extending-query-builder
and i implemented it like this
public static function getPermissionsOfRolesName(string $role)
{
return QueryBuilder::for(
Role::findByName($role)->permissions()
)->paginate(10);
}
what do you think ?
minaremonshaker started a new conversation+100 XP
3mos ago
Hi everyone,
In my Laravel API project using Spatie Laravel Permission (with Sanctum guard) and Spatie Query Builder, I'm trying to fetch permissions for a specific role like 'user', but running into an issue with QueryBuilder.
These attempts fail with Call to undefined method Spatie\QueryBuilder\QueryBuilder::findByName():
QueryBuilder::for(Role::class)->where('name', 'user')->permissions()->get()
or
QueryBuilder::for(Role::class)->findByName('user', 'sanctum')->permissions()
However, this direct Eloquent call works perfectly and returns all related permissions:
Role::findByName('user', 'sanctum')->permissions()
Why can't I chain findByName() or similar scopes directly on the QueryBuilder instance?
Is there a proper way to combine Spatie Query Builder with Role model scopes/finders from laravel-permission to get a role by name and then load its permissions? Should I use allowedIncludes('permissions') or a different approach like where('name', 'user')->with('permissions')?
Using Laravel 12, latest Spatie packages. Thanks!
minaremonshaker wrote a reply+100 XP
3mos ago
I passed the authenticated user's ID to the service, so the ticket will be created exclusively for that user. Therefore, I don’t think any additional verification of the user ID is necessary since it’s assigned automatically.
- Controller Method:
public function store(StoreTicketRequest $request)
{
$this->authorize('create' , Ticket::class);
$ticket = TicketsService::create($request->validated());
return TicketResource::make($ticket);
}
- Store Ticket Request:
class StoreTicketRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'title' => ['required' , 'max:100'],
'description' => ['required'],
'status' => ['required' , Rule::in(['A', 'C' , 'P'])],
/* 'user_id' => ['required','exists:users,id'],*/
];
}
public function messages(): array
{
return [
'status.in' => 'The status field needs to be in A,C,P',
/*'user_id.exists' => 'There is no user with the provided id'*/
];
}
}
- Policy:
public function create(User $user ): bool
{
if ($user->hasPermissionTo('CreateTicket') || $user->hasPermissionTo('CreateOwnTicket')) {
return true;
}
return false;
}
- TicketService :
public static function create(array $validated)
{
$validated = Arr::add($validated, 'user_id', auth()->user()->id);
return Ticket::create($validated);
}
minaremonshaker wrote a reply+100 XP
3mos ago
minaremonshaker started a new conversation+100 XP
3mos ago
Hi, I have a question about Laravel policies. Is it possible to define a custom method in a policy, like createOwn, or is it better to stick with the default methods provided in the policy stubs?
I'm asking because, in the method below, I want to ensure that a user can only create their own ticket, so I plan to pass the ticket ID as an argument.
public function create(User $user): bool
{
if ($user->hasPermissionTo('CreateTicket')) {
return true;
}
return false;
}
minaremonshaker wrote a reply+100 XP
3mos ago
minaremonshaker wrote a reply+100 XP
3mos ago
what make me surprised that the create got the foreign key but i cant see in in phpmyadmin digram
CREATE TABLE `tickets` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`title` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
`status` varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_USER_TICKETS` (`user_id`),
FULLTEXT KEY `tickets_title_fulltext` (`title`) /*!50100 WITH PARSER `ngram` */ ,
FULLTEXT KEY `tickets_description_fulltext` (`description`) /*!50100 WITH PARSER `ngram` */ ,
FULLTEXT KEY `tickets_title_description_fulltext` (`title`,`description`) /*!50100 WITH PARSER `ngram` */ ,
CONSTRAINT `FK_USER_TICKETS` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
minaremonshaker wrote a reply+100 XP
3mos ago
minaremonshaker wrote a reply+100 XP
3mos ago
minaremonshaker wrote a reply+100 XP
3mos ago
hi , first of all Mary Christmas and hope you are doing well , please can you clarify more, i am adding a forging key to the tickets table cause there are a one to many relation but the problem i that the method above dose not create it in the database tickets table , also for the key name i like to make all of the keys start with specific prefix like FL, UQ , etc...
minaremonshaker started a new conversation+100 XP
3mos ago
Hi, I’m encountering an issue with a migration that creates a tickets table, which has a one-to-many relationship with the users table. Normally, the tickets table should include a foreign key referencing the users table. However, when I try to define it using: $table->foreignId('user_id')->constrained(indexName: 'FK_USER_TICKETS')->onDelete('cascade'); it only creates the user_id column without actually generating the foreign key constraint in the database, even though I expected it to.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('tickets', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained(indexName: 'FK_USER_TICKETS')->onDelete('cascade');
$table->string('title' , 40);
$table->text('description');
$table->string('status');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tickets');
}
};
minaremonshaker wrote a reply+100 XP
3mos ago
hi @martinbean
What I mean is that without using the Spatie permissions package, I would need to assign specific abilities to each user token, and update those abilities whenever I want the token to perform certain actions. However, when using Spatie permissions, this process is overridden — meaning there’s no need to manually assign abilities to the token.
minaremonshaker started a new conversation+100 XP
3mos ago
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker started a new conversation+100 XP
4mos ago
hi Merry Christmas! I have a UsersService class with a method that handles filtering and sorting data from the request. Is it good practice to pass the Request object directly to this service method (or inject it via the service provider), or should I avoid it? If not, what are the better alternatives?
note: i am using spaite query builder package
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker started a new conversation+100 XP
4mos ago
Hi,
I want to implement sorting on my users table using some columns from related tables — for example, date_of_birth from the profiles table and status from the tickets table — combined with columns from the users table itself.
Currently, I’m constructing the URL like this:
http://localhost:8000/authors?search=Co&searchBy=first_name&sort=profiles.date_of_birth,users.status
In this format, the part before the dot represents the table name, and the part after the dot represents the column I want to sort by. Is this approach correct?
Think also for safety from injection i can make a array inside the class that contains the feilds that i want to sort with and check if value provided is included in the array of sortable other than that i can ignore
Notes:
- There’s a one-to-one relationship between the users and profiles tables.
- There’s a one-to-many relationship between the users and tickets tables.
included my UsersFilters class where there are sort method
<?php
namespace App\Filters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
class UsersFilter extends BaseFilter
{
protected array $searchFields = [
'first_name',
'last_name',
'email',
'username',
];
protected array $relations = [
'profiles',
'tickets',
];
protected array $sortable = [
'users.status',
'users.created_at',
'users.updated_at',
'users.email_verified_at',
'profiles.date_of_birth',
'profiles.gender',
'profiles.created_at',
'profiles.updated_at',
'tickets.status',
'tickets.created_at',
'tickets.updated_at',
];
protected function filter(array $filters = []): Builder
{
return $this->builder;
}
public function DOB(...$ranges): Builder
{
return $this->builder;
}
protected function sort(...$fields): Builder
{
$sortableFields = explode(',', ...$fields);
foreach ($this->relations as $relation) {
$this->builder->join($relation, $this->modelTable . '.id', '=', $relation . '.user_id');
}
foreach ($sortableFields as $field) {
$table = Str::before($field, '.');
$column = Str::after($field, '.');
if (!in_array(Str::after($field, '-'), $this->sortable)) {
continue;
}
if (Str::startsWith($field, '-')) {
clock(Str::after($table . '.' . $column, '-'));
$this->builder->orderByDesc(Str::after($table . '.' . $column, '-'));
} else {
$this->builder->orderBy(Str::after($table . '.' . $column, '-'));
}
}
return $this->builder;
}
}
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker wrote a reply+100 XP
4mos ago
hi i still have the same problem here , below is another test that i have tried , this test dose not work unless i used the DatabaseTruncation triat in my test class , this is confusing
public function ListUsersSearchedByTerm() {
User::factory()->createMany([
[
'first_name' => 'mina',
'last_name' => 'remon',
'email' => '[email protected]',
'username' => 'mina20088',
],
[
'first_name' => 'beshoy',
'last_name' => 'shaker',
'email' => '[email protected]',
'username' => 'beshoy20088',
]
]);
$request = new Request([
'search' => 'mina'
]);
$userService = new UserService(User::query(),new UsersFilter($request));
$users = $userService->listUsers();
$this->request = new Request([
'search' => 'mina'
]);
$this->assertCount(1, $users);
}
minaremonshaker wrote a reply+100 XP
4mos ago
hi ,
i have implemented what you said in the post above but it still fails , included in my post here the
1-test method 2-test result from the console
public function users_search_returns_results_for_existing_data(array $payload)
{
$users = User::factory()->createMany($payload);
$user = $users->find($users[0]->id);
$response = $this
->withHeader("Accept", 'application/json')
->actingAs($user)
->getJson(route('authors.index', ['search' => 'b']));
$response->assertStatus(200);
$response->assertJsonCount(1, 'data');
}
test Results
/bin/php /home/mina/Desktop/Tickets/vendor/phpunit/phpunit/phpunit --configuration /home/mina/Desktop/Tickets/phpunit.xml --filter "/(Tests\\Feature\\AuthorTest::users_search_returns_results_for_existing_data)( .*)?$/" --test-suffix AuthorTest.php /home/mina/Desktop/Tickets/tests/Feature --teamcity
Testing started at 2:03 AM ...
PHP Warning: PHP Startup: Unable to load dynamic library 'couchbase' (tried: /usr/lib/php/20240924/couchbase (/usr/lib/php/20240924/couchbase: cannot open shared object file: No such file or directory), /usr/lib/php/20240924/couchbase.so (/usr/lib/php/20240924/couchbase.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
PHPUnit 11.5.46 by Sebastian Bergmann and contributors.
Runtime: PHP 8.4.15
Configuration: /home/mina/Desktop/Tickets/phpunit.xml
Failed to assert that the response count matched the expected 1
Failed asserting that actual size 0 matches expected size 1.
/home/mina/Desktop/Tickets/vendor/laravel/framework/src/Illuminate/Testing/AssertableJsonString.php:74
/home/mina/Desktop/Tickets/vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:1006
/home/mina/Desktop/Tickets/tests/Feature/AuthorTest.php:81
Time: 00:07.109, Memory: 44.50 MB
There was 1 failure:
1) Tests\Feature\AuthorTest::users_search_returns_results_for_existing_data with data set "users_to_create" ([['mina', 'remon', '[email protected]', 'mina20088'], ['beshoy', 'shaker', '[email protected]', 'beshoy20088']])
Failed to assert that the response count matched the expected 1
Failed asserting that actual size 0 matches expected size 1.
/home/mina/Desktop/Tickets/vendor/laravel/framework/src/Illuminate/Testing/AssertableJsonString.php:74
/home/mina/Desktop/Tickets/vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:1006
/home/mina/Desktop/Tickets/tests/Feature/AuthorTest.php:81
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
Process finished with exit code 1
minaremonshaker started a new conversation+100 XP
4mos ago
Hello, I want to understand why this test fails specifically with the RefreshDatabase trait but succeeds with DatabaseTruncation. The failing assertion is $response->assertJsonCount(1, 'data')—all other assertions and the complete test method work fine.
I've tested other database traits, but none resolve the issue. Here's the test code:
#[Test]
/*#[DataProviderExternal(AuthorsDataProvider::class, 'validAuthors')]*/
public function users_returned_when_searched(/*array $payload*/)
{
$users = User::factory()->createMany([
[
'first_name' => 'mina',
'last_name' => 'remon',
'email' => '[email protected]',
'username' => 'mina20088',
],
[
'first_name' => 'beshoy',
'last_name' => 'shaker',
'email' => '[email protected]',
'username' => 'beshoy20088',
]
]);
$user = $users->find(1);
$response = $this
->withHeader("Accept", 'application/json')
->actingAs($user)
->getJson(route('authors.index', ['search' => 'b']));
$response->assertJsonCount(1, 'data');
$this->assertAuthenticated();
$this->assertAuthenticatedAs($user);
$response->assertStatus(200);
}
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker wrote a reply+100 XP
4mos ago
minaremonshaker wrote a reply+100 XP
4mos ago