entityType = $entityType; $this->user = $user; $this->dateTimeItemTransformer = $dateTimeItemTransformer; $this->scanner = $scanner; $this->itemConverterFactory = $itemConverterFactory; $this->randomStringGenerator = $randomStringGenerator; $this->entityManager = $entityManager; $this->ormDefs = $ormDefs; $this->config = $config; $this->metadata = $metadata; } public function convert(QueryBuilder $queryBuilder, Item $item): WhereClauseItem { $type = $item->getType(); $value = $item->getValue(); $attribute = $item->getAttribute(); $data = $item->getData(); if ($data instanceof Item\Data\DateTime) { return $this->convert( $queryBuilder, $this->dateTimeItemTransformer->transform($item) ); } if (!$type) { throw new Error("Bad where item. No 'type'."); } if ( $attribute && $this->itemConverterFactory->has($this->entityType, $attribute, $type) ) { $converter = $this->itemConverterFactory->create( $this->entityType, $attribute, $type, $this->user ); return $converter->convert($queryBuilder, $item); } switch ($type) { case Type::OR: case Type::AND: return WhereClause::fromRaw( $this->groupProcessAndOr($queryBuilder, $type, $attribute, $value) ); case Type::NOT: case Type::SUBQUERY_NOT_IN: case Type::SUBQUERY_IN: return WhereClause::fromRaw( $this->groupProcessSubQuery($queryBuilder, $type, $attribute, $value) ); } if (!$attribute) { throw new Error("Bad where item. No 'attribute'."); } switch ($type) { case 'columnLike': case 'columnIn': case 'columnNotIn': case 'columnIsNotNull': case 'columnEquals': case 'columnNotEquals': return WhereClause::fromRaw( $this->groupProcessColumn($queryBuilder, $type, $attribute, $value) ); case Type::ARRAY_ANY_OF: case Type::ARRAY_NONE_OF: case Type::ARRAY_IS_EMPTY: case Type::ARRAY_IS_EMPTY: case Type::ARRAY_ALL_OF: return WhereClause::fromRaw( $this->groupProcessArray($queryBuilder, $type, $attribute, $value) ); } $methodName = 'process' . ucfirst($type); if (method_exists($this, $methodName)) { return WhereClause::fromRaw( $this->$methodName($queryBuilder, $attribute, $value) ); } if (!$this->itemConverterFactory->hasForType($type)) { throw new Error("Unknown where item type '{$type}'."); } $converter = $this->itemConverterFactory->createForType($type, $this->entityType, $this->user); return $converter->convert($queryBuilder, $item); } /** * @param mixed $value * @return array * @throws Error */ protected function groupProcessAndOr( QueryBuilder $queryBuilder, string $type, ?string $attribute, $value ): array { if (!is_array($value)) { throw new Error("Bad where item."); } $whereClause = []; foreach ($value as $item) { $subPart = $this->convert($queryBuilder, Item::fromRaw($item))->getRaw(); foreach ($subPart as $left => $right) { if (!empty($right) || is_null($right) || $right === '' || $right === 0 || $right === false) { $whereClause[] = [ $left => $right, ]; } } } return [ strtoupper($type) => $whereClause, ]; } /** * @param mixed $value * @return array * @throws Error */ protected function groupProcessSubQuery( QueryBuilder $queryBuilder, string $type, ?string $attribute, $value ): array { if (!is_array($value)) { throw new Error("Bad where item."); } $sqQueryBuilder = $this->entityManager ->getQueryBuilder() ->select() ->from($this->entityType); $whereItem = Item::fromRaw([ 'type' => Type::AND, 'value' => $value, ]); $whereClause = $this->convert($sqQueryBuilder, $whereItem)->getRaw(); $this->scanner->applyLeftJoins($sqQueryBuilder, $whereItem); $rawParams = $sqQueryBuilder->build()->getRaw(); $key = $type === 'subQueryIn' ? 'id=s' : 'id!=s'; return [ $key => [ 'select' => ['id'], 'from' => $this->entityType, 'whereClause' => $whereClause, 'leftJoins' => $rawParams['leftJoins'] ?? [], 'joins' => $rawParams['joins'] ?? [], ], ]; } /** * @param mixed $value * @return array * @throws Error */ protected function groupProcessColumn( QueryBuilder $queryBuilder, string $type, string $attribute, $value ): array { $link = $this->metadata->get(['entityDefs', $this->entityType, 'fields', $attribute, 'link']); $column = $this->metadata->get(['entityDefs', $this->entityType, 'fields', $attribute, 'column']); if (!$column || !$link) { throw new Error("Bad where item 'column'."); } $alias = $link . 'ColumnFilter' . $this->randomStringGenerator->generate(); $queryBuilder->distinct(); $queryBuilder->leftJoin($link, $alias); $columnKey = $alias . 'Middle.' . $column; if ($type === 'columnLike') { return [ $columnKey . '*' => $value, ]; } if ($type === 'columnIn') { return [ $columnKey . '=' => $value, ]; } if ($type === 'columnEquals') { return [ $columnKey . '=' => $value, ]; } if ($type === 'columnNotEquals') { return [ $columnKey . '!=' => $value, ]; } if ($type === 'columnNotIn') { return [ $columnKey . '!=' => $value, ]; } if ($type === 'columnIsNull') { return [ $columnKey . '=' => null, ]; } if ($type === 'columnIsNotNull') { return [ $columnKey . '!=' => null, ]; } throw new Error("Bad where item 'column'."); } /** * @param mixed $value * @return array * @throws Error */ protected function groupProcessArray( QueryBuilder $queryBuilder, string $type, string $attribute, $value ): array { $arrayValueAlias = 'arrayFilter' . $this->randomStringGenerator->generate(); $arrayAttribute = $attribute; $arrayEntityType = $this->entityType; $idPart = 'id'; $isForeign = strpos($attribute, '.') !== false; $isForeignType = false; $entityDefs = $this->ormDefs->getEntity($this->entityType); if (!$isForeign) { $isForeignType = $entityDefs->getAttribute($attribute)->getType() === Entity::FOREIGN; $isForeign = $isForeignType; } if ($isForeign) { if ($isForeignType) { $arrayAttributeLink = $entityDefs->getAttribute($attribute)->getParam('relation'); $arrayAttribute = $entityDefs->getAttribute($attribute)->getParam('foreign'); } else { list($arrayAttributeLink, $arrayAttribute) = explode('.', $attribute); } if (!$arrayAttributeLink || !$arrayAttribute) { throw new Error("Bad where item."); } $arrayEntityType = $entityDefs->getRelation($arrayAttributeLink)->getForeignEntityType(); $arrayLinkAlias = $arrayAttributeLink . 'ArrayFilter' . $this->randomStringGenerator->generate(); $idPart = $arrayLinkAlias . '.id'; $queryBuilder->leftJoin($arrayAttributeLink, $arrayLinkAlias); $relationType = $entityDefs->getRelation($arrayAttributeLink)->getType(); if ($relationType === Entity::MANY_MANY || $relationType === Entity::HAS_MANY) { $queryBuilder->distinct(); } } if ($type === Type::ARRAY_ANY_OF) { if (is_null($value) || !$value && !is_array($value)) { throw new Error("Bad where item. No value."); } $queryBuilder->leftJoin( 'ArrayValue', $arrayValueAlias, [ $arrayValueAlias . '.entityId:' => $idPart, $arrayValueAlias . '.entityType' => $arrayEntityType, $arrayValueAlias . '.attribute' => $arrayAttribute, ] ); $queryBuilder->distinct(); return [ $arrayValueAlias . '.value' => $value, ]; } if ($type === Type::ARRAY_NONE_OF) { if (is_null($value) || !$value && !is_array($value)) { throw new Error("Bad where item 'array'. No value."); } $queryBuilder->leftJoin( 'ArrayValue', $arrayValueAlias, [ $arrayValueAlias . '.entityId:' => $idPart, $arrayValueAlias . '.entityType' => $arrayEntityType, $arrayValueAlias . '.attribute' => $arrayAttribute, $arrayValueAlias . '.value=' => $value, ] ); $queryBuilder->distinct(); return [ $arrayValueAlias . '.id' => null, ]; } if ($type === Type::ARRAY_IS_EMPTY) { $queryBuilder->distinct(); $queryBuilder->leftJoin( 'ArrayValue', $arrayValueAlias, [ $arrayValueAlias . '.entityId:' => $idPart, $arrayValueAlias . '.entityType' => $arrayEntityType, $arrayValueAlias . '.attribute' => $arrayAttribute, ] ); return [ $arrayValueAlias . '.id' => null, ]; } if ($type === Type::ARRAY_IS_NOT_EMPTY) { $queryBuilder->distinct(); $queryBuilder->leftJoin( 'ArrayValue', $arrayValueAlias, [ $arrayValueAlias . '.entityId:' => $idPart, $arrayValueAlias . '.entityType' => $arrayEntityType, $arrayValueAlias . '.attribute' => $arrayAttribute, ] ); return [ $arrayValueAlias . '.id!=' => null, ]; } if ($type === Type::ARRAY_ALL_OF) { if (is_null($value) || !$value && !is_array($value)) { throw new Error("Bad where item 'array'. No value."); } if (!is_array($value)) { $value = [$value]; } $whereList = []; foreach ($value as $arrayValue) { $whereList[] = [ $idPart .'=s' => QueryBuilder::create() ->from('ArrayValue') ->select('entityId') ->where([ 'value' => $arrayValue, 'attribute' => $arrayAttribute, 'entityType' => $arrayEntityType, 'deleted' => false, ]) ->build() ->getRaw() ]; } return $whereList; } throw new Error("Bad where item 'array'."); } /** * A complex expression w/o a value. * * @param mixed $value * @return array */ protected function processExpression(QueryBuilder $queryBuilder, string $attribute, $value): array { $key = $attribute; if (substr($key, -1) !== ':') { $key .= ':'; } return [ $key => null, ]; } /** * @param mixed $value * @return array */ protected function processLike(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '*' => $value, ]; } /** * @param mixed $value * @return array */ protected function processNotLike(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '!*' => $value, ]; } /** * @param mixed $value * @return array */ protected function processEquals(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '=' => $value, ]; } /** * @param mixed $value * @return array */ protected function processOn(QueryBuilder $queryBuilder, string $attribute, $value): array { return $this->processEquals($queryBuilder, $attribute, $value); } /** * @param mixed $value * @return array */ protected function processNotEquals(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '!=' => $value, ]; } /** * @param mixed $value * @return array */ protected function processNotOn(QueryBuilder $queryBuilder, string $attribute, $value): array { return $this->processNotEquals($queryBuilder, $attribute, $value); } /** * @param mixed $value * @return array */ protected function processStartsWith(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '*' => $value . '%', ]; } /** * @param mixed $value * @return array */ protected function processEndsWith(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '*' => '%' . $value, ]; } /** * @param mixed $value * @return array */ protected function processContains(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '*' => '%' . $value . '%', ]; } /** * @param mixed $value * @return array */ protected function processNotContains(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '!*' => '%' . $value . '%', ]; } /** * @param mixed $value * @return array */ protected function processGreaterThan(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '>' => $value, ]; } /** * @param mixed $value * @return array */ protected function processAfter(QueryBuilder $queryBuilder, string $attribute, $value): array { return $this->processGreaterThan($queryBuilder, $attribute, $value); } /** * @param mixed $value * @return array */ protected function processLessThan(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '<' => $value, ]; } /** * @param mixed $value * @return array */ protected function processBefore(QueryBuilder $queryBuilder, string $attribute, $value): array { return $this->processLessThan($queryBuilder, $attribute, $value); } /** * @param mixed $value * @return array */ protected function processGreaterThanOrEquals(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '>=' => $value, ]; } /** * @param mixed $value * @return array */ protected function processLessThanOrEquals(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '<=' => $value, ]; } /** * @param mixed $value * @return array * @throws Error */ protected function processIn(QueryBuilder $queryBuilder, string $attribute, $value): array { if (!is_array($value)) { throw new Error("Bad where item 'in'."); } return [ $attribute . '=' => $value, ]; } /** * @param mixed $value * @return array * @throws Error */ protected function processNotIn(QueryBuilder $queryBuilder, string $attribute, $value): array { if (!is_array($value)) { throw new Error("Bad where item 'notIn'."); } return [ $attribute . '!=' => $value, ]; } /** * @param mixed $value * @return array * @throws Error */ protected function processBetween(QueryBuilder $queryBuilder, string $attribute, $value): array { if (!is_array($value) || count($value) < 2) { throw new Error("Bad where item 'between'."); } return [ 'AND' => [ $attribute . '>=' => $value[0], $attribute . '<=' => $value[1], ] ]; } /** * @param mixed $value * @return array */ protected function processAny(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ 'true:' => null, ]; } /** * @param mixed $value * @return array */ protected function processNone(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ 'false:' => null, ]; } /** * @param mixed $value * @return array */ protected function processIsNull(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '=' => null, ]; } /** * @param mixed $value * @return array */ protected function processIsNotNull(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '!=' => null, ]; } /** * @param mixed $value * @return array */ protected function processEver(QueryBuilder $queryBuilder, string $attribute, $value): array { return $this->processIsNotNull($queryBuilder, $attribute, $value); } /** * @param mixed $value * @return array */ protected function processIsTrue(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '=' => true, ]; } /** * @param mixed $value * @return array */ protected function processIsFalse(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '=' => false, ]; } /** * @param mixed $value * @return array */ protected function processToday(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '=' => date('Y-m-d'), ]; } /** * @param mixed $value * @return array */ protected function processPast(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '<' => date('Y-m-d'), ]; } /** * @param mixed $value * @return array */ protected function processFuture(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ $attribute . '>' => date('Y-m-d'), ]; } /** * @param mixed $value * @return array */ protected function processLastSevenDays(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt1 = new DateTime(); $dt2 = clone $dt1; $dt2->modify('-7 days'); return [ 'AND' => [ $attribute . '>=' => $dt2->format('Y-m-d'), $attribute . '<=' => $dt1->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processLastXDays(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt1 = new DateTime(); $dt2 = clone $dt1; $number = strval(intval($value)); $dt2->modify('-'.$number.' days'); return [ 'AND' => [ $attribute . '>=' => $dt2->format('Y-m-d'), $attribute . '<=' => $dt1->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processNextXDays(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt1 = new DateTime(); $dt2 = clone $dt1; $number = strval(intval($value)); $dt2->modify('+' . $number . ' days'); return [ 'AND' => [ $attribute . '>=' => $dt1->format('Y-m-d'), $attribute . '<=' => $dt2->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processOlderThanXDays(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); $number = strval(intval($value)); $dt->modify('-' . $number . ' days'); return [ $attribute . '<' => $dt->format('Y-m-d'), ]; } /** * @param mixed $value * @return array */ protected function processAfterXDays(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); $number = strval(intval($value)); $dt->modify('+' . $number . ' days'); return [ $attribute . '>' => $dt->format('Y-m-d'), ]; } /** * @param mixed $value * @return array */ protected function processCurrentMonth(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); return [ 'AND' => [ $attribute . '>=' => $dt->modify('first day of this month')->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processLastMonth(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); return [ 'AND' => [ $attribute . '>=' => $dt->modify('first day of last month')->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processNextMonth(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); return [ 'AND' => [ $attribute . '>=' => $dt->modify('first day of next month')->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array * @throws \Exception */ protected function processCurrentQuarter(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); $quarter = ceil($dt->format('m') / 3); $dt->modify('first day of January this year'); return [ 'AND' => [ $attribute . '>=' => $dt->add(new DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P3M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array * @throws \Exception */ protected function processLastQuarter(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); $quarter = ceil($dt->format('m') / 3); $dt->modify('first day of January this year'); $quarter--; if ($quarter == 0) { $quarter = 4; $dt->modify('-1 year'); } return [ 'AND' => [ $attribute . '>=' => $dt->add(new DateInterval('P' . (($quarter - 1) * 3) . 'M'))->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P3M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processCurrentYear(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); return [ 'AND' => [ $attribute . '>=' => $dt->modify('first day of January this year')->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1Y'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processLastYear(QueryBuilder $queryBuilder, string $attribute, $value): array { $dt = new DateTime(); return [ 'AND' => [ $attribute . '>=' => $dt->modify('first day of January last year')->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1Y'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processCurrentFiscalYear(QueryBuilder $queryBuilder, string $attribute, $value): array { $dtToday = new DateTime(); $dt = new DateTime(); $fiscalYearShift = $this->config->get('fiscalYearShift', 0); $dt->modify('first day of January this year')->modify('+' . $fiscalYearShift . ' months'); if (intval($dtToday->format('m')) < $fiscalYearShift + 1) { $dt->modify('-1 year'); } return [ 'AND' => [ $attribute . '>=' => $dt->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1Y'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processLastFiscalYear(QueryBuilder $queryBuilder, string $attribute, $value): array { $dtToday = new DateTime(); $dt = new DateTime(); $fiscalYearShift = $this->config->get('fiscalYearShift', 0); $dt->modify('first day of January this year')->modify('+' . $fiscalYearShift . ' months'); if (intval($dtToday->format('m')) < $fiscalYearShift + 1) { $dt->modify('-1 year'); } $dt->modify('-1 year'); return [ 'AND' => [ $attribute . '>=' => $dt->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P1Y'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array * @throws \Exception */ protected function processCurrentFiscalQuarter(QueryBuilder $queryBuilder, string $attribute, $value): array { $dtToday = new DateTime(); $dt = new DateTime(); $fiscalYearShift = $this->config->get('fiscalYearShift', 0); $dt->modify('first day of January this year')->modify('+' . $fiscalYearShift . ' months'); $month = intval($dtToday->format('m')); $quarterShift = floor(($month - $fiscalYearShift - 1) / 3); if ($quarterShift) { if ($quarterShift >= 0) { $dt->add(new DateInterval('P' . ($quarterShift * 3) . 'M')); } else { $quarterShift *= -1; $dt->sub(new DateInterval('P' . ($quarterShift * 3) . 'M')); } } return [ 'AND' => [ $attribute . '>=' => $dt->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P3M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array * @throws \Exception */ protected function processLastFiscalQuarter(QueryBuilder $queryBuilder, string $attribute, $value): array { $dtToday = new DateTime(); $dt = new DateTime(); $fiscalYearShift = $this->config->get('fiscalYearShift', 0); $dt->modify('first day of January this year')->modify('+' . $fiscalYearShift . ' months'); $month = intval($dtToday->format('m')); $quarterShift = floor(($month - $fiscalYearShift - 1) / 3); if ($quarterShift) { if ($quarterShift >= 0) { $dt->add(new DateInterval('P' . ($quarterShift * 3) . 'M')); } else { $quarterShift *= -1; $dt->sub(new DateInterval('P' . ($quarterShift * 3) . 'M')); } } $dt->modify('-3 months'); return [ 'AND' => [ $attribute . '>=' => $dt->format('Y-m-d'), $attribute . '<' => $dt->add(new DateInterval('P3M'))->format('Y-m-d'), ] ]; } /** * @param mixed $value * @return array */ protected function processIsNotLinked(QueryBuilder $queryBuilder, string $attribute, $value): array { return [ 'id!=s' => [ 'select' => ['id'], 'from' => $this->entityType, 'joins' => [$attribute], ] ]; } /** * @param mixed $value * @return array */ protected function processIsLinked(QueryBuilder $queryBuilder, string $attribute, $value): array { $link = $attribute; $alias = $link . 'IsLinkedFilter' . $this->randomStringGenerator->generate(); $queryBuilder->distinct(); $queryBuilder->leftJoin($link, $alias); return [ $alias . '.id!=' => null, ]; } /** * @param mixed $value * @return array * @throws Error */ protected function processLinkedWith(QueryBuilder $queryBuilder, string $attribute, $value): array { $link = $attribute; if (!$this->ormDefs->getEntity($this->entityType)->hasRelation($link)) { throw new Error("Not existing link '{$link}' in where item."); } $defs = $this->ormDefs->getEntity($this->entityType)->getRelation($link); $alias = $link . 'LinkedWithFilter' . $this->randomStringGenerator->generate(); if (is_null($value) || !$value && !is_array($value)) { throw new Error("Bad where item. Empty value."); } $relationType = $defs->getType(); $queryBuilder->distinct(); if ($relationType == Entity::MANY_MANY) { $queryBuilder->leftJoin($link, $alias); $key = $defs->getForeignMidKey(); if (!$key) { throw new Error("Bad link '{$link}' in where item."); } return [ $alias . 'Middle.' . $key => $value, ]; } else if ($relationType == Entity::HAS_MANY) { $queryBuilder->leftJoin($link, $alias); return [ $alias . '.id' => $value, ]; } else if ($relationType == Entity::BELONGS_TO) { $key = $defs->getKey(); if (!$key) { throw new Error("Bad link '{$link}' in where item."); } return [ $key => $value, ]; } else if ($relationType == Entity::HAS_ONE) { $queryBuilder->leftJoin($link, $alias); return [ $alias . '.id' => $value, ]; } throw new Error("Bad where item. Not supported relation type."); } /** * @param mixed $value * @return array * @throws Error */ protected function processNotLinkedWith(QueryBuilder $queryBuilder, string $attribute, $value): array { $link = $attribute; if (!$this->ormDefs->getEntity($this->entityType)->hasRelation($link)) { throw new Error("Not existing link '{$link}' in where item."); } $defs = $this->ormDefs->getEntity($this->entityType)->getRelation($link); $alias = $link . 'NotLinkedWithFilter' . $this->randomStringGenerator->generate(); if (is_null($value)) { throw new Error("Bad where item. Empty value."); } $relationType = $defs->getType(); $queryBuilder->distinct(); if ($relationType == Entity::MANY_MANY) { $key = $defs->getForeignMidKey(); if (!$key) { throw new Error("Bad link '{$link}' in where item."); } $queryBuilder->leftJoin( $link, $alias, [$key => $value] ); return [ $alias . 'Middle.' . $key => null, ]; } else if ($relationType == Entity::HAS_MANY) { $queryBuilder->leftJoin( $link, $alias, ['id' => $value] ); return [ $alias . '.id' => null, ]; } else if ($relationType == Entity::BELONGS_TO) { $key = $defs->getKey(); if (!$key) { throw new Error("Bad link '{$link}' in where item."); } return [ $key . '!=' => $value, ]; } else if ($relationType == Entity::HAS_ONE) { $queryBuilder->leftJoin($link, $alias); return [ $alias . '.id!=' => $value, ]; } throw new Error("Bad where item. Not supported relation type."); } /** * @param mixed $value * @return array * @throws Error */ protected function processLinkedWithAll(QueryBuilder $queryBuilder, string $attribute, $value): array { $link = $attribute; if (!$this->ormDefs->getEntity($this->entityType)->hasRelation($link)) { throw new Error("Not existing link '{$link}' in where item."); } if (is_null($value) || !$value && !is_array($value)) { throw new Error("Bad where item. Empty value."); } if (!is_array($value)) { $value = [$value]; } $defs = $this->ormDefs->getEntity($this->entityType)->getRelation($link); $relationType = $defs->getType(); if ($relationType === Entity::MANY_MANY) { $key = $defs->getForeignMidKey(); $whereList = []; foreach ($value as $targetId) { $sq = QueryBuilder::create() ->from($this->entityType) ->select('id') ->leftJoin($link) ->where([ $link . 'Middle.' . $key => $targetId, ]) ->build(); $whereList[] = ['id=s' => $sq->getRaw()]; } return $whereList; } if ($relationType === Entity::HAS_MANY) { $whereList = []; foreach ($value as $targetId) { $sq = QueryBuilder::create() ->from($this->entityType) ->select('id') ->leftJoin($link) ->where([ $link . '.id' => $targetId, ]) ->build(); $whereList[] = ['id=s' => $sq->getRaw()]; } return $whereList; } throw new Error("Bad where item. Not supported relation type."); } }