Show your model code for all of this and someone can help.
I am thinking a Has may through would work:
https://laravel.com/docs/12.x/eloquent-relationships#has-many-through
Situation: I have three tables: Product (inventory products), Flags (indicator flags that show 'something needs attention'), and Rules (rules for creating Flags). The relationship chain goes product->flags->rules. Each product can have many flags, while each flag belongs to one rule (but one rule can have many flags).
We're transitioning to a new way of defining and handling flags. Under the old way, the flag record itself contains a flag message, and the first few characters of the message are the severity. Under the new way, the severity is shown by a boolean field in the rule definition: false means low severity, true means high severity.
I need a way to retrieve all flags for a given product where severity = 'high', whether the flag was defined under the old way or the new way. That is, I want a query in a Product class method that says "retrieve all flags for this product where either flag->message like 'HIGH%' or flag->rule->severe = TRUE."
I've tried the straightforward approach:
$flagList = $this->flags()
->where('message', 'like', 'HIGH%')
->orWhere('rule->severe',TRUE)
->get();
but got an error because table Flags doesn't contain a column called 'rule'. I also tried using an orWhereHas query:
$flagList = $this->flags()
->where('message', 'like', 'HIGH%')
->orWhereHas('rule',function($q) {
$q->where('severe',TRUE);
})
->get();
but that didn't work either -- I got all high-severity flags on all products, not just the current one. How do I do this query?
Thanks.
Your orWhereHas() attempt is very close. The issue is operator precedence.
When you do this on a relationship query:
$this->flags()
->where('message', 'like', 'HIGH%')
->orWhereHas('rule', function ($q) {
$q->where('severe', true);
})
it effectively becomes:
(product_id = current_product AND message LIKE 'HIGH%')
OR
(exists rule where severe = true)
So the OR escapes the product constraint and starts matching flags from other products too. Laravel’s docs explicitly warn that chaining orWhere-style clauses after relationship constraints requires logical grouping.
Use a grouped where(...) closure so both severity checks stay inside the current product’s flag relationship:
$flagList = $this->flags()
->where(function ($query) {
$query->where('message', 'like', 'HIGH%')
->orWhereHas('rule', function ($q) {
$q->where('severe', true);
});
})
->get();
That keeps the query shaped like:
product_id = current_product
AND
(
message LIKE 'HIGH%'
OR EXISTS (
SELECT * FROM rules
WHERE rules.id = flags.rule_id
AND severe = true
)
)
So this will return only this product’s flags, where either:
the old-format flag says high severity in message, or
the related rule has severe = true
Also, this part was never going to work:
->orWhere('rule->severe', TRUE)
because rule is a relationship, not a column on the flags table. To filter by related model data in Eloquent, you need whereHas / orWhereHas rather than dotting into a relationship as if it were a JSON column.
https://laravel.com/docs/12.x/eloquent-relationships https://laravel.com/docs/12.x/queries https://laravel.com/docs/12.x/collections
Please or to participate in this conversation.