skip to Main Content

I need to show posts by tags. My solution works for single tag and it looks as follows:

Route:

Route::get('/posts',
   [PostController::class, 'index'])->middleware('auth');

Post model filter:

public function scopeFilter($query, array $filters)
{
    if ($filters['tag'] ?? false) {
        $tagId = Tag::where('name', $filters['tag'])->first()->id;
        $query->whereHas('tags', function($q) use ($tagId) {
                $q->where('tag_id', $tagId);
        });
    }
}

Method index from PostController:

public function index()
{
    return view('posts', [
        'posts' => Post::latest()->filter(request(['tag']))->get()
    ]);
}

This code works for url as follows: "http://127.0.0.1:8000/posts/?tag=test". But I need to find a way to be able search posts that have more tags, for example I would like find posts with "test" and "unit" tags. For this I would like to use url as follows: "http://127.0.0.1:8000/posts/?tag=test&unit". I stuck, because I was thinking that "request([‘tag’])" will return "test&unit" but it only return "test". Is it even possible to get in some way also ‘unit’ tag from this request?

2

Answers


  1. What you can do is seperate it by comma so your url will by ?tag=test,unit

    the code will look like this (untested)

    I will explain the code later today

    public function scopeFilter($query, array $filters)
    {
        if ($filters['tag'] ?? false) {
            $tags = explode(',', $filters['tag']);
            $tagIds = Tag::whereIn('name', $tags)->pluck('id')->toArray();
            
            foreach ($tagIds as $tagId) {
                $query->whereHas('tags', function($q) use ($tagId) {
                    $q->where('tag_id', $tagId);
                });
            }
        }
    }
    
    
    Login or Signup to reply.
  2. A GET request using a Query String can accept multiple parameters. Instead of ?tag=test&unit (which wouldn’t really work anyway, since &unit would be parsed as $request->input('unit'), and would be null, & is a reserved character), you’d send it as:

    http://127.0.0.1:8000/posts?tags[]=test&tags[]=unit
    

    On the backend, when you access request()->input('tags'), you’d get the following array:

    $tags = request()->input('tags'); // ['test', 'unit']
    

    So, putting it all together:

    // ScopeFilter method on your `Post` Model
    public function scopeFilter($query, array $tagNames) {
      if (!empty($tagNames)) {
        $tagIds = Tag::whereIn('name', $tagNames)->pluck('id');
    
        return $query->whereHas('tags', function($subQuery) use ($tagIds) {
          return $subQuery->whereIn('tags.id', $tagIds);
        });
      }
    
      return $query;
    }
    
    // Usage
    public function index() {
      return view('posts', [
        'posts' => Post::latest()->filter(request()->input('tags', []))->get()
      ]);
    }
    
    • Adjust the queries to use whereIn() to handle multiple values
    • Use request()->input('tags', []) to access ?tags[]=...&tags[]=..., or an empty Array if not supplied.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search