Laravel Livewire Pagination
Laravel - 8.6.0 (Jetstream), Livewire - 2.2.7
Trong số những thứ khác, tôi có hai bảng đơn giản với đầu vào tìm kiếm được hiển thị bằng Livewire. Cả hai thành phần Livewire đều có logic gần như giống nhau, điểm khác biệt chính là định dạng truy vấn DB. Mọi thứ hoạt động hoàn hảo trong một bảng (tìm kiếm, phân trang), nhưng trong bảng thứ hai, có vấn đề với phân trang.
Chuyện gì vậy? Khi tôi nhấp vào liên kết phân trang (ví dụ: từ trang đầu tiên gotoPage (2)), yêu cầu được gửi, url trong trình duyệt thay đổi thành chuỗi truy vấn thích hợp, phản hồi được nhận, nhưng không có phần HTML trong phản hồi, không có thay đổi trong DOM và phân trang cho thấy trang đầu tiên đang hoạt động. Nếu tôi làm mới trang theo cách thủ công, bảng sẽ hiển thị dữ liệu cho trang 2 và sau đó, tôi thậm chí có thể nhận dữ liệu mới cho trang tiếp theo hoặc trang trước đó, nhưng chỉ một lần sau khi làm mới trang đầu tiên. Nếu tôi nhấp lại vào các liên kết phân trang, không có dữ liệu HTML phản hồi, không có thay đổi DOM ...
Tôi đã thử những gì?
- Tôi đã xem qua tài liệu Livewire, đã thử tất cả các giải pháp được đề xuất từ phần "khắc phục sự cố".
- Tôi đã kiểm tra mã của mình nhiều lần, so sánh cả các thành phần Livewire và chế độ xem Blade, không thể tìm thấy một số sai lầm rõ ràng.
- Trong 10 ngày qua, tôi đã tìm kiếm câu trả lời trên SO, diễn đàn Livewire, diễn đàn Laracast, ... tìm thấy một số gợi ý hay, đã thử hầu hết chúng, nhưng không có cái nào giải quyết được vấn đề của tôi.
- Đã loại bỏ khả năng logic cho truy vấn DB gây ra một số vấn đề, sự cố thoát ra ngay cả khi tôi không sử dụng tính năng lọc bản ghi phức tạp (ví dụ: nếu tôi chỉ phân trang tất cả các bản ghi - Model :: all () -> paginate (10)).
- Bảng sự cố có một số tính năng lọc bản ghi bổ sung thông qua các nút radio, tôi đã tạm thời loại bỏ điều đó, nhưng sự cố vẫn còn.
Mã cho thành phần Livewire đang hoạt động và chế độ xem Blade:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Extension extends Model
{
use HasFactory;
protected $guarded = []; public function user() { return $this->belongsTo('App\Models\User', 'directory', 'ext');
}
public static function search($query) { return empty($query) ? static::query()
: static::where('directory', 'like', '%' . $query . '%') ->orWhere('mobile', 'like', '%' . $query . '%')
->orWhere('department', 'ilike', '%' . $query . '%') ->orWhere('name1', 'ilike', '%' . $query . '%')
->orWhere('name2', 'ilike', '%' . $query . '%');
}
}
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use Livewire\WithPagination;
class PhonebookTable extends Component
{
use WithPagination;
public $perPage = 10;
public $search = ''; public function render() { return view('phonebook.phonebook-table', [ 'phonebook' => \App\Models\Extension::search($this->search)
->with('user')
->orderBy('directory', 'asc')
->paginate(10)
]);
}
}
/**
* Livewire Lifecycle Hook
*/
public function updatingSearch(): void
{
$this->resetPage();
}
}
<div>
<div class="flex items-center justify-between">
{{-- Search Input --}}
<div class="relative text-gray-700 focus-within:text-gray-600 m-2">
<span class="absolute inset-y-0 left-0 flex items-center pl-2">
<button type="submit" class="p-1 focus:outline-none focus:shadow-outline">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" viewBox="0 0 24 24" class="w-6 h-6">
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
</span>
<input wire:model="search" type="search" name="q"
class="py-2 text-sm text-gray
bg-gray-100 rounded-md pl-10 focus:outline-none
focus:bg-gray-200 focus:text-gray-700"
placeholder="{{ __('Search...') }}"
autocomplete="off">
</div>
{{-- End Of Search Input --}}
{{-- PerPage Select Dropdown --}}
<div>
<span>{{ __('Per page:') }}</span>
<div class="inline-block relative mr-2">
<select wire:model="perPage" class="block appearance-none w-full
bg-white border border-gray-400 hover:border-gray-500 px-4 py-2
pr-8 rounded shadow leading-tight focus:outline-none
focus:shadow-outline">
<option>10</option>
<option>25</option>
<option>50</option>
</select>
<div class="pointer-events-none absolute
inset-y-0 right-0 flex items-center px-2 text-gray-700">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757
6.586 4.343 8z" />
</svg>
</div>
</div>
</div>
{{-- End Of PerPage Select Dropdown --}}
</div>
{{-- Phonebook Table --}}
<div class="container p-2">
<table class="text-center w-full">
<thead class="bg-gray-400 flex text-white w-full item-center rounded-md">
<tr class="flex w-full py-2">
<th class="px-4 w-1/4">{{ __('Name1') }}</th>
<th class="px-4 w-1/4">{{ __('Name2') }}</th>
<th class="px-4 w-1/4">{{ __('Department') }}</th>
<th class="px-4 w-1/4">{{ __('Extension') }}</th>
<th class="px-4 w-1/4">{{ __('Mobile') }}</th>
</tr>
</thead>
<tbody class="">
@foreach ($phonebook as $directory) <tr class="flex w-full py-2 hover:bg-gray-100"> <td class="px-4 w-1/4">{{ $directory->name1??$directory->name }} </td> <td class="px-4 w-1/4">{{ $directory->name2 }}</td>
<td class="px-4 w-1/4">{{ $directory->user->department??'' }}</td> <td class="px-4 w-1/4">{{ $directory->directory }}</td>
<td class="px-4 w-1/4">{{ $directory->user->mobile??'' }}</td> </tr> @endforeach </tbody> </table> </div> {{-- End Of Phonebook Table --}} {{-- Pagination and Records Count info --}} <div class="p-2 bg-gray-200"> {{ $phonebook->links('vendor.pagination.livewire-tailwind') }}
</div>
{{-- End Of Pagination and Records Count info --}}
</div>
Mã cho thành phần Livewire KHÔNG hoạt động và chế độ xem Blade:
<?php
namespace App\Models;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Livewire\WithPagination;
class Phonecall extends Model
{
use HasFactory;
use WithPagination;
protected $table = "md_phonecall"; // custom method for "live" searching from livewire component public static function search($query)
{
return empty($query) ? static::query() : static::where('dialednumber', 'like', '%' . $query . '%')
->orWhere('startdate', 'like', $query) ->orWhere('starttime', 'like', $query)
->orWhere('chargednumber', 'like', '%' . $query . '%') ->orWhere('conditioncode', 'ilike', '%' . $query . '%');
}
// 'conditioncode' accessor for casting integer value from DB to String shown in table View
public function getConditioncodeAttribute($value) { switch ($value) {
case 7;
return 'Incoming call (' . $value . ')'; case 8; return 'Internal call (' . $value . ')';
case 10;
return 'Outgoing call (' . $value . ')'; case 15; return 'Transfer call (' . $value . ')';
case 19;
return 'Outgoing call (' . $value . ')'; case 23; return 'Missed call (' . $value . ')';
case 25;
return 'Internal call (' . $value . ')'; case 28; return 'Outgoing call (' . $value . ')';
case 29;
return 'Outgoing call (' . $value . ')'; default; return 'Unknown type (' . $value . ')';
}
}
// Local Scope for filtering only calls belonging to logged User
public function scopeMycalls($query) { return $query->where('chargednumber', '=', Auth::user()->ext)->orWhere('dialednumber', '=', Auth::user()->ext);
}
/**
* Scope a query to only include phonecalls of a given type.
*
* @param \Illuminate\Database\Eloquent\Builder $query * @param mixed $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfType($query, $type)
{
if ($type === 'all') return $query->where('conditioncode', 'like', '%');
if ($type === 'incoming') return $query->where('conditioncode', '=', 7);
if ($type === 'outgoing') return $query->where('conditioncode', '=', 10)->orWhere('conditioncode', '=', 19)->orWhere('conditioncode', '=', 28);
if ($type === 'missed') return $query->where('conditioncode', '=', 23);
if ($type === 'internal') return $query->where('conditioncode', '=', 8)->orWhere('conditioncode', '=', 15)->orWhere('conditioncode', '=', 25);
}
public function scopePeriod($query, $from, $to) { return $query->whereBetween('startdate', [$from, $to]);
}
public function scopeOfExtension($query, $extension)
{
return $query->where('dialednumber', 'like', '%' . $extension . '%')
->orWhere('chargednumber', 'like', '%' . $extension . "%");
}
}
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use Livewire\WithPagination;
class MyPhonecallsTable extends Component
{
use WithPagination;
public $perPage = 10;
public $search = ''; public $calltype = 'all';
public function render()
{
return view('mycalls.my-phonecalls-table', [
'mycalls' => \App\Models\Phonecall::search(
$this->search )->mycalls() ->ofType($this->calltype)
->orderBy('startdate', 'desc')->orderBy('starttime', 'desc')->paginate($this->perPage), ]); } /** * Livewire Lifecycle Hook */ public function updatingSearchInput(): void { $this->resetPage();
}
}
<div>
<div class="flex items-center justify-between">
{{-- Search Input --}}
<div class="relative text-gray-700 focus-within:text-gray-600 m-2">
<span class="absolute inset-y-0 left-0 flex items-center pl-2">
<button type="submit" class="p-1 focus:outline-none focus:shadow-outline">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" viewBox="0 0 24 24" class="w-6 h-6">
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
</span>
<input wire:model="search" type="search" name="q"
class="py-2 text-sm text-gray bg-gray-100 rounded-md pl-10 focus:outline-none focus:bg-gray-200 focus:text-gray-700"
placeholder="{{ __('Search...') }}" autocomplete="off">
</div>
{{-- End Of Search Input --}}
{{-- Call type selector --}}
@include('partials.calltypes')
{{-- End of Call type selector --}}
{{-- Paginator Select Dropdown --}}
<div>
<span>{{ __('Per page:') }}</span>
<div class="inline-block relative mr-2">
<select wire:model="perPage"
class="block appearance-none w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline">
<option>10</option>
<option>25</option>
<option>50</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" /></svg>
</div>
</div>
</div>
{{-- End Of Paginator Select Dropdown --}}
</div>
{{-- Phonecalls Table --}}
<div class="container p-2" id="my-calls">
<table class="text-center w-full" id="my-calls-table">
<thead class="bg-gray-400 flex text-white w-full item-center rounded-md">
<tr class="flex w-full py-2">
<th class="px-4 w-1/5">{{ __('Date') }}</th>
<th class="px-4 w-1/5">{{ __('Time') }}</th>
<th class="px-4 w-1/5">{{ __('Duration') }}</th>
<th class="px-4 w-1/5">{{ __('Calling No') }}</th>
<th class="px-4 w-1/5">{{ __('Called No') }}</th>
{{-- <th class="px-4 w-1/6">{{ __('Call Type') }}</th> --}}
</tr>
</thead>
<!-- Remove the nasty inline CSS fixed height on production and replace it with a CSS class — this is just for demonstration purposes! -->
<tbody class="">
@foreach ($mycalls as $mycall)
<tr class="flex w-full py-2 hover:bg-gray-100">
<td class="px-4 w-1/5">
{{ \Carbon\Carbon::createFromFormat('Y-m-d',$mycall->startdate)->format('d.m.Y.')}} </td> <td class="px-4 w-1/5"> {{ \Carbon\Carbon::createFromFormat('H:i:s', $mycall->starttime, 'UTC')->tz('Europe/Belgrade')->toTImeString() }}
</td>
<td class="px-4 w-1/5">
{{ gmdate('H:i:s', $mycall->duration) }}</td> <td class="px-4 w-1/5">{{ $mycall->chargednumber}}</td>
<td class="px-4 w-1/5">{{ $mycall->dialednumber}}</td> {{-- <td class="px-4 w-1/6">{{ $phonecall->conditioncode}}</td> --}}
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- End Of Phonecalls Table --}}
{{-- Pagination and Records Count info --}}
<div class="p-2 bg-gray-200">
{{ $mycalls->links('vendor.pagination.livewire-tailwind') }}
</div>
{{-- End Of Pagination and Records Count info --}}
</div>
Trong khi chờ đợi bất kỳ đề xuất nào, tôi sẽ thử một lần cài đặt Laravel / Jetstream / Livewire sạch hơn. Cảm ơn trước, bất kỳ sự giúp đỡ nào cũng được đánh giá cao!
Trả lời
Bằng cách xem lại mã của mình một lần nữa, tôi đã nhận thấy một số "phần thừa" trong các lớp mô hình. Trong lớp mô hình không nên có "use WithPagination;" Đặc điểm Livewire! Khi tôi loại bỏ trạng thái "sử dụng" và xóa bộ nhớ cache của chế độ xem, mọi thứ đều hoạt động tốt!