I'm not sure wether to post this in Laravel or Livewire, but I thought this was the best place. So I started with Livewire and created a simple datatable. Live search, pagination and sortable all works. In my table I show several data. But whenever I filter (either by search, pagination or sortable) the component changes the value of the status of all the posts to "Published". When I hit refresh everything works fine. Can anyone think of something what it could be? It is only the status that gets changed. All the other values remains untouched.
The status come from an Enum. But even when I remove the enum or cast from the model, the behaviour remains the same.
PostIndex.php (Livewire)
<?php
namespace App\Livewire\Admin;
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Post;
class PostIndex extends Component
{
use WithPagination;
public int $perPage = 10;
public string $search = '';
public string $sortField = 'title';
public string $sortDirection = 'asc';
protected array $queryString = ['sortField', 'sortDirection'];
public function sortBy($fieldName) {
$this->sortDirection = $this->sortField === $fieldName
? $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'
: $this->sortDirection = 'asc';
$this->sortField = $fieldName;
}
public function render()
{
return view('livewire.admin.post-index', [
'tableContents' => Post::where('title', 'like', '%' . $this->search . '%')
->orderBy($this->sortField, $this->sortDirection)
->paginate($this->perPage),
]);
}
}
Post-Index (View)
<div>
<div class="row">
<div class="col-4">
<x-form.input
wire:model.live="search"
placeholder="{{ __('Search Results ..') }}"/>
</div>
<div class="col-8 flex action-group justify-content-end">
<x-form.group>
<x-form.select wire:model.live="perPage">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</x-form.select>
</x-form.group>
<x-button.add :permission="'post'" :route="route('post.create')">{{ __('Article') }}</x-button.add>
</div>
</div>
<div class="row">
<div class="align-middle min-w-full overflow-x-auto overflow-hidden sm:rounded-lg">
<x-table>
<x-slot name="header">
<x-table.heading sortable wire:click="sortBy('title')" :direction="$sortField === 'title' ? $sortDirection : null" class="w-full">{{ __('Title') }}</x-table.heading>
<x-table.heading>{{ __('Category') }}</x-table.heading>
<x-table.heading sortable wire:click="sortBy('status')" :direction="$sortField === 'status' ? $sortDirection : null">{{ __('Publicatie') }}</x-table.heading>
<x-table.heading>{{ __('Status') }}</x-table.heading>
<x-table.heading>{{__('Actions')}}</x-table.heading>
</x-slot>
<x-slot name="body">
@forelse($tableContents as $tableContent)
<x-table.row>
<x-table.cell>{{ \Str::words($tableContent->title, 5, ' ..') }}</x-table.cell>
<x-table.cell>{{ $tableContent->category->name }}</x-table.cell>
<x-table.cell>
@if($tableContent->status === 'published')
<span class="badge bg-success d-block">{{ __(ucfirst($tableContent->status)) }}</span>
@elseif($tableContent->status === 'unpublished')
<span class="badge bg-danger d-block">{{ __(ucfirst($tableContent->status)) }}</span>
@elseif($tableContent->status === 'archived')
<span class="badge bg-secondary d-block">{{ __(ucfirst($tableContent->status)) }}</span>
@else
<span class="badge bg-info d-block">{{ __(ucfirst($tableContent->status)) }}</span>
@endif
</x-table.cell>
<x-table.cell>
@if($tableContent->status === 'published')
@if($tableContent->published === null)
<span class="badge bg-success d-block">{{ __('Online') }}</span>
@elseif($tableContent->unpublished_at > now())
<span class="badge bg-success d-block">{{ __('Online') }}</span>
@else
@endif
@else
<span class="badge bg-danger d-block">{{ __('Offline') }}</span>
@endif
</x-table.cell>
<x-table.cell class="flex index-button">
<x-button.edit :permission="'post'" :id="$tableContent->id">{{ __('Edit') }}</x-button.edit>
<x-button.delete :permission="'post'" :id="$tableContent->id">{{ __('Delete') }}</x-button.delete>
</x-table.cell>
</x-table.row>
@empty
<x-table.row>
<x-table.cell colspan="5">
<div class="flex justify-center items-center">
<span>{{ __('No Results Found') }}</span>
</div>
</x-table.cell>
</x-table.row>
@endforelse
</x-slot>
</x-table>
<div class="mt-2">
{{ $tableContents->links() }}
</div>
</div>
</div>
</div>
Post.php (Model)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Enums\PublishedState;
use Spatie\MediaLibrary\MediaCollections\File;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\Permission\Traits\HasRoles;
use App\Scopes\PublishedScope;
class Post extends Model implements HasMedia
{
use HasFactory;
use HasRoles;
use InteractsWithMedia;
protected static function booted()
{
return static::addGlobalScope(new PublishedScope);
}
protected $fillable = [
'user_id',
'category_id',
'slug',
'title',
'caption',
'content',
'meta_title',
'meta_description',
'meta_tags',
'og_title',
'og_description',
'og_slug',
'og_type',
'og_locale',
'status',
'published_at',
'unpublished_at',
];
protected $casts = [
'user_id' => 'integer',
'category_id' => 'integer',
'state' => PublishedState::class,
'published_at' => 'datetime',
'unpublished_at' => 'datetime',
];
protected $dates = [
'published_at',
'unpublished_at',
'created_at',
'updated_at',
];
public function user()
{
return $this->belongsTo(\App\Models\User::class);
}
public function category()
{
return $this->belongsTo(\App\Models\Category::class);
}
}
PublishedState.php
<?php
namespace App\Enums;
enum PublishedState: string
{
case Archived = 'archived';
case Draft = 'draft';
case Published = 'published';
case Unpublished = 'unpublished';
}