Search code examples
phparrayslaravellaravel-livewire

How to order an array by bool values in laravel livewire


I have an array, I want to order that array by "is_available" key, (true first) I'm using laravel 8 and a livewire component

  0 => array:12 [▼
    "id" => 1
    "name" => "치킨 ~The Chicken~"
    "is_available" => true
    "nearest_customer_distance" => 4905.4423678942
    "customers" => array:26 [▶]
  ]
  1 => array:12 [▼
    "id" => 2
    "name" => "混ぜるな危険"
    "is_available" => false
    "customers" => array:10 [▶]
  ]
  2 => array:12 [▼
    "id" => 3
    "name" => "Oh! Bánh mì"
    "is_available" => true
    "customers" => array:8 [▶]
  ]
  3 => array:12 [▼
    "id" => 5
    "name" => "CHIJIMI DEVIL"
    "is_available" => false
    "customers" => array:44 [▶]
  ]
]

I'm trying this

$newFranchiseList = $this->getFranchiseListActualPage();
$finalFranchiseList = array_merge($this->franchiseList, $newFranchiseList);
$finalFranchiseList = collect($finalFranchiseList)->sortBy('is_available')->reverse();
$this->franchiseList = $finalFranchiseList->toArray();

but I get the order by ID in my view, this is my view

@forelse($franchiseList as $franchise)
    @if($franchise['is_available'])
            {{$franchise['name']}}
                IS AVAILABLE
    @else
            {{$franchise['name']}}
                NOT AVAILABLE
    @endif
@empty

@endforelse

if I do a dump($this->franchiseList) the list is shown with the order of is_available!

note: $this->franchiseList is never used in the component, the only line to be used is the last one, if I don't use this line, the list is empty

component data collection process

first, in javascript call a livewire listener

window.livewire.emit('franchises:selectedCoords', JSON.stringify(selectedCoords));

then that is received by the component

public $selectedLatitude,
    $selectedLongitude,
    $perPage = 20,
    $franchiseList = [],
    $page = 1,
    $totalFranchises = 0,
    $isLoaded = false;
    protected $listeners = [
    'franchises:selectedCoords' => 'getCoords'
    ];

public function render()
{
    return view('livewire.franchise-list');
}

public function getCoords($selectedCoords)
{
    if ($selectedCoords) {
        if (!empty($this->franchiseList)) {
            $this->clearList();
        }
        $this->selectedLatitude = json_decode($selectedCoords, true)['lat'];
        $this->selectedLongitude = json_decode($selectedCoords, true)['lng'];
    }
    $this->getFranchiseList();
}

public function getFranchiseList()
{
    if ($this->selectedLatitude && $this->selectedLongitude) {
        if (!$this->allFranchisesAreLoaded())
        {
            $newFranchiseList = $this->getFranchiseListActualPage();
            $finalFranchiseList = array_merge($this->franchiseList, $newFranchiseList);
            $this->franchiseList = collect($finalFranchiseList)->sortBy('is_available')->reverse()->toArray();
        }
        $this->isLoaded = true;
    }
}

remember that $this->franchiseList is never overwritten, that is, it is only used once, in the line $this->franchiseList = collect($finalFranchiseList)->sortBy('is_available')->reverse()->toArray (); and if it is not done, it ends up being an empty array, so there is no other $this->franchiseList with the order shown by the blade view


Solution

  • Livewire will on each request serialize the public properties, so that it can be sent to the frontend and stored in JavaScript.

    Now here's the kicker: JavaScript will do its own internal sorting on objects, which you can't prevent. So to work around that, you have to re-key your array after you sort it, so that the keys are now in ascending order of what you sorted it to.

    Basically, all you need to do before you call ->toArray(), is to call ->values() on it, thereby grabbing the values in the order they were, and PHP will assign it new keys starting from 0.

    $this->franchiseList = collect($finalFranchiseList)->sortBy('is_available')->reverse()->values()->toArray();
    

    For example, a object like this in JavaScript,

    {
        1: 'foo',
        0: 'bar',
    }
    

    Will be internally sorted by JavaScript like this

    {
        0: 'bar',
        1: 'foo',
    }
    

    And you can't change that. That's why you need to re-key it.