Pagination Laravel Livewire
Laravel - 8.6.0 (Jetstream), Livewire - 2.2.7
Entre autres choses, j'ai deux tableaux simples avec une entrée de recherche rendue avec Livewire. Les deux composants Livewire ont presque la même logique, la principale différence est le format de requête DB. Tout fonctionne parfaitement dans une table (recherche, pagination), mais dans la deuxième table, il y a un problème de pagination.
Qu'est-ce qui ne va pas? Lorsque je clique sur le lien de pagination (par exemple, à partir de la première page gotoPage (2)), la demande est envoyée, l'URL du navigateur change pour la chaîne de requête appropriée, la réponse est reçue, mais il n'y a pas de partie HTML dans la réponse, il n'y a pas de changement dans le DOM et la pagination indique que la première page est active. Si j'actualise manuellement la page, le tableau affiche les données de la page 2, et après cela, je peux même obtenir de nouvelles données pour la page suivante ou précédente, mais une seule fois après l'actualisation initiale de la page. Si je clique à nouveau sur les liens de pagination, pas de données HTML en réponse, pas de changement DOM ...
Qu'ai-je essayé?
- J'ai parcouru la documentation Livewire, essayé toutes les solutions suggérées dans la section "Dépannage".
- J'ai vérifié mon code plusieurs fois, comparé à la fois les composants Livewire et les vues Blade, je n'ai pas été en mesure de trouver une erreur évidente.
- Depuis 10 jours, j'ai cherché la réponse sur SO, forum Livewire, forum Laracast, ... j'ai trouvé de bonnes suggestions, essayé la plupart d'entre elles, mais aucune d'elles ne résout mon problème.
- Éliminé la possibilité que la logique pour la requête DB provoque des problèmes, le problème se termine même si je n'utilise pas de filtrage d'enregistrement complexe (par exemple, si je pagine simplement tous les enregistrements - Model :: all () -> paginate (10)).
- La table problématique a un filtrage d'enregistrement supplémentaire via des boutons radio, je l'ai temporairement supprimé, mais le problème persiste.
Code pour le composant Livewire fonctionnel et la vue 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>
Code pour le composant Livewire NON fonctionnel et la vue 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>
En attendant toute suggestion, je vais essayer une autre installation propre de Laravel / Jetstream / Livewire. Merci d'avance, toute aide est très appréciée!
Réponses
En regardant mon code une fois de plus, j'ai remarqué des "restes" dans les classes de modèles. Dans la classe de modèle, il ne devrait pas y avoir "use WithPagination"; Trait Livewire! Quand j'ai supprimé les statemnets "use" et effacé le cache de vue, tout fonctionne bien!