We believe that Laravel Livewire is really powerful for dynamic elements on the pages, so every inner page in our adminpanel is a Livewire component that you can customize however you want.
So, the main project structure is good old Laravel with Routes and Blade, and inside of each resources/views/admin/xxxxx.blade.php you will find a line similar to this:
It means that there are three separate Livewire components for each CRUD:
Create Page;
Edit Page;
Index Page.
Typical code of Livewire component:
app/Http/Livewire/Transaction/Index.php
<?php
namespace App\Http\Livewire\Transaction;
use App\Http\Livewire\WithConfirmation;
use App\Http\Livewire\WithSorting;
use App\Models\Transaction;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Gate;
use Livewire\Component;
use Livewire\WithPagination;
class Index extends Component
{
use WithPagination;
use WithSorting;
use WithConfirmation;
public int $perPage;
public array $orderable;
public string $search = '';
public array $selected = [];
public array $paginationOptions;
protected $queryString = [
'search' => [
'except' => '',
],
'sortBy' => [
'except' => 'id',
],
'sortDirection' => [
'except' => 'desc',
],
];
public function getSelectedCountProperty()
{
return count($this->selected);
}
public function updatingSearch()
{
$this->resetPage();
}
public function updatingPerPage()
{
$this->resetPage();
}
public function resetSelected()
{
$this->selected = [];
}
public function mount()
{
$this->sortBy = 'id';
$this->sortDirection = 'desc';
$this->perPage = 100;
$this->paginationOptions = config('project.pagination.options');
$this->orderable = (new Transaction())->orderable;
}
public function render()
{
$query = Transaction::advancedFilter([
's' => $this->search ?: null,
'order_column' => $this->sortBy,
'order_direction' => $this->sortDirection,
]);
$transactions = $query->paginate($this->perPage);
return view('livewire.transaction.index', compact('query', 'transactions', 'transactions'));
}
public function deleteSelected()
{
abort_if(Gate::denies('transaction_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');
Transaction::whereIn('id', $this->selected)->delete();
$this->resetSelected();
}
public function delete(Transaction $transaction)
{
abort_if(Gate::denies('transaction_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$transaction->delete();
}
}
For Datatables, we don't use any external packages, as we believe it's not needed in this case - all the pagination/filtering/sorting could happen directly in the component, using the power of some reusable Traits.
For Roles/Permissions, we use the core Laravel Gate functionality, without any external packages. You can read more about it here.