/** * @param $str * @return From * @throws SyntaxErrorException */ public static function parse($str) : From { $from = new From(); $tableStrings = StringUtils::trimStringArray(explode(',', $str)); $tables = []; foreach ($tableStrings as $tableStr) { $match = []; $wasFound = preg_match(self::$regFromTable, $tableStr, $match); if ($wasFound) { $tables[] = new Table($match['prefix'], $match['name'], array_key_exists('alias', $match) ? $match['alias'] : null); } else { throw new SyntaxErrorException("Invalid column \"{$tableStr}\""); } } $from->tables = $tables; return $from; }
/** * @Get("/{tableName}/", name="trinity_table_search") * * @QueryParam(name="q", nullable=false, strict=true, description="DB Query", allowBlank=true) * * @param ParamFetcher $paramFetcher * @param string $tableName * * @return JsonResponse * @throws \Doctrine\ORM\ORMException * * @throws \Trinity\Bundle\SearchBundle\Exception\SyntaxErrorException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @throws \InvalidArgumentException * @throws \Exception * * @View */ public function tableAction(ParamFetcher $paramFetcher, $tableName) { $queryParams = $paramFetcher->get('q'); /** @var Search $search */ $search = $this->get('trinity.search'); if ($tableName === 'global') { if (StringUtils::isEmpty($queryParams)) { throw new \InvalidArgumentException('Query is empty'); } return new Response($search->convertArrayToJson($search->queryGlobal($queryParams)), 200, ['Content-Type' => 'application/json']); } else { try { /** @var NQLQuery $nqlQuery */ $nqlQuery = $search->queryTable($tableName, $queryParams); return new Response($search->convertToJson($nqlQuery, count($nqlQuery->getSelect()->getColumns())), 200, ['Content-Type' => 'application/json']); } catch (SyntaxErrorException $e) { $result = $search->queryEntity($tableName, null, null, $queryParams)->getQueryBuilder()->getQuery()->getResult(); return new Response($search->convertArrayToJson($result)); } } }
/** * Parse condition; * * @param $str * @return array * @throws SyntaxErrorException */ private static function parseCondition($str) : array { $parts = []; // REMOVE TRAILING SPACES $str = trim($str); // LOOP THROUGH ALL CHARACTERS $iMax = strlen($str); /** @noinspection ForeachInvariantsInspection */ for ($i = 0; $i < $iMax; $i++) { // IF CHARACTER IS LEFT BRACKET, FIND PAIR BRACKET AND RECURSIVELY FIND CONDITIONS WITHIN THESE BRACKETS if ($str[$i] === '(') { $pairBracketIndex = self::findPairBracketIndex($str, $i + 1); if ($pairBracketIndex > 0) { $part = new WherePart(); $part->type = WherePartType::SUBCONDITION; $subCondition = substr($str, $i + 1, $pairBracketIndex - $i - 1); $part->baseExpr = trim($subCondition); $part->subTree = self::parseCondition($subCondition); $parts[] = $part; $i = $pairBracketIndex; continue; } else { throw new SyntaxErrorException('Missing pair bracket'); } } else { if ($str[$i] === ' ') { if (trim(substr($str, $i, 4)) === Operator::AND) { $part = new WherePart(); $part->type = WherePartType::OPERATOR; $part->value = Operator::AND; $parts[] = $part; $i += 3; } else { if (trim(substr($str, $i, 3)) === Operator::OR) { $part = new WherePart(); $part->type = WherePartType::OPERATOR; $part->value = Operator::OR; $parts[] = $part; $i += 2; } } } else { $match = []; $wasFound = preg_match(self::$regKeyOpValue, substr($str, $i), $match); if ($wasFound) { $part = new WherePart(); $part->type = WherePartType::CONDITION; $part->key = Column::parse($match['key']); $value = $match['value']; if (StringUtils::startsWith($value, '"') && StringUtils::endsWith($value, '"')) { $value = StringUtils::substring($value, 1, StringUtils::length($value) - 1); } $part->value = $value; $part->operator = $match['operator']; $parts[] = $part; $i = $i + strlen($match[0]) - 1; } else { $part = new WherePart(); $part->type = 'UNKNOWN'; $part->value = $str[$i]; $parts[] = $part; $context = self::getErrorContext($str, $i); throw new SyntaxErrorException("Unrecognized char sequence at '{$context['subString']}' starting from index {$context['errorAt']}"); } } } } return $parts; }
/** * @param WherePart[] $conditions * @param string $columnDefaultAlias * @param int $paramCounter * @return array */ private function getParametrizedWhere($conditions, $columnDefaultAlias = '', &$paramCounter = 0) : array { $whereClause = ''; $whereParams = []; foreach ($conditions as $cond) { switch ($cond->type) { case WherePartType::OPERATOR: $whereClause .= ' ' . $cond->value; break; case WherePartType::CONDITION: $whereClause .= ' ' . ($cond->key->getWrappingFunction() === null ? '' : $cond->key->getWrappingFunction() . '(') . (!count($cond->key->getJoinWith()) ? $cond->key->getAlias() ?? $columnDefaultAlias : $cond->key->getJoinWith()[count($cond->key->getJoinWith()) - 1]) . '.' . $cond->key->getName() . ' ' . ($cond->key->getWrappingFunction() === null ? '' : ')') . ($cond->operator === '!=' ? '<>' : $cond->operator) . ' ?' . $paramCounter; if ($cond->operator === Operator::LIKE && !StringUtils::startsWith($cond->value, '%') && !StringUtils::endsWith($cond->value, '%')) { $whereParams[] = '%' . $cond->value . '%'; } else { $whereParams[] = $cond->value; } $paramCounter++; break; case WherePartType::SUBCONDITION: $parametrizedSubWhere = $this->getParametrizedWhere($cond->subTree, $columnDefaultAlias, $paramCounter); $subWhereClause = $parametrizedSubWhere['clause']; $subWhereParams = $parametrizedSubWhere['params']; $whereClause .= ' (' . $subWhereClause . ')'; $whereParams = array_merge($whereParams, $subWhereParams); break; } } return ['clause' => $whereClause, 'params' => $whereParams]; }
/** * Alias = null - parsed alias is used, otherwise parsed alias is used as join field * @param $str * @param null $alias * @return Column * @throws SyntaxErrorException */ public static function parse($str, $alias = null) { $match = []; $column = trim($str); $wasFound = preg_match(self::$regFuncColumn, $column, $match); if ($wasFound) { $name = $match['column']; $alias = null === $alias ? $match['alias'] : $alias; $function = $match['function']; /** @noinspection NestedTernaryOperatorInspection */ $joinWith = null === $alias ? $match['alias'] : StringUtils::isEmpty($match['joinWith']) ? [] : explode(':', $match['joinWith']); return new Column($name, $alias, $function, $joinWith); } else { throw new SyntaxErrorException("Invalid column name '{$column}'"); } }
/** * @param string $tableName * @param string $queryParams * @return Response * @throws \InvalidArgumentException */ protected function table($tableName, $queryParams = '') { /** @var Search $search */ $search = $this->get('trinity.search'); if ($tableName === 'global') { if (StringUtils::isEmpty($queryParams)) { throw new \InvalidArgumentException('Query is empty'); } return $search->convertArrayToJson($search->queryGlobal($queryParams)); } else { $nqlQuery = $search->queryTable($tableName, $queryParams); return $search->convertToJson($nqlQuery, count($nqlQuery->getSelect()->getColumns())); } }