Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ilaurens's avatar

whereHas where multiple (must contain)

I'm in a bit of a situation with whereHas, the problem is I got a array with keys, I need to use those selected key to find results that contains all (ids that is contained within the array). The problem is it works with one, but when having more than one it does not work.

It seems when using where('id', 3) it will select that one, but when you use another where('id', 5) it does not work (it probably uses the same record for that)

This is the code:

        $keys = array_keys($request->genres);

        $list = Movie::with('genres')->whereHas('genres', function ($query) use($keys) {
                $query->where('id', 3)->where('id', 5); // testing it manually, but it must be done with the value in "$keys"
        })->limit(3)->get();

It also has a pivot table, for relationship based on movie_id. If there is any other way, I'm hoping to hear that too. My preference is the best, clean and without much overhead.

Thank you in advance :)

0 likes
14 replies
jekinney's avatar

Just use has() instead of with(). Has() excepts your constraints too.

InaniELHoussain's avatar
   $keys = array_keys($request->genres);

    $list = Movie::whereHas('genres', function ($query) use($keys) {
            $query->whereIn('id', $keys); // use whereIn
    })->limit(3)->get();
ilaurens's avatar

@InaniELHoussain that will select any movie that contains any of the keys, since genres is selected (it must contain all keys).

@jekinney The (with, limit) is just for debugging, other than getting the movies, it's not needed at al. I've tried the has function, but... To no avail, perhaps I'm not thinking correctly regarding it :P?

ilaurens's avatar

I got a form with genres that you can select with checkboxes, it will give a array with genre id's on submit to the controller.

Those id's must be used to select movies (all of the id's must exist within the results of the movie -> genres).

In other words I select Adventure(id:1) and Action(id:2), it will select all movies that contains both genres (1 & 2)

InaniELHoussain's avatar

@ilaurens thats what I wrote for you, the Movies that its genre's id belongs to the $keys array, have you tried it?

ilaurens's avatar

Yes, I tried it already, whereIn was also my first guess :P

The Keys as reference: array(2) { [0]=> int(6) [1]=> int(80) }

The first result will return: 6 | 21 | 76 | 10 | 15 | 13

You can see the '6' that is correct, but there is no '80'. Both must exist for it to be selected. I think whereIn detect whether id is inside the array, which is not good for my purpose (because all of it must be AND WHERE).

Thank you for your time btw, :)

InaniELHoussain's avatar

@ilaurens now I got it now, what about this

$keys = array_keys($request->genres);

$list = Movie::whereHas('genres', function ($query) use($keys) {
foreach($keys as $key){
$query= $query>where('id', $key); 
}
        
})->limit(3)->get();
1 like
ilaurens's avatar

@InaniELHoussain Thank you, but that does not seem to work either ;(

Tried something like that already, but did not work before either, did test it though, just to be sure :)

I think where selects a single of many results, and when id is 2, it cannot also be 5 for example. ;(

got a feeling it's something easy, but perhaps I'm looking something over the head...

ilaurens's avatar
ilaurens
OP
Best Answer
Level 1

Oke, I found something, it seems like this does the trick, but I think there must be a better way to do this.

        $keys = array_keys($request->genres);

        $list = Movie::with('genres');

        foreach($keys as $k => $v) {
            $list->whereHas('genres', function ($query) use($v) {
                $query->where('id', $v);
            });
        }

dd($list->limit(3)->get()->toArray());

It kinda feel like this will give a lot of overhead, is this the best way? Any other & better ways will be appreciated :) Solution mark will go to the best.

6 likes
Hesesses's avatar

@ilaurens did you ever get a better way to solve this? Having exactly the same problem

staudenmeir's avatar

@hesesses You can also use whereHas()'s third and fourth parameter in combination with whereIn():

$keys = array_keys($request->genres);

$list = Movie::with('genres');

$list->whereHas('genres', function ($query) use ($keys) {
    $query->whereIn('id', $keys);
}, '=', count($keys));

dd($list->limit(3)->get()->toArray());
5 likes
Hesesses's avatar

Thank you for suggesting but this doesnt help. Using whereIn is OR operator and this needs to be done with AND

Ben Taylor's avatar

I know this is an old post, but came across it while seeking a solution to a similar problem. Here is my solution.


->when(
	$request->filled('tags'), 
	fn($q) => array_reduce(
		$request->tags, 
		fn($carry, $tag_id) => $carry->whereHas('tags', fn($qu) => $qu->where('id', $tag_id)), 
		$q
	)
)

Please or to participate in this conversation.