2
votes

I am following a tutorial to write 2 classes for filtering threads in a forum application.I got this error in line

 $threads = Thread::latest()->filter($filters); // in threadscontroller

Error:

Method Illuminate\Database\Query\Builder::filter does not exist.

ThreadsController with index method:

<?php

namespace App\Http\Controllers;

use App\Thread;
use App\Channel;
use App\Filters\ThreadFilters;

use Illuminate\Http\Request;

class ThreadsController extends Controller
{

    public function __construct(){

        $this->middleware('auth')->only('store','create');

    }
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Channel $channel,ThreadFilters $filters)  
    {

$threads = Thread::latest()->filter($filters);  

if($channel->exist){
    $threads->where('channel_id',$channel->id);
}

$threads = $threads->get();

return view('threads.index',compact('threads'));


     
    }

This is the abstract class Filters:

<?php

namespace App\Filters;
use Illuminate\Http\Request;

abstract class Filters{

protected $request;
protected $builder;

protected $filters = [];

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



    }

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

        foreach($this->getFilters() as $filter=>$value){   //filter by,value yunus mesela.
if(method_exist($this,$filter)){    
    $this->$filter($value);  
}

        }

        return $this->builder;

    }


    public function getFilters(){

        return $this->request->intersect($this->filters); 
    } 

}

Here ThreadFilters.php which extends filters class:

<?php

namespace App\Filters;

use App\User;

use Illuminate\Http\Request;


class ThreadFilters extends Filters
{
protected $filters =['by'];        

protected function by($username){
    $user = User::where('name',$username)->firstorFail();

    return $this->builder->where('user_id',$user->id);          
}

}

If I change latest to all, I get this error:

Type error: Argument 1 passed to Illuminate\Support\Collection::filter() must be callable or null, object given, called in

Also can anyone explain me what is $builder doing in those classes?

2

2 Answers

5
votes

latest() is a modifier shortcut, equivalent to orderBy('created_at', 'desc'). All it does is add the ORDER BY constraint to the query.

filter() is a method on the Collection class. That method does not exist in the query builder, hence the "method not found" error you're receiving.

It does not appear that your filter class should be used with the resulting Collection. Rather, it adds conditionals to your original query. Try implementing it like this:

// Remove the filters() method here.
$threads = Thread::latest();  

if ($channel->exist) {
    $threads->where('channel_id', $channel->id);
}

// Pass your query builder instance to the Filters' apply() method.
$filters->apply($threads);

// Perform the query and fetch results.
$threads = $threads->get();

Also, for future questions, including the tutorial you're attempting/following can provide beneficial context to those helping you. :)

2
votes

If you change latest to all, you're getting a Laravel Collection. So you are calling filter() on a Collection ($threads = Thread::all()->filter($filters);).

If you take a look into the code, you'll see, that the where() method of the array class gets called, which calls PHP's array_filter method. As you can see, a callable must be given. But you are passing an Object to the filter method, $filters, which is an ThreadFilters-Object -> method injection here: public function index(Channel $channel,ThreadFilters $filters) ...

Your error message answers your question in a great way: Type error: Argument 1 passed to Illuminate\Support\Collection::filter() must be callable or null, object given, called in