lukeboy_2002's avatar

Sort a table

Earlier I add a post for a score system.

Now I build it with a form in livewire that where a item can be edited inline and save it. For now this is the best what I could find. The problem now is that I can not sort the table on username, points or played games.

my livewire code with thanks to Povilas Korop:

<?php

namespace App\Http\Livewire\Admin\Games;

use App\Models\Player;
use Livewire\Component;

class All extends Component
{
    public $editedPlayerIndex = null;
    public $editedPlayerField = null;
    public $players = [];

    public $sortField;

    public $sortAsc = true;

    protected $queryString = ['sortAsc', 'sortField'];

    protected $validationAttributes = [
        'players.*.points' => 'points',
        'players.*.played_games' => 'played_games',
    ];

    protected $rules = [
        'players.*.points' => ['required', 'numeric'],
        'players.*.played_games' => ['required', 'numeric'],
    ];

    public function sortBy($field)
    {
        if ($this->sortField === $field) {
            $this->sortAsc = !$this->sortAsc;
        } else {
            $this->sortAsc = true;
        }

        $this->sortField = $field;
    }

    public function mount()
    {
        $this->players = Player::all()->toArray();
    }

    public function render()
    {
        return view('livewire.admin.games.all', [
            'players' => $this->players
        ]);
    }

    public function editPlayer($playerIndex)
    {
        $this->editedPlayerIndex = $playerIndex;
    }

    public function editPlayerField($playerIndex, $fieldName)
    {
        $this->editedPlayerField = $playerIndex . '.' . $fieldName;
    }

    public function savePlayer($playerIndex)
    {
        $this->validate();

        $player = $this->players[$playerIndex] ?? NULL;
        if (!is_null($player)) {
            optional(Player::find($player['id']))->update($player);
        }
        $this->editedPlayerIndex = null;
        $this->editedPlayerField = null;
    }
}

my blade file

<div x-data="{}">
    <div class="overflow-x-auto relative">
        <div class="flex justify-end items-center p-2">
            <p class="text-orange-500">Click "Edit", modify that line data and click "Save".</p>
        </div>

        <div class="relative overflow-x-auto shadow-md">
            <table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
                <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
                <tr>
                    <th scope="col" class="px-6 py-3"></th>
                    <th scope="col" class="px-6 py-3">
                        <div class="flex items-center">
                            <button wire:click="sortBy('username')" class="uppercase">Username</button>
                            <x-icons.sort-icon
                                field="username"
                                :sortField="$sortField"
                                :sortAsc="$sortAsc"
                            />
                        </div>
                    </th>
                    <th scope="col" class="px-6 py-3">
                        <div class="flex items-center">
                            <button wire:click="sortBy('points')" class="uppercase">Points</button>
                            <x-icons.sort-icon
                                field="points"
                                :sortField="$sortField"
                                :sortAsc="$sortAsc"
                            />
                        </div>
                    </th>
                    <th scope="col" class="px-6 py-3">
                        <div class="flex items-center">
                            <button wire:click="sortBy('played_games')" class="uppercase">Played Games</button>
                            <x-icons.sort-icon
                                field="played_games"
                                :sortField="$sortField"
                                :sortAsc="$sortAsc"
                            />
                        </div>
                    </th>
                    <th scope="col" class="px-6 py-3 flex justify-end space-x-4">Actions</th>
                </tr>
                </thead>
                <tbody>
                @foreach ($players as $index => $player)
                    <tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
                        <th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
                            <img src="{{ asset('storage/' . $player['image']) }}" alt="{{ $player['username'] }}" class="h-12 w-12 rounded-full" >
                        </th>
                        <th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
                            {{ $player['username'] }}
                        </th>
                        <th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
                            @if ($editedPlayerIndex === $index || $editedPlayerField === $index . '.points')
                                <x-form.input type="text"
                                       @click.away="$wire.editedPlayerField === '{{ $index }}.name' ? $wire.savePlayer({{ $index }}) : null"
                                       wire:model.defer="players.{{ $index }}.points"
                                       class="{{ $errors->has('players.' . $index . '.points') ? 'border-red-500' : 'border-gray-400' }}"
                                />
                                @if ($errors->has('players.' . $index . '.points'))
                                    <div class="text-sm text-red-500">
                                        {{ $errors->first('players.' . $index . '.points') }}
                                    </div>
                                @endif
                            @else
                                <div class="cursor-pointer" wire:click="editPlayerField({{ $index }}, 'name')">
                                    {{ $player['points'] }}
                                </div>
                            @endif
                        </th>
                        <th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
                            @if ($editedPlayerIndex === $index || $editedPlayerField === $index . '.played_games')
                                <x-form.input type="text"
                                       @click.away="$wire.editedPlayerField === '{{ $index }}.played_games' ? $wire.savePlayer({{ $index }}) : null"
                                       wire:model.defer="players.{{ $index }}.played_games"
                                       class="{{ $errors->has('players.' . $index . '.played_games') ? 'border-red-500' : 'border-gray-400' }}"
                                />
                                @if ($errors->has('players.' . $index . '.played_games'))
                                    <div class="text-sm text-red-500">
                                        {{ $errors->first('players.' . $index . '.played_games') }}
                                    </div>
                                @endif
                            @else
                                <div class="cursor-pointer" wire:click="editPlayerField({{ $index }}, 'played_games')">
                                    {{ $player['played_games'] }}
                                </div>
                            @endif
                        </th>
                        <td class="px-6 py-4 flex justify-end space-x-4">
                            @if($editedPlayerIndex === $index || (isset($editedPlayerField) && (int)(explode('.',$editedPlayerField)[0])===$index))
                                <x-button.primary class="px-3 py-2 text-xs font-medium" wire:click.prevent="savePlayer({{$index}})">
                                    Save
                                </x-button.primary>
                            @else
                                <x-button.secondary class="px-3 py-2 text-xs font-medium" wire:click.prevent="editPlayer({{$index}})">
                                    Edit
                                </x-button.secondary>
                            @endif
                        </td>
                    </tr>
                @endforeach
                </tbody>
            </table>
        </div>
    </div>

