#Order By issue when query chaining filters

3 messages · Page 1 of 1 (latest)

vapid sierra
#

Hello, I am implementing a Filter class for my Thread model & I am having trouble with working out how to handle the ORDER BY aspect when I chain filters in the query string / request.

This is my ThreadFilters class:

<?php

namespace App\Filters;

use App\Models\User;

class ThreadFilters extends Filters
{

    protected array $filters = ['creator', 'best', 'search', 'filter'];

    public function creator($value)
    {
        $user = User::select('id')->where('username', $value)->get()->first();

        if ($user) {
            return $this->query->where('user_id', $user->id)->orderBy('created_at', 'desc');
        } else {
            return $this->query->where('user_id', 0)->orderBy('created_at', 'desc');
        }
    }

    public function best($value)
    {
        $user = User::select('id')->where('username', $value)->get()->first();

        if ($user) {
            return $this->query->whereHas('replies', function ($q) use ($user) {
                $q->where('user_id', $user->id)
                    ->where('best', 1);
            })->orderBy('created_at', 'desc');
        } else {
            return $this->query->whereHas('replies', function ($q) {
                $q->where('user_id', 0)
                    ->where('best', 1);
            });
        }
    }

    public function search($query, $value)
    {
        return $query->where(function ($q) use ($value) {
            $q->where('title', 'like', '%' . $value . '%')
                ->orWhere('body', 'like', '%' . $value . '%');
        })->orderBy('created_at', 'desc');
    }

    public function filter($value)
    {
        switch ($value) {
            case 'unsolved':
                return $this->query->where('solved', 0)
                    ->orderBy('created_at', 'desc');
            case 'popular':
                // count the replies and order by the count, then order by views
                return $this->query->withCount('replies')
                    ->orderBy('replies_count', 'desc')
                    ->orderBy('views', 'desc');
            case 'unanswered':
                // get the threads that don't have any replies
                return $this->query->whereDoesntHave('replies')
                    ->orderBy('created_at', 'desc');
            case 'solved':
                // get the threads that have been solved
                return $this->query->where('solved', 1)
                    ->orderBy('created_at', 'desc');
            case 'trending':
                // get the threads that have been created in the last week, and order by the replies count
                // and then order by the views, descending order for both of them
                return $this->query->withCount(['replies' => function ($subQuery) {
                    $subQuery->where('created_at', '>=', now()->subWeek());
                }])->orderByDesc('replies_count', 'desc')
                    ->orderByDesc('views', 'desc');
            case 'participated':
                // get the threads that the user has participated in, either by replying to it
                return $this->query->whereHas('replies', function ($q) {
                    $q->where('user_id', auth()->id());
                })
                    // or the user has created the thread (uncomment the line below if you want to include the threads that the user has created)
                    //->orWhere('user_id', auth()->id())
                    ->orderBy('created_at', 'desc');
            case 'following':
                // get the threads that the user is following
                return $this->query->whereHas('follows', function ($q) {
                    $q->where('user_id', auth()->id());
                })->orderBy('created_at', 'desc');
        }
    }

}
#

This the the abstract Filters class that it extends:

<?php

namespace App\Filters;

abstract class Filters
{

    protected object $request, $query;
    protected array $filters = [];

    public function __construct($request)
    {
        $this->request = $request;
    }

    public function apply($query)
    {
        $this->query = $query;

        foreach ($this->getFilters() as $filter => $value) {
            if (method_exists($this, $filter)) {
                $this->$filter($value);
            }
        }

        if (empty($this->getFilters())) {
            $this->query->orderBy('created_at', 'desc');
        }

        return $this->query;
    }

    public function getFilters()
    {
        return $this->request->only($this->filters);
    }

}
#

The problem I am having is when I do a filter like ?filter=trending&creator=myusername It is applying the ORDER BY 'created_at' that is on the creator filter, before the the ORDER BY values that are applied by the trending filter.

I want it to apply the trending filter first & then the creator filter (In the order of the query string).

How do I go about handling that?