/** * Add a condition over a related model. * * Usage: * ------ * * There is different calling form for this function. * * * Check existence of related models. This is a merely a WHERE EXISTS * clause in SQL: * * `$query->root('Blog')->has('articles');` * * * Perform a condition over the *number* of related models: * * `$query->root('Blog')->where('articles', '>', 100);` * * This would retrieve blogs instance which contains at least one hundred * articles. * * * Use a Closure to add conditions to the subquery: * * ```php * $query->root('Blog')->has('articles', function ($q) { * $q->where('online', true); * }); * * @param string $relation The name of the relation to check on. * @param string $op Optional. The operator for the second use form. * @param mixed $value Optional. The value for the second use form. * * @return $this */ public function whereHas($relation, $op = null, $value = null, $not = false) { $mainQuery = $this->getQueryOrNewSelect(); $mainQueryRootAlias = $mainQuery->getBuilder()->getRootAlias(); // We'll use $rels for recursivity. $rels = explode('/', $relation); $relation = array_shift($rels); $root = $this->getRoot(); $rel = $root::relation($relation); // We construct the sub-query. It can be the sub-query of an Exists // clause or a Count (if $op and $value are given). $subRootAlias = $this->getRootAlias(); $subRootAlias = $subRootAlias ? $subRootAlias . '.' . $rel->name : $rel->name; $subQuery = new self($rel->lm->class, $subRootAlias); $subQuery->setConnection($this->getConnection()); $subQuery->newSelect(); $subQuery->whereRelation($rel, $mainQueryRootAlias); $subQuery->getQuery()->getCompiler()->useTableAlias = true; $mainQuery->getQuery()->getCompiler()->useTableAlias = true; if ($rels) { $relation = implode('/', $rels); $subQuery->whereHas($relation); } // We want a Count sub-query. if ($op !== null && $value !== null) { $subQuery->getQuery()->addAggregate('COUNT'); $mainQuery->selectSub($subQuery->getQuery(), '#' . $relation); $mainQuery->where(DB::expr('#' . $relation), $op, $value, null, $not); } else { if ($op instanceof \Closure) { $op($subQuery); } $subQuery->get(array('*'), false); $mainQuery->whereExists($subQuery->getQuery(), null, $not); } return $this; }