I don't know how to place the rest of their code like I always do to sort the table. I think the problem is that everything is placed in an array. Normally I made my code like the code below.

    public function render()
    {
        return view('livewire.admin.posts.all', [
            'posts' => Post::orderby('active')
                ->where(function ($query) {
                    $query->where('title', 'like', '%' . $this->search . '%')
                        ->orWhere('body', 'like', '%' . $this->search . '%');
                })
                ->when($this->sortField, function ($query) {
                    $query->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
                })
                ->paginate(10),
        ]);
    }

Who can help me or with solving my problem or another score system.

0 likes
5 replies
LaryAI's avatar
Level 58

To sort the table in your Livewire component, you can modify the render method to include the sorting logic. Here's an example of how you can update the render method in your All component:

public function render()
{
    $players = Player::query()
        ->when($this->sortField, function ($query) {
            $query->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
        })
        ->get()
        ->toArray();

    return view('livewire.admin.games.all', compact('players'));
}

This code retrieves the players from the database and applies the sorting based on the $sortField and $sortAsc properties. The sorted players are then passed to the view.

Make sure to update your blade file to use the players variable instead of $players. Replace the following line:

@foreach ($players as $index => $player)

with:

@foreach ($players as $index => $player)

Now, when you click on the "Username," "Points," or "Played Games" headers, the table will be sorted accordingly.

PovilasKorop's avatar

@lukeboy_2002 so same thing.

Instead of:

$this->players = Player::all()->toArray();

You do something like:

$this->players = Player::when($this->sortField, function ($query) {
                    $query->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
                })->get();
1 like
lukeboy_2002's avatar

@PovilasKorop Thanks. but when I change the value I get a error:

Illuminate\Database\Eloquent\Model::update(): Argument #1 ($attributes) must be of type array, App\Models\Player given, called in /Users/antoine/Sites/TBV-TripleB_New/vendor/laravel/framework/src/Illuminate/Support/Optional.php on line 128

my code:

<?php

namespace App\Http\Livewire\Admin\Games;

use App\Models\Player;
use Livewire\Component;

class All extends Component
{
    public $editedPlayerIndex = null;
    public $editedPlayerField = null;

    public $players = [];

    public $sortField;

    public $sortAsc = true;

    protected $queryString = ['sortAsc', 'sortField'];

    protected $validationAttributes = [
        'players.*.points' => 'points',
        'players.*.played_games' => 'played_games',
        'players.*.won_games' => 'played_games',
    ];

    protected $rules = [
        'players.*.points' => ['required', 'numeric'],
        'players.*.played_games' => ['required', 'numeric'],
        'players.*.won_games' => ['required', 'numeric'],
    ];

    public function sortBy($field)
    {
        if ($this->sortField === $field) {
            $this->sortAsc = !$this->sortAsc;
        } else {
            $this->sortAsc = true;
        }

        $this->sortField = $field;
    }

    public function mount()
    {
        $this->players = Player::all()->toArray();
    }

    public function render()
    {
        return view('livewire.admin.games.all', [
            $this->players = Player::when($this->sortField, function ($query) {
                $query->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
            })->get()
        ]);
    }

    public function editPlayer($playerIndex)
    {
        $this->editedPlayerIndex = $playerIndex;
    }

    public function editPlayerField($playerIndex, $fieldName)
    {
        $this->editedPlayerField = $playerIndex . '.' . $fieldName;
    }

    public function savePlayer($playerIndex)
    {
        $this->validate();

        $player = $this->players[$playerIndex] ?? NULL;
        if (!is_null($player)) {
            optional(Player::find($player['id']))->update($player);
        }
        $this->editedPlayerIndex = null;
        $this->editedPlayerField = null;
    }
}
PovilasKorop's avatar
Level 11

@lukeboy_2002 try adding toArray(), as you did in your original code:

Player::when($this->sortField, function ($query) {
                $query->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
            })->get()->toArray()

Please or to participate in this conversation.