0
votes

Application Structure

  • App/Http/Livewire
  1. UserController - Livewire/UserManagement/UserController
  2. WithBulkActions - Livewire/Traits/DataTable/WithBulkActions
  • resources/views/livewire
  1. UserController Blade - livewire/user-management/user-controller

Description

I am creating a CRUD system, with bulk actions to delete the selected data. I have extracted the logic which is responsible to select multiple items or to select all items in the current data table i.e UserController, so I can use it as a trait i.e. WithBulkActions, in other data table components. The trait is only responsible to select multiple items or to select all items from the data table. The public function method to delete an array of data, is present in the data table component controller itself i.e. UserController. Now, to make the multiple select with bulk actions work, I have to include the confirmation modal, which is responsible to fire the method to delete an array of data, in the data table component blade view i.e. UserController Blade. It works perfectly when I include the confirmation modal to delete the selected data inside the user controller. While each row has a checkbox modeled to wire:model="selected" with the value="{{ $user->id }}" within the foreach loop. The public variable selected is initialized as public $selected = []; within the trait, WithBulkActions and then the trait is being used in the component, UserController.

rowsQuery property within the UserController component gets the data from the User model in a query with the filters, sorting and return it as a property.

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);
    }

Then the rowsQuery is cloned in the selectedRowsQuery property within the trait, WithBulkActions.

public function getSelectedRowsQueryProperty()
    {
        return (clone $this->rowsQuery)
            ->unless($this->selectAll, fn($query) => $query->whereKey($this->selected));
    }

If incase an array of data is being selected and the selected data should be deleted, the public function confirmDeleteBulk() is responsible for it, which is in UserController component, the following public function calls for selectedRowsQuery property, which is initialized in the trait itself, and then it executes the delete() method and notify accordingly.

public function confirmDeleteBulk()
    {
        $deleteCount = $this->selectedRowsQuery->count();
        foreach ($this->selectedRowsQuery->get() as $queryRow) {
            $queryRow->roles()->detach();
        }
        $this->selectedRowsQuery->delete();
        $this->showUserBulkDeletionModal = false;
        $this->notify('You\'ve deleted '.$deleteCount.' users');
    }

I tried making a separate livewire component with public function method to delete the selected data by displaying a confirmation modal but I was unable to pass the selected number of rows to the UserBulkDeletion nested component located at, Livewire/UserManagement/LogicalComponent/UserBulkDeletion. Please tell me the best use case in this situation, incase if what I want is possible. Help will be really appreciated, as I'm trying to do this since 2 weeks now.

Context

  • Livewire version: 2.3.8
  • Laravel version: 8.24.0
  • Alpine version: 2.8.0
  • Browser: Chrome

Edit

  • Livewire/UserManagement/UserController
class UserController extends Component
{
    use WithCachedRows, WithSorting, WithBulkActions, WithPerPagePagination;

    public User $user;
    public $showFilters = false;
    public $filters = [
        'search' => "",
        'email' => null,
        'role' => '',
        'date-min' => null,
        'date-max' => null,
    ];

    protected $listeners = ['sectionRefresh' => '$refresh'];

    public function toggleShowFilters()
    {
        $this->useCachedRows();

        $this->showFilters = ! $this->showFilters;
    }

    public function resetFilters() 
    {
        $this->reset('filters'); 
    }

    public function updatedFilters() 
    {
        $this->resetPage();
    }
    
    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);
    }

    public function getRowsProperty()
    {
        return $this->cache(function () {
            return $this->applyPagination($this->rowsQuery);
        });
    }

    public function getRolesProperty()
    {
        $roles = Role::all();
        return $roles;
    }
    
    public function render()
    {
        return view('livewire.backend.management.audience-management.user-controller', ['users' => $this->rows, 'roles' => $this->roles]);
    }
}
  • Livewire/UserManagement/LogicalComponent/UserBulkDeletion
class UserBulkDeletion extends Component
{
    use WithCachedRows, WithBulkActions, WithSorting;
    
    public $showUserBulkDeletionModal = false;
    public User $user;
    public $filters = [
        'search' => "",
        'email' => null,
        'role' => '',
        'date-min' => null,
        'date-max' => null,
    ];

    protected $listeners = ['deleteUserBulk'];

    public function deleteUserBulk()
    {
        $this->useCachedRows();

        $this->showUserBulkDeletionModal = true;
    }

    public function confirmDeleteBulk()
    {
        $deleteCount = $this->selectedRowsQuery->count();
        
        foreach ($this->selectedRowsQuery->get() as $queryRow) {
            $queryRow->roles()->detach();
        }

        $this->selectedRowsQuery->delete();
        $this->showUserBulkDeletionModal = false;

        $this->notify('You\'ve deleted '.$deleteCount.' users');
        $this->emit('sectionRefresh');
    }

    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);
    }

    public function render()
    {
        return view('livewire.backend.management.audience-management.logical-component.user-bulk-deletion');
    }
}
  • Livewire/Traits/DataTable/WithBulkActions
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));
    }
}
  • views/livewire/user-management/logical-component/user-bulk-deletion
<div>
    <form wire:submit.prevent="confirmDeleteBulk">
        <x-modal.confirmation wire:model.defer="showUserBulkDeletionModal">
            <x-slot name="title">Delete User</x-slot>

            <x-slot name="content">
                @json($selected)
                Are you sure you want to delete users? This action is irreversible!
            </x-slot>

            <x-slot name="footer">
                <x-button.secondary wire:click="$set('showUserBulkDeletionModal', false)">Cancel</x-button.primary>

                <x-button.primary type="submit">Delete</x-button.primary>
            </x-slot>
        </x-modal.confirmation>
    </form>
</div>
1

1 Answers

0
votes

To pass values from one Livewire component to another use events.

Livewire components can communicate with each other through a global event system. As long as two Livewire components are living on the same page, they can communicate using events and listeners.

In one of your Livewire components you trigger an event like

$this->event('usersToDelete', [ 1,2,3 ]);

In another component you can listen for this event

public class MyOtherComponent extends Component
{

    protected $listeners = [
      'usersToDelete' => 'deleteUsers'
    ];

    public function deleteUsers(array $user)
    {
      // .. do something
    }
}