mikecou's avatar

Searching with dynamic parameters and pagination: example needed

I can’t find a good example on how to implement a search for my site.

I want to have a series of search boxes on the side of my index page and, when a user selects options and hits the search button, the page is refreshed with the new changes. I currently have pagination as well and I would like to keep that if possible.

Think of the search Newegg has on the side when using their site. Maybe not as complex though. Only two or three option sets like size, color, etc. I don’t need vue or anything complex, just laravel standard if I can.

I found some examples but they all seem to be incomplete in some way or another.

Can anyone point me to a good example on how to set this up? I would love a Laracast about it if there is one, but I haven’t been through enough of the videos to find it yet.

Thank you

0 likes
4 replies
Tray2's avatar

One way you can do it is to build the query dynamicaly in your index method.

public function index(Request $request)
{
    if(request->has('filter_author_name') {
        $books = Books::whereAuthor($request->filter_author_name)->paginate(25);
    } else {
        $books = Books::paginate(25);
    }

    return view('books.index')->with(['books' => $books]);
}

Or you can use redis. https://laracasts.com/series/learn-laravel-and-redis-through-examples

Snapey's avatar

See this page

https://speakernet.co.uk/talks

and the filters that are in the top right of the page

In the controller, I use Scopes to filter the records, so the main part of the method looks like;


public function talks()
    {
        $filters = $this->filters;

        // get topics, filtered by adding local scopes through the Topic class 
        $talks = Topic::with('speaker.region','tagged','category')    
                        ->regions($filters['region'])
                        ->category($filters['category'])
                        ->fee($filters['fee'])
                        ->recency($filters['recency'])
                        ->tagged($filters['tagged'])
                        ->published()
                        ->notice($filters['notice'])
                        ->orderBy('subject')
                        ->paginate(15);

and in the model, the talks are filtered by scopes such as;

    public function scopeCategory($query,$category)
    {
        if($category!=""){
            return $query->where('category_id',$category);
        }
        return $query;
    }

    public function scopeFee($query,$fee)
    {
        if(!Empty($fee)){
            return $query->where('fee_id',$fee);
        }
        return $query;
    }


To work from page to page, the filters are persisted in the session, so when the index page is loaded, its applying all the filters it already has in session for this user

So there are a bunch of functions which are related to saving state in session



    public function initialiseFilters()
    {
        //ensure that the filters are setup in session and hydrated into controller
        $this->filters = session('filters');
        if(!isset($this->filters)) {
            $this->resetFilters();
        }
    }

    private function resetFilters()
    {
        // create empty filters
        $this->filters = [
            'category' => '',
            'fee' => '',
            'region' => '',
            'recency' => '',
            'notice' => '',
            'tagged' => '',
            'filtered' => false]
        ;

        session(['filters' => $this->filters]);
    }

    private function saveFilters()
    {
        // save updated filters to the session
        session(['filters' => $this->filters]);
    }

    private function unsetFilter($filter)
    {
        $this->initialiseFilters();
        $this->filters[$filter] = '';
        $this->setFilteredFlag();
        return $this->saveFilters();
    }

    private function setFilteredFlag()
    {
        $f = $this->filters;

        if (!
            empty($f['region'])
            || !empty($f['category'])
            || !empty($f['fee'])
            || !empty($f['recency'])
            || !empty($f['notice'])
            || !empty($f['tagged']))
         {
            return $this->filters['filtered']= true;
        }
        return $this->filters['filtered']= false;
    }



When you change one of the dropdowns on the page, it updates the session with the new value, then loads the index where the database is queried using the new value that is in session.

    public function category(Request $request, $category)
    {
        $this->initialiseFilters();
        $this->filters = array_merge($this->filters,['category' => $category, 'filtered' => true]);

        $this->saveFilters();

        return $this->talks();

    }
mikecou's avatar

Thank you for your replies. I will try these methods of searching today and will hopefully get it working.

mikecou's avatar

Thanks for the help. I got it working. I think it may be a little bit of a hybrid approach. If there are any flaws in it, please let me know. Still getting used to the syntax and tooling.

Here is what I did.

On my Model I added several scopes to help with what I am doing. Each of them is for a specific column on my table like tech_type_id. But I wanted to use the $request information to see if I needed to add an element to them for the actual filtering.

public function scopeTechType($query, $request)
    {
        if($request->has('tech_type_id') && $request->tech_type_id != '')
        {
            $query->where('tech_type_id', '=', $request->tech_type_id);

        }
    }

    public function scopeTechName($query, $request)
    {
        if($request->has('tech_search') && $request->tech_search != '')
        {
            $query->orwhere('tech_name', 'like', '%' . $request->tech_search . '%');

        }
    }
    public function scopeTechOneLineDescription($query, $request)
    {
        if($request->has('tech_search') && $request->tech_search != '')
        {
            $query->orwhere('one_line_description', 'like', '%' . $request->tech_search . '%');

        }
    }
    public function scopeTechDetailedDescription($query, $request)
    {
        if($request->has('tech_search') && $request->tech_search != '')
        {
            $query->orwhere('detailed_description', 'like', '%' . $request->tech_search . '%');

        }
    }

in my TechController i added these scope elements in as part of the selecting of the data

$techs = Tech::with('tech_company')
            ->with('tech_type')
            ->with('tech_status')
            ->TechName($request)
            ->TechOneLineDescription($request)
            ->TechDetailedDescription($request)
            ->TechType($request)
            ->PublishedTechStatus()
            ->paginate(10, ['*'], 'techs');

On my index page, I put in a form so I can have a user select the information they want for filtering.

<form method="GET" action="/tech">
                @csrf
                <div class="field">
                    <div class = "control">
                        <input type="text"  class="input" name="tech_search" placeholder="Name" value="{{ $request->tech_search != '' ? $request->tech_search : '' }}" >
                    </div>
                </div>
                <div class="field">
                    <div class = "select">
                        <select name="tech_type_id">
                            <option value="">-- Technology Type --</option>
                            @foreach($TechTypes as $TechType)
                                <option value="{{ $TechType->id  }}" {{ $request->tech_type_id == $TechType->id ? 'selected' : '' }}>{{ $TechType->name  }}</option>
                            @endforeach
                        </select>
                    </div>
                </div>
                <button type="submit" class="button is-link">Filter</button> <a href="/tech">Remove Filter</a>
            </form>

All of this accomplished what I think I wanted. On my index page, I now have a form a user can fill out and do a search and it will search the technologies and bring back the results in a paginated format.

Thank you for the help.

Please or to participate in this conversation.