-
-
Notifications
You must be signed in to change notification settings - Fork 137
Relationship columns doesn't work? #93
Comments
@darkons you can aggregate the relations columns in the query builder:
Then use like this:
|
Thank you @jbunning! Now the value is displayed correctly in the table. However I can make the column searchable because: but if I add the provider_name filter to query builder allowedFilters: Tried with |
@darkons Ran into the same problems myself, I was able to find solutions but it got a bit ugly. Already I replaced the query builder for Laravel's one instead of the Spatie package, then had to do things like this:
Basically reporting searches, and sorting manually, adding aliases for ambiguous columns, etc. It seems to be working now, even if it's a bit hectic. Hope the package devs will find a nicer solution ;) |
After some research I think I have found the reason why the table does not show nested values from relationships: Example: $users = QueryBuilder::for(User::class)
->defaultSort('name')
->allowedSorts(['name', 'email'])
->allowedFilters(['name', 'email', 'phone.number'])
->with('phone') // Eager load phone relation
->paginate()
->withQueryString();
return Inertia::render('Users/Index', [
'users' => $users,
])->table(function (InertiaTable $table) {
$table
->defaultSort('name')
->column(key: 'name', searchable: true, sortable: true, canBeHidden: false)
->column(key: 'email', searchable: true, sortable: true)
->column(key: 'phone.number', searchable: true)
->column(label: 'Actions'); With the current version the <slot :name="`cell(${column.key})`" :item="item">
{{ item[column.key] }}
</slot> Using Lodash <slot :name="`cell(${column.key})`" :item="item">
{{ get(item, column.key) }}
</slot> Example of how it works https://playcode.io/979205 It is actually a very simple fix. Hopefully @pascalbaljet can take a look at it and fix it :) |
Can you provide a less generic example, sorry I am having issues implementing this in my project. |
Any updates on this? I'd love to keep the source intact, although the @darkons's fix works like a charm. |
For now, I also used aggregation and decided not to change the source of the table as our deployment process does not like manual interference. Instead I used a template and dumped the content, there but with the disadvantage, that neither sorting nor searching works very well...
And do some templating: This works but the name club.name is actually invalid and you might see some warning like in my current VSCode: "'v-slot' directive doesn't support any modifier.eslint-plugin-vue" @SDIjeremy Then you can load the relation and aggregate its name attribute: You can then access the aggregated variable in your column but I failed to make sorting and filtering work... I assume it is a generic problem: spatie/laravel-query-builder#91 however, I did not make sorting work, yet |
I've been looking at this issue, for now I've just ended up restructuring our data. |
@MaticSulc for sorting I had to just restructure my data as well, I think it's probably for the better anyways. |
If you are still wondering how to filter a relationship, here is the most complicated case, then One-to-many is easy. Many-to-manyExample: users-roles relationship, filter by role name. UserService.php$roleSearch = AllowedFilter::callback('roles_name', static function ($query, $value) {
$query->where(function ($query) use ($value) {
Collection::wrap($value)->each(function ($value) use ($query) {
$query->orwhereHas('roles', function ($subquery) use ($value) {
$subquery->where('name', 'LIKE', "%{$value}%");
});
});
});
}); You definitely want to put it in the global filter. $globalSearch = AllowedFilter::callback('global', static function ($query, $value) {
$query->where(function ($query) use ($value) {
Collection::wrap($value)->each(function ($value) use ($query) {
// Copy: Begin
$query->orwhereHas('roles', function ($subquery) use ($value) {
$subquery->where('name', 'LIKE', "%{$value}%");
});
// Copy: End
$query
->orWhere('first_name', 'LIKE', "%{$value}%")
->orWhere('last_name', 'LIKE', "%{$value}%")
->orWhere('email', 'LIKE', "%{$value}%")
// Practically you want to do this so your search is more human-friendly.
->orWhere(DB::raw("CONCAT(first_name, ' ', last_name)"), 'LIKE', "%$value%");
});
});
}); Then add the custom filter: $users = QueryBuilder::for(User::class)
// This has nothing to do with the filter, just to provide the role names, see the end of this comment
->with('roles:id,name')
->defaultSort('first_name')
->allowedSorts(['first_name', 'last_name', 'email'])
->allowedFilters(['first_name', 'last_name', 'email', $roleSearch, $globalSearch])
// A lil sugar, in case your pagination won't work out of the box
// It was not your fault, the example provided was wrong.
->paginate($queryParams['perPage'] ?? 15)
->appends($queryParams); UserController.phpreturn Inertia::render('User/Index', ['users' => $users])->table(function (InertiaTable $table) {
...
$table->column(key: 'roles_name', label: 'Role', searchable: true);
...
}); You don't sort a many-to-many relationship for obvious reason. Even if you want, it's still achievable, just define your rule using This way you can get rid of Below is how you can display all the role names in Vue. User/Index.vue<Table :resource="users">
...
<template #cell(roles_name)="{ item: user }">
<div class="flex space-x-1">
<div v-for="role in user.roles">
<span>{{ role.name }}</span>
</div>
</div>
</template>
...
</Table> |
Thanks @musiwei - it works as intended, although I can't get my one to many relationships to show in the table. |
Glad that I helped. Could you please paste your code? It's more intuitive and others can learn from it too. |
Basically, I have a "Ticket", that can have an "Owner". The owner has props like name, address ... $ownerSearch = AllowedFilter::callback('owner_name', static function ($query, $value) {
$query->where(function ($query) use ($value) {
Collection::wrap($value)->each(function ($value) use ($query) {
$query->orwhereHas('owner', function ($subquery) use ($value) {
$subquery->where('name', 'LIKE', "%{$value}%");
});
});
});
});
$globalSearch = AllowedFilter::callback('global', static function ($query, $value) {
$query->where(function ($query) use ($value) {
Collection::wrap($value)->each(function ($value) use ($query) {
$query->orwhereHas('owner', function ($subquery) use ($value) {
$subquery->where('name', 'LIKE', "%{$value}%");
});
$query
->orWhere('number', 'LIKE', "%{$value}%");
});
});
});
//inside QueryBuilder
->with('owner:id,name')
->allowedFilters(['number', $globalSearch, $ownerSearch])
//inside Table call
->column(key: 'owner_name', label: 'Owner', searchable: true, sortable: false, canBeHidden: false) It works by doing the folowind in Vue, but I'd expect a better solution here. <template #cell(owner_name)="{ item: order }">
{{ order.owner.name }}
</template> |
What do you mean by 'a better solution'? Isn't this working as intended? What is a better solution from your perspective? |
@musiwei By simply doing owner_name inside the column key, I'd expect the data to get extracted - not that I have to create a slot and extract it myself. |
I see. However, that is not a better solution, you are looking for an over-simplified solution that contains less code. This topic can be quite big, and I'd like to share some thoughts with you because this may or may not help you become a better developer. To start with, you have fantastic attitude. At the beginning of my career, I also try to find the simplest way to write code, it definitely helps learning because you constantly seek better ways. However, only within a few years that I realised everything in software development is a tradeoff, with more simplicity, you lose more customisation. In addition, as a senior software developer in the industry for 10+ years, I have seen many complicated applications, compared with those, this implementation is so clean, with great room to customise. This is actually one of the best Vue plugins that I have used recently. The author clearly has extensive amount of commercial experience and uses a very smart and clean way to tackle the table cell customising issue - which used to require you to write a full page of dirty code. I am not playing the 'experience' card to convince you, but just trying to say that your mind evolves, and you will work on complex projects. This level of complexity is quite okay, and it also encourages you to think about how to take more advantage of it. If there is only one thing that I am certain: there is no perfect code. |
I tried my first table with relation columns and I can't get them to work properly.
My query builder:
What am I doing wrong?
Thank you!
The text was updated successfully, but these errors were encountered: