I have a "belongsToMany" eloquent relationship between users and roles . I am creating a CRUD system with capability to do bulk actions. I followed the Livewire Screencasts Bulk Export/Delete and Refactoring For Re-Usability. I have created a trait for the bulk actions, so I'm able to use it out of the box.
I need to detach the role from the user and then delete the user in bulk. I am unable to call roles
relationship on a public method property and detach the same. This is how I detach the role for a single user $this->user->roles()->detach();
but I'm unable to do it with $this->selectedRowsQuery->roles()->detach();
in case of bulk user deletion.
public $showUserBulkDeletionModal = false;
public function confirmDeleteBulk()
{
$deleteCount = $this->selectedRowsQuery->count();
$this->selectedRowsQuery->roles()->detach();
$this->selectedRowsQuery->delete();
$this->showUserBulkDeletionModal = false;
$this->notify('You\'ve deleted '.$deleteCount.' users');
}
public function getRowsQueryProperty()
{
$query = User::query()
->when($this->filters['email'], fn($query, $email) => $query->where('email', 'like', '%'.$email.'%'))
->when($this->filters['role'], fn($query, $role) => $query->whereHas('roles', fn ($query) => $query->where('id', $role)))
->when($this->filters['search'], fn($query, $search) => $query->where('name', 'like', '%'.$search.'%'))
->when($this->filters['date-min'], fn($query, $created_at) => $query->where('created_at', '>=', Carbon::createFromFormat('d/m/Y', $created_at)))
->when($this->filters['date-max'], fn($query, $created_at) => $query->where('created_at', '<=', Carbon::createFromFormat('d/m/Y', $created_at)));
return $this->applySorting($query);
}
trait WithBulkActions
{
public $selectPage = false;
public $selectAll = false;
public $selected = [];
public function renderingWithBulkActions()
{
if ($this->selectAll) $this->selectPageRows();
}
public function updatedSelected()
{
$this->selectAll = false;
$this->selectPage = false;
}
public function updatedSelectPage($value)
{
if ($value) return $this->selectPageRows();
$this->selectAll = false;
$this->selected = [];
}
public function selectPageRows()
{
$this->selected = $this->rows->pluck('id')->map(fn($id) => (string) $id);
}
public function selectAll()
{
$this->selectAll = true;
}
public function getSelectedRowsQueryProperty()
{
return (clone $this->rowsQuery)
->unless($this->selectAll, fn($query) => $query->whereKey($this->selected));
}
}
Context
This line won't work:
$this->selectedRowsQuery->roles()->detach();
$this->selectedRowsQuery
is a Collection, and your code isn't smart enough to know which instance of roles()
you're trying to detach (delete). You simply need to do this in a loop:
foreach ($this->selectedRowsQuery as $queryRow) {
$queryRow->roles()->detach();
$queryRow->delete(); // Can do this here (single delete), or below
}
$this->selectedRowsQuery->delete(); // Can do this here (batch delete), or above
Edit: At the time of the foreach()
, $this->selectedRowsQuery
is still an instance of the Builder
class, which is incompatible with foreach()
until a Closure (get()
, cursor()
, etc.) is passed. To handle this, simply adjust your code as:
foreach ($this->selectedRowsQuery->get() as $queryRow) {
$queryRow->roles()->detach();
...
}
Note: ->get()
is more widely used, but ->cursor()
is available and generally more performant for larger loops.