Commit b9c69db1 authored by Никита Костюничев's avatar Никита Костюничев 💤
Browse files

Merge branch 'hotfix/content-type' into 'test'

fix: Исправлено форматирование сложных состояний по типу фильтра. Добавлена...

See merge request !227
parents 14381859 a6be6189
......@@ -44,6 +44,11 @@ class QueryBusHandlerLocator
}
}
public function setLazyLoader(QueryHandlerLoaderInterface $loader): void
{
$this->loader = $loader;
}
/**
* @param QueryInterface $query
*
......
......@@ -10,7 +10,7 @@ namespace ServiceCore\Application\Service;
interface StateServiceInterface
{
/**
* @param int[]|int $states
* @param int[] $states
* @param array $recordData
* @param string $logicalOperator
* @param bool $addWhere
......@@ -18,7 +18,7 @@ interface StateServiceInterface
* @return array
*/
public function convertToApiQl(
$states,
array $states,
array $recordData = [],
string $logicalOperator = 'and',
bool $addWhere = true
......
......@@ -3,7 +3,7 @@
namespace ServiceCore\Infrastructure\Delivery;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMException;
......@@ -153,18 +153,84 @@ abstract class AbstractBootstrap
$this->di->set('event_provider', new DomainEventProvider($this->di->get('user_information')));
$this->di->set('event_bus', new DomainEventBus());
$this->di->set("command_bus", new CommandBus(
new CommandBusHandlerLocator([], $this->getCommandContext()),
$this->loadCommandBus();
$this->loadQueryBus();
$this->afterLoadDi();
}
/**
* Загрузка шины команд
*/
protected function loadCommandBus()
{
$commandContext = $this->getCommandContext();
$commandLocator = new CommandBusHandlerLocator(
$this->getCommandBusRoutes(),
$commandContext
);
$this->di->set('command_bus', new CommandBus(
$commandLocator,
new DoctrineORMTransactionService($this->entityManager),
$this->di->get('event_provider'),
$this->di->get('event_bus')
));
$this->di->set('command_context', $commandContext);
$this->di->set('command_locator', $commandLocator);
}
$this->di->set('query_bus', new QueryBus(
new QueryBusHandlerLocator([], $this->getQueryContext())
));
/**
* Routes for calling command handlers using CommandBus
*
* ```php
* return [
* EntityCreateCommand::class => new EntityCreateHandler(
* $this->di->get('entity_command_repository')
* )
* ]
* ```
*
* @return array
*/
protected function getCommandBusRoutes(): array
{
return [];
}
$this->afterLoadDi();
/**
* Загрузка шины запросов
*/
protected function loadQueryBus()
{
$queryContext = $this->getQueryContext();
$queryLocator = new QueryBusHandlerLocator(
$this->getQueryBusRoutes(),
$queryContext
);
$this->di->set('query_bus', new QueryBus($queryLocator));
$this->di->set('query_context', $queryContext);
$this->di->set('query_locator', $queryLocator);
}
/**
* Routes for calling query handlers using QueryBus
*
* ```php
* return [
* EntityQuery::class => new EntityQueryHandler(
* $this->di->get('entity_query_repository')
* )
* ]
* ```
*
* @return array
*/
protected function getQueryBusRoutes(): array
{
return [];
}
/**
......
......@@ -11,7 +11,10 @@ use Doctrine\DBAL\Query\QueryBuilder;
*
* @property QueryBuilder $queryBuilder
* @property Connection $connection
*
* @property string[] $jsonFields
* @property bool $payload
* @property bool $ordered
* @property string $orderedBy
* @property string $tableName
*
* @package ServiceCore\Infrastructure\Domain\Repository
......@@ -134,12 +137,14 @@ trait QueryableTrait
if (isset($result[0])) {
foreach ($result as $index => $value) {
foreach ($this->jsonFields as $field) {
$result[$index][$field] = json_decode($value[$field], true);
if (isset($result[$index][$field])) {
$result[$index][$field] = json_decode($value[$field], true);
}
}
}
} else {
foreach ($this->jsonFields as $field) {
if (isset($result[$field])) {
if (isset($result[$field]) && is_string($result[$field])) {
$result[$field] = json_decode($result[$field], true);
}
}
......
<?php
namespace ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception;
use ServiceCore\Domain\Exception\AbstractDomainException;
/**
* Class UnexpectedFileType
*
* @package ReportEditor\Domain\Exception
*/
class ParameterNotSet extends AbstractDomainException
{
protected $code = 'parameter_not_set';
}
......@@ -6,6 +6,7 @@ use ApiQL\ApiQL;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
use ServiceCore\Domain\Repository\ReportEditor\ReportQueryRepositoryInterface;
use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ParameterNotSet;
use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ReportSourceAliasNotDefined;
use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ReportSourcePropertiesIsInvalid;
use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ReportSourceRelationNotDefined;
......@@ -126,7 +127,7 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
if ($this->validateProperties($reportSource['properties'])) {
if ($reportSource['report_source_type_id'] === 'extended_object') {
$extendedObject = $this->getExtendedObject($reportSource['properties']['id']);
$sourceQuery = $this->buildExtendedObjectQuery($extendedObject);
$sourceQuery = $this->buildExtendedObjectQuery($extendedObject, $payload ?? []);
} else {
$sourceQuery = sprintf("SELECT * FROM %s", $this->getSourceTable(
$reportSource['report_source_type_id'],
......@@ -168,10 +169,11 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
/**
* @param array $extendedObject
* @param array $payload
*
* @return string
*/
private function buildExtendedObjectQuery(array $extendedObject): string
private function buildExtendedObjectQuery(array $extendedObject, array $payload): string
{
$sqlStatements = $extendedObject['sql_statements'];
......@@ -192,8 +194,20 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
));
}
if (!empty($extendedObject['filter'])) {
$apiQL = new ApiQL($extendedObject['filter'], $query);
$filter = $extendedObject['filter'];
// Форматируем фильтры
if (!empty($filter) && isset($filter['logical_operator']) && isset($filter['children'])) {
if (is_array($filter['children']) && count($filter['children']) > 0) {
$filter = $this->toApiQl($filter, $payload);
} else {
$filter = null;
}
}
// Крепим после форматирования
if (!empty($filter)) {
$apiQL = new ApiQL($filter, $query);
$apiQL->execute();
}
......@@ -210,6 +224,166 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
return $sourceQuery;
}
/**
* @param array $filter
* @param array $parameters
*
* @return array|array[]
*
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function toApiQl(array $filter, array $parameters = [])
{
$logicalOperator = $filter['logical_operator'];
$result = [
$logicalOperator => []
];
foreach ($filter['children'] as $child) {
$query = $child['query'];
if ($child['type'] === 'condition_group') {
$result[$logicalOperator][] = $this->toApiQl($query, $parameters);
} else {
if (in_array($query['operator'], ['is_null', 'is_not_null'])) {
$result[$logicalOperator][] = [
$query['operator'] => $query['field']
];
} else {
$value = $this->getValueByFilterType($query, $parameters);
if ($value) {
$result[$logicalOperator][] = [
$query['operator'] => [
$query['field'] => $value
]
];
}
}
}
}
return $result;
}
/**
* @param array $filter
* @param array $parameters
*
* @return mixed|null
*
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function getValueByFilterType(array $filter, array $parameters) {
// Докрутить таблицу сотрудников, текущего пользователя и параметр
switch ($filter['filter_type']) {
case 'users_table':
if ($filter['value']) {
return $this->getUserFieldValue($filter['value'], $this->userInfo->id);
}
return null;
case 'current_user':
return $this->userInfo->id;
case 'current_datetime':
return date('Y-m-d H:i:s');
case 'parameter':
$key = $filter['value'];
if (isset($parameters[$key])) {
return $parameters[$key];
}
return null;
default:
return $filter['value'];
}
}
/**
* @return false|int
*
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function getUsersTableId()
{
return $this->connection
->createQueryBuilder()
->select('id')
->from("object_editor.entities")
->where("\"getPropertyById\"(properties, 'is_users_table') = 'true'")
->execute()
->fetchOne();
}
/**
* @param int $fieldId
*
* @return string
*
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function getFieldType(int $fieldId): string
{
return $this->connection
->createQueryBuilder()
->select("entity_type_id")
->from("object_editor.entities")
->where("id = :fieldId")
->setParameter("fieldId", $fieldId)
->execute()
->fetchOne();
}
/**
* @param int $fieldId
* @param int $userId
*
* @return mixed|null
*
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function getUserFieldValue(int $fieldId, int $userId)
{
$tableId = $this->getUsersTableId();
if (!$tableId) {
return null;
}
$value = $this->connection
->createQueryBuilder()
->select(sprintf("attr_%s_", $fieldId))
->from(sprintf("registry.object_%s_main", $tableId))
->where("id = :userId")
->setParameter("userId", $userId)
->execute()
->fetchOne();
if (!$value) {
return null;
}
$fieldType = $this->getFieldType($fieldId);
if ($fieldType) {
if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
return array_column(
json_decode(
$value,
true
),
'id'
);
}
}
return $value;
}
/**
* @param array $sources
* @param int|null $parentId
......@@ -384,9 +558,11 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
/**
* @param array $filters
* @param array $payload
* @param array|null $payload
*
* @return array
*
* @throws ParameterNotSet
*/
private function buildExpression(array $filters, ?array $payload): array
{
......@@ -398,14 +574,23 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
$fieldValue = $filter['properties']['value'];
if ($filterType === 'parameter' && !empty($payload)) {
isset($payload[$fieldName]) ? $fieldValue = $payload[$fieldName] : $fieldValue = null;
$parameterName = empty($filter['properties']['value'])
? 'id'
: $filter['properties']['value']
;
if (!isset($payload[$parameterName])) {
throw new ParameterNotSet('Undefined values for one or more parameters', [
'payload' => $payload,
'filter' => $filter
]);
}
$fieldValue = $payload[$parameterName];
} elseif ($filterType === 'constant') {
$fieldValue = $filter['properties']['value'];
$fieldValue = $filter['properties']['value'] ?? null;
} elseif ($filterType === 'current_user') {
$fieldValue = $this->userInfo->id;
} elseif ($filterType === 'current_date') {
} elseif ($filterType === 'current_datetime') {
} elseif ($filterType === 'users_table') {
} elseif ($filterType === 'condition') {
$expression['and'][] = [
$fieldValue => sprintf('"s.%s"', $fieldValue)
......
......@@ -24,7 +24,7 @@ class BodyParserMiddleware implements MiddlewareInterface
public function beforeHandleRoute(Event $event, Micro $application): void
{
$contentType = strtolower($application->request->getHeader('CONTENT_TYPE'));
if ($contentType === 'application/json' || $contentType === 'application/json;charset=utf-8') {
if (stripos($contentType, 'application/json') === 0) {
$requestBody = $application->request->getRawBody();
if (!empty($requestBody)) {
$jsonRawBody = json_decode($requestBody, true);
......
......@@ -25,7 +25,7 @@ class Converter
]
);
return $addWhere ? ['where' => $result] : $result;
return $addWhere ? ['where' => $result[0]] : $result[0];
}
/**
......@@ -50,7 +50,7 @@ class Converter
} else {
$result[] = [
$expr['query']['operator'] => [
$expr['query']['field'] => $expr['query']['value']
$expr['query']['field'] => $expr['query']['y_value']
]
];
}
......
......@@ -46,14 +46,14 @@ class ComplexStateFormatter
$this->recordData = $recordData;
}
public function format(array $state): array
public function format(array $state): ?array
{
if (!isset($state['properties'])) {
return [];
return null;
}
if (!isset($state['properties']['filters'])) {
return [];
return null;
}
return $this->finalFormat([
......@@ -64,6 +64,13 @@ class ComplexStateFormatter
])[0];
}
/**
* @param array $data
*
* @return array
*
* @throws \Exception
*/
private function finalFormat(array $data): array
{
$expressions = [];
......@@ -78,25 +85,112 @@ class ComplexStateFormatter
]
];
} else {
$xField = $item['query']['field'];
$fieldId = (int)filter_var($item['query']['field'], FILTER_SANITIZE_NUMBER_INT);
$fieldType = $this->stateRepository->getFieldType($fieldId);
$xFieldType = !isset($item['query']['field_type'])
? $this->stateRepository->getFieldType($fieldId)
: $item['query']['field_type'];
$filterType = $item['query']['filter_type'] ?? 'constant';
$operator = $item['query']['operator'] ?? 'eq';
$xValue = $this->getFieldValue($xField, $xFieldType);
$yValue = null;
if ($filterType === 'condition') {
$operator = $item['query']['value'] === 'not_empty'
? 'is_not_null'
: 'is_null';
} else {
$yValue = $this->getYValue($filterType, $item['query']['value'] ?? null);
}
if ($fieldType === 'xref_field' || $fieldType === 'xref_multi_field') {
$item['query']['field'] = sprintf('%sid', $item['query']['field']);
if (in_array($xFieldType, ['xref_field', 'xref_multi_field'])) {
$xField = sprintf('%sid', $xField);
if ($item['query']['operator'] === 'eq') {
$item['query']['operator'] = 'equals_any';
} elseif ($item['query']['operator'] === 'neq') {
$item['query']['operator'] = 'not_equals_any';
if ($operator === 'eq') {
$operator = 'equals_any';
}
}
$item['query']['field_type'] = $fieldType;
$expressions[] = $item;
$expressions[] = [
'type' => 'condition',
'query' => [
'field' => $xField,
'field_type' => $xFieldType,
'x_value' => $xValue,
'y_value' => $yValue,
'operator' => $operator
]
];
}
}
return $expressions;
}
/**
* @param string $filterType
* @param $value
*
* @return mixed
*
* @throws \Exception
*/
private function getYValue(string $filterType, $value)
{
switch ($filterType) {
case 'current_user':
return $this->userInfo->id;
case 'users_table':
return $this->stateRepository->getUserFieldValue(
$value,
$this->userInfo->id
);
case 'current_date':
return date("Y-m-d");
case 'current_datetime':
return date("Y-m-d H:i:s");
case 'current_table_field':
if (!empty($value)) {
$field = sprintf('attr_%s_', $value);
$fieldType = $this->stateRepository->getFieldType($value);
return $this->getFieldValue($field, $fieldType);
} else {
throw new \Exception('Состояние с типом "Поле текущей таблицы" не настроено');
}
case 'external_table_field':
throw new \Exception('Состояние с типом "Поле внещней таблицы" не обслуживается');
default:
if (is_null($value) || $value === '') {
throw new \Exception('Состояние с типом "Константа" соддержит пустое значение');
}
return $value;
}
}
private function getFieldValue(string $field, string $fieldType) {
if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
$value = $this->recordData[$field . 'id'] ?? [];
if (is_string($value)) {
$value = str_replace('{', '[', $value);
$value = str_replace('}', ']', $value);
$value = json_decode($value, true);
} elseif (is_null($value)) {
$value = [];
} elseif (is_numeric($value)) {
$value = [$value];
}
return $value;
} elseif ($fieldType === 'boolean_field') {
$value = $this->recordData[$field] ?? null;
return $value === 'true' || $value === true;
}
return $this->recordData[$field] ?? null;
}
}
......@@ -121,7 +121,21 @@ class Formatter
];
foreach ($states as $state) {
$exprGroups['query']['children'][] = $this->formatting($state);
$childGroup = $this->formatting($state);
if (!is_null($childGroup)) {
if (
$childGroup['query']['logical_operator'] === $logicalOperator
&& count($childGroup['query']['children']) === 1
) {
$exprGroups['query']['children'] = array_merge(
$exprGroups['query']['children'],
$childGroup['query']['children']
);
} else {
$exprGroups['query']['children'][] = $childGroup;
}
}
}
return $exprGroups;
......@@ -132,7 +146,7 @@ class Formatter
*
* @return array
*/
private function formatting(array $state): array {
private function formatting(array $state): ?array {
if ($state['is_complex'] === true) {
return $this->complexFormatter->format($state);
} else {
......
......@@ -46,78 +46,78 @@ class SimpleStateFormatter
$this->recordData = $recordData;
}
public function format(array $state): array
public function format(array $state): ?array
{
if (
!is_array($state['state_fields'])
|| (
is_array($state['state_fields'])
&& count($state['state_fields']) === 0
)
) {
return null;
}
$expressions = [];
foreach ($state['state_fields'] as $stateField) {
$attribute = sprintf('attr_%d_', $stateField['field_id']);
$fieldType = $this->stateRepository->getFieldType($stateField['field_id']);
$xField = sprintf('attr_%d_', $stateField['field_id']);
$xFieldType = $this->stateRepository->getFieldType($stateField['field_id']);
$filterType = $stateField['state_field_type_id'];
$operator = 'eq';
$value = null;
if ($stateField['state_field_type_id'] === 'current_user') {
$value = $this->userInfo->id;
} elseif ($stateField['state_field_type_id'] === 'users_table') {
$value = $this->stateRepository->getUserFieldValue($stateField['users_field_id'], $this->userInfo->id);
} elseif ($stateField['state_field_type_id'] === 'current_date') {
$value = date('Y-m-d');
} elseif ($stateField['state_field_type_id'] === 'condition') {
if ($stateField['state_field_type_id'] === 'not_empty') {
$xValue = $this->getFieldValue($xField, $xFieldType);
$yValue = null;
// Вычисляем значение Y
if ($filterType === 'current_user') {
$yValue = $this->userInfo->id;
} elseif ($filterType === 'users_table') {
$yValue = $this->stateRepository->getUserFieldValue($stateField['users_field_id'], $this->userInfo->id);
} elseif ($filterType === 'current_date') {
$yValue = date('Y-m-d');
} elseif ($filterType === 'current_datetime') {
$yValue = date('Y-m-d H:i:s');
} elseif ($filterType === 'condition') {
if ($stateField['condition_field_type_id'] === 'not_empty') {
$operator = 'is_not_null';
} else {
$operator = 'is_null';
}
} elseif ($stateField['state_field_type_id'] === 'current_table_field') {
} elseif ($filterType === 'current_table_field') {
if (!empty($stateField['current_table_field_id'])) {
$field = sprintf('attr_%d_', $stateField['current_table_field_id']);
$yField = sprintf('attr_%s_', $stateField['current_table_field_id']);
$yFieldType = $this->stateRepository->getFieldType($stateField['current_table_field_id']);
$value = isset($this->recordData[$field]) ? $this->recordData[$field] : null;
$yValue = $this->getFieldValue($yField, $yFieldType);
} else {
throw new \Exception('Состояние с типом "Поле текущей таблицы" не настроено');
}
} elseif ($filterType === 'external_table_field') {
throw new \Exception('Состояние с типом "Поле внещней таблицы" не обслуживается');
} elseif ($stateField['state_field_type_id'] === 'constant') {
$value = $stateField['properties']['value'];
} else {
continue;
}
$yValue = $stateField['properties']['value'] ?? null;
if ($fieldType === 'boolean_field') {
if ($value === 'true' || $value === true) {
$value = true;
} else {
$value = false;
if (is_null($yValue)) {
throw new \Exception('Состояние с типом "Константа" соддержит пустое значение');
}
}
if ($fieldType === 'xref_field' || $fieldType === 'xref_multi_field') {
$attribute = sprintf('%sid', $attribute);
if (in_array($xFieldType, ['xref_field', 'xref_multi_field'])) {
$xField = sprintf('%sid', $xField);
if ($operator === 'eq') {
$operator = 'equals_any';
}
if (!empty($this->recordData)) {
if (!$value) {
$value = isset($this->recordData[$attribute]) ? $this->recordData[$attribute] : null;
}
if (is_string($value)) {
$value = json_decode($value, true);
}
if (is_null($value)) {
$value = [];
}
}
}
$expressions[] = [
'type' => 'condition',
'query' => [
'field' => $attribute,
'value' => $value,
'filter_type' => 'constant',
'operator' => $operator,
'field_type' => $fieldType
'field' => $xField,
'field_type' => $xFieldType,
'x_value' => $xValue,
'y_value' => $yValue,
'operator' => $operator
]
];
}
......@@ -130,4 +130,29 @@ class SimpleStateFormatter
]
];
}
private function getFieldValue(string $field, string $fieldType) {
if ($fieldType === 'xref_field' || $fieldType === 'xref_multi_field') {
$value = $this->recordData[$field . 'id'] ?? [];
if (is_string($value)) {
$value = str_replace('{', '[', $value);
$value = str_replace('}', ']', $value);
$value = json_decode($value, true);
} elseif (is_null($value)) {
$value = [];
} elseif (is_numeric($value)) {
$value = [$value];
}
return $value;
} elseif ($fieldType === 'boolean_field') {
$value = $this->recordData[$field] ?? null;
return $value === 'true' || $value === true;
}
return $this->recordData[$field] ?? null;
}
}
......@@ -68,6 +68,175 @@ class StateService implements StateServiceInterface
);
}
/**
* @param array $states - Список идентификаторов состояний
* @param array $recordData - Актуальные данные записи реестра, если необходимо использование типа "Поле текущей таблицы"
* @param string $matchType - Логическуая связка "all", "any", или "and", "or"
* @param bool $isDeny - Отрицание условия `if (!$myVar)`
*
* @return bool
*
* @throw \Exception
*/
public function checkConditions(
array $states,
array $recordData = [],
string $matchType = 'all',
bool $isDeny = false
) {
$matches = [];
if (!empty($states)) {
foreach ($states as $stateId) {
$matches[] = $this->isEqualsState(
$stateId,
$recordData
);
}
if (in_array($matchType, ['all', 'and'])) {
/*
* Если логическая связка "Все", то есть "И"
* Если (Состояние1 И Состояние2 И ...)
*/
if ($isDeny) {
/*
* Если "Не соответствует условию" И все состояния в логической связке "И" прошли проверку
* Тогда пропускаем ограничение
*/
if (!in_array(false, $matches)) {
return false;
}
} else {
/*
* Если "Соответствует условию"
* И одно состояние в логической связке "И" не проходит проверку
* Тогда пропускаем ограничение
*/
if (in_array(false, $matches)) {
return false;
}
}
} else {
/*
* Если логическая связка "Любое", то есть "ИЛИ"
* Если (Состояние1 ИЛИ Состояние2 ИЛИ ...)
*/
if ($isDeny) {
/*
* Если "Не соответствует условию"
* И одно из состояний в логической связке "ИЛИ" прошло проверку
* Тогда пропускаем ограничение
*/
if (in_array(true, $matches)) {
return false;
}
} else {
/*
* Если "Соответствует условию"
* И все состояния в логической связке "ИЛИ" не прошли проверку
* Тогда пропускаем ограничение
*/
if (!in_array(true, $matches)) {
return false;
}
}
}
}
return true;
}
public function isEqualsState(int $stateId, array $recordData = []): bool
{
$expressions = $this->format($stateId, $recordData);
$expressions = [$expressions];
return $this->isEqualsStateRecursive($expressions, $recordData);
}
private function isEqualsStateRecursive(array $expressions, array $recordData = [], string $logicalOperator = 'and'): bool
{
$matches = [];
foreach ($expressions as $expr) {
if ($expr['type'] === 'condition_group') {
$matches[] = $this->isEqualsStateRecursive($expr['query']['children'], $recordData, $expr['query']['logical_operator']);
} else {
$xValue = $expr['query']['x_value'];
$yValue = $expr['query']['y_value'];
switch ($expr['query']['operator']) {
case 'eq':
$matches[] = $xValue == $yValue;
break;
case 'neq':
$matches[] = $xValue != $yValue;
break;
case 'in':
case 'equals_any':
$xValue = !is_array($xValue)
? (array) $xValue
: $xValue;
$yValue = !is_array($yValue)
? (array) $yValue
: $yValue;
$matches[] = count(array_intersect($xValue, $yValue)) > 0;
break;
case 'not_in':
case 'not_equals_any':
$xValue = !is_array($xValue)
? (array) $xValue
: $xValue;
$yValue = !is_array($yValue)
? (array) $yValue
: $yValue;
$matches[] = count(array_intersect($xValue, $yValue)) === 0;
break;
case 'gt':
$matches[] = $xValue > $yValue;
break;
case 'lt':
$matches[] = $xValue < $yValue;
break;
case 'gte':
$matches[] = $xValue >= $yValue;
break;
case 'lte':
$matches[] = $xValue <= $yValue;
break;
case 'is_null':
$matches[] = empty($xValue);
break;
case 'is_not_null':
$matches[] = !empty($xValue);
break;
case 'like':
$matches[] = strpos(mb_strtolower($xValue), mb_strtolower($yValue)) !== false;
break;
case 'not_like':
$matches[] = strpos(mb_strtolower($xValue), mb_strtolower($yValue)) === false;
break;
default:
$matches[] = false;
break;
}
}
}
if ($logicalOperator === 'and') {
// Если одно из всех выражение вернёт ЛОЖЬ, тогда условие не прошло проверку
return !in_array(false, $matches);
}
// если любое (any, or) из выражений равно ИСТИНА, тогда условие прошло проверку
return in_array(true, $matches);
}
/**
* Форматирует разные состояния, простые и сложные, одно или несколько, в один формат
*
......@@ -86,7 +255,13 @@ class StateService implements StateServiceInterface
);
if (is_array($states)) {
return $formatter->format($states)->many($logicalOperator);
if (count($states) > 1) {
return $formatter->format($states)->many($logicalOperator);
} else if (count($states) == 1) {
return $formatter->format($states[0])->one();
} else {
return [];
}
} elseif (is_numeric($states)) {
return $formatter->format($states)->one();
} else {
......
......@@ -6,100 +6,187 @@ use ServiceCore\Domain\Repository\StateQueryRepositoryInterface;
class StateRepository implements StateQueryRepositoryInterface
{
public function getState(int $id): ?array
{
$states = [
[
'id' => 1,
'name' => 'Simple State',
'is_complex' => false,
'properties' => null,
'state_fields' => [
[
'id' => 1,
'state_id' => 1,
'field_id' => 1000,
'state_field_type_id' => 'constant',
'properties' => [
'value' => 'str1'
]
],
[
'id' => 2,
'state_id' => 1,
'field_id' => 1001,
'state_field_type_id' => 'constant',
'properties' => [
'value' => '314zdec'
]
],
[
'id' => 3,
'state_id' => 1,
'field_id' => 1002,
'state_field_type_id' => 'condition',
'condition_field_type_id' => 'not_empty'
],
[
'id' => 4,
'state_id' => 1,
'field_id' => 1003,
'state_field_type_id' => 'current_table_field',
'current_table_field_id' => 2000
static $states = [
[
'id' => 1,
'name' => 'Simple State',
'is_complex' => false,
'properties' => null,
'state_fields' => [
[
'id' => 1,
'state_id' => 1,
'field_id' => 1000,
'state_field_type_id' => 'constant',
'properties' => [
'value' => 'str1'
]
],
[
'id' => 2,
'state_id' => 1,
'field_id' => 1001,
'state_field_type_id' => 'constant',
'properties' => [
'value' => '314zdec'
]
],
[
'id' => 3,
'state_id' => 1,
'field_id' => 1002,
'state_field_type_id' => 'condition',
'condition_field_type_id' => 'not_empty'
],
[
'id' => 4,
'state_id' => 1,
'field_id' => 1003,
'state_field_type_id' => 'current_table_field',
'current_table_field_id' => 1002
]
],
[
'id' => 2,
'name' => 'Complex State',
'is_complex' => true,
'properties' => [
'filters' => [
'logical_operator' => 'and',
'children' => [
[
'type' => 'condition',
'query' => [
'field' => 'attr_1010_',
'filter_type' => 'constant',
'operator' => 'eq',
'value' => '2021-07-15'
]
],
[
'type' => 'condition_group',
'query' => [
'logical_operator' => 'or',
'children' => [
[
'type' => 'condition',
'query' => [
'field' => 'attr_1011_',
'filter_type' => 'constant',
'operator' => 'gte',
'value' => '2022-01-01'
]
],
[
'type' => 'condition',
'query' => [
'field' => 'attr_1011_',
'filter_type' => 'constant',
'operator' => 'lte',
'value' => '2021-06-01'
]
]
],
[
'id' => 2,
'name' => 'Complex State',
'is_complex' => true,
'properties' => [
'filters' => [
'logical_operator' => 'and',
'children' => [
[
'type' => 'condition',
'query' => [
'field' => 'attr_1010_',
'filter_type' => 'constant',
'operator' => 'eq',
'value' => '2021-07-15'
]
],
[
'type' => 'condition_group',
'query' => [
'logical_operator' => 'or',
'children' => [
[
'type' => 'condition',
'query' => [
'field' => 'attr_1011_',
'filter_type' => 'constant',
'operator' => 'gte',
'value' => '2022-01-01'
]
],
[
'type' => 'condition',
'query' => [
'field' => 'attr_1011_',
'filter_type' => 'constant',
'operator' => 'lte',
'value' => '2021-06-01'
]
]
]
]
]
]
]
],
'state_fields' => []
],
[
'id' => 3,
'name' => 'Simple State 2',
'is_complex' => false,
'properties' => null,
'state_fields' => [
[
'id' => 1,
'state_id' => 1,
'field_id' => 2000,
'state_field_type_id' => 'constant',
'properties' => [
'value' => 4
]
],
'state_fields' => []
[
'id' => 2,
'state_id' => 1,
'field_id' => 2001,
'state_field_type_id' => 'constant',
'properties' => [
'value' => 0
]
]
]
];
],
[
'id' => 4,
'name' => 'Complex State',
'is_complex' => true,
'properties' => [
'filters' => [
'logical_operator' => 'and',
'children' => [
[
'type' => 'condition',
'query' => [
'field' => 'attr_2001_',
'filter_type' => 'users_table',
'operator' => 'eq',
'value' => 3001
]
]
]
]
],
'state_fields' => []
]
];
static $fields = [
[
'id' => 1000,
'type' => 'string_field'
],
[
'id' => 1001,
'type' => 'string_field'
],
[
'id' => 1002,
'type' => 'string_field'
],
[
'id' => 1003,
'type' => 'string_field'
],
[
'id' => 1010,
'type' => 'date_field'
],
[
'id' => 1011,
'type' => 'date_field'
],
[
'id' => 2000,
'type' => 'xref_field'
],
[
'id' => 2001,
'type' => 'xref_field'
],
[
'id' => 3001,
'type' => 'xref_field'
]
];
foreach ($states as $state) {
public function getState(int $id): ?array
{
foreach (self::$states as $state) {
if ($state['id'] === $id) {
return $state;
}
......@@ -108,44 +195,50 @@ class StateRepository implements StateQueryRepositoryInterface
return null;
}
public function getFieldType(int $fieldId): string {
$fields = [
[
'id' => 1000,
'type' => 'string_field'
],
[
'id' => 1001,
'type' => 'string_field'
],
[
'id' => 1002,
'type' => 'string_field'
],
[
'id' => 1003,
'type' => 'string_field'
],
[
'id' => 1010,
'type' => 'date_field'
],
public function getFieldType(int $fieldId): string
{
foreach (self::$fields as $field) {
if ($field['id'] === $fieldId) {
return $field['type'];
}
}
return 'undef_field';
}
public function getUserFieldValue(int $fieldId, int $userId)
{
$users = [
[
'id' => 1011,
'type' => 'date_field'
'id' => 1,
'username' => 'test1',
'attr_3001_' => '[{"id": 1, "name": "Name1"}]',
'attr_3001_id' => 1
]
];
foreach ($fields as $field) {
if ($field['id'] === $fieldId) {
return $field['type'];
$targetAttribute = sprintf('attr_%s_', $fieldId);
$value = null;
foreach ($users as $user) {
if ($user['id'] === $userId) {
$value = $user[$targetAttribute] ?? null;
break;
}
}
return 'huy_field';
}
$fieldType = $this->getFieldType($fieldId);
if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
return array_column(
json_decode(
$value,
true
),
'id'
);
}
public function getUserFieldValue(int $fieldId, int $userId): int {
return 666;
return $value;
}
}
......@@ -24,13 +24,18 @@ class StateUnitTest extends TestCase
$this->recordData = [
'id' => 1,
'attr_1000_' => 'str1',
'attr_1001_' => 'str2',
'attr_1001_' => '314zdec',
'attr_1002_' => 'str3',
'attr_1003_' => 'str4',
'attr_1003_' => 'str3',
'attr_2010_' => '2022-12-01',
'attr_2011_' => '2022-12-01',
// Для сравнения одного поля с другим полем в текущей таблице
'attr_2000_' => 'value of the current table field'
'attr_2000_' => [],
'attr_2001_' => [],
'attr_3001_' => [],
'attr_2000_id' => null,
'attr_2001_id' => null,
'attr_3001_id' => null
];
$this->userInfo = new \stdClass();
......@@ -39,78 +44,126 @@ class StateUnitTest extends TestCase
$this->repository = new StateRepository();
}
/**
* Баг с логической группировкой состояний для множественной конвертации
*/
public function testStateBag1()
{
$service = new StateService($this->repository, $this->userInfo);
$result = $service->convertToApiQl(3, $this->recordData);
$testResult = '{"where":[{"and":[{"equals_any":{"attr_2000_id":4}},{"equals_any":{"attr_2001_id":0}}]}]}';
self::assertEquals(json_encode($result), $testResult);
}
/**
* Баг с получением значения из поля таблицы сотрудников, если оно с типом "Ссылка"
*/
public function testStateBag2()
{
$service = new StateService($this->repository, $this->userInfo);
$result = $service->convertToApiQl(4, $this->recordData);
$testResult = '{"where":[{"and":[{"equals_any":{"attr_2001_id":[1]}}]}]}';
self::assertEquals($testResult, json_encode($result));
}
public function testIsEqualsState()
{
$service = new StateService($this->repository, $this->userInfo);
$result = $service->isEqualsState(1, $this->recordData);
$testResult = true;
self::assertEquals($testResult, $result);
}
public function testCheckConditions()
{
$service = new StateService($this->repository, $this->userInfo);
$result = $service->checkConditions([1], $this->recordData);
$testResult = true;
self::assertEquals($testResult, $result);
}
public function testStateFormatting(): void
{
$service = new StateService($this->repository, $this->userInfo);
$result = $service->format(1);
$result = $service->format(1, $this->recordData);
$testResult =
'{"type":"condition_group","query":{"logical_operator":"and","children":[{"type":"condition","query":{"fi' .
'eld":"attr_1000_","value":"str1","filter_type":"constant","operator":"eq","field_type":"string_field"}},' .
'{"type":"condition","query":{"field":"attr_1001_","value":"314zdec","filter_type":"constant","operator":' .
'"eq","field_type":"string_field"}},{"type":"condition","query":{"field":"attr_1002_","value":null,"filte' .
'r_type":"constant","operator":"is_null","field_type":"string_field"}},{"type":"condition","query":{"fiel' .
'd":"attr_1003_","value":null,"filter_type":"constant","operator":"eq","field_type":"string_field"}}]}}'
'eld":"attr_1000_","field_type":"string_field","x_value":"str1","y_value":"str1","operator":"eq"}},{"type' .
'":"condition","query":{"field":"attr_1001_","field_type":"string_field","x_value":"314zdec","y_value":"3' .
'14zdec","operator":"eq"}},{"type":"condition","query":{"field":"attr_1002_","field_type":"string_field",' .
'"x_value":"str3","y_value":null,"operator":"is_not_null"}},{"type":"condition","query":{"field":"attr_10' .
'03_","field_type":"string_field","x_value":"str3","y_value":"str3","operator":"eq"}}]}}'
;
self::assertEquals(json_encode($result), $testResult);
self::assertEquals($testResult, json_encode($result));
$result = $service->format(2);
$result = $service->format(2, $this->recordData);
$testResult =
'{"type":"condition_group","query":{"logical_operator":"and","children":[{"type":"condition","query":{"fi' .
'eld":"attr_1010_","filter_type":"constant","operator":"eq","value":"2021-07-15","field_type":"date_field' .
'"}},{"type":"condition_group","query":{"logical_operator":"or","children":[{"type":"condition","query":{' .
'"field":"attr_1011_","filter_type":"constant","operator":"gte","value":"2022-01-01","field_type":"date_f' .
'ield"}},{"type":"condition","query":{"field":"attr_1011_","filter_type":"constant","operator":"lte","val' .
'ue":"2021-06-01","field_type":"date_field"}}]}}]}}'
'eld":"attr_1010_","field_type":"date_field","x_value":null,"y_value":"2021-07-15","operator":"eq"}},{"ty' .
'pe":"condition_group","query":{"logical_operator":"or","children":[{"type":"condition","query":{"field":' .
'"attr_1011_","field_type":"date_field","x_value":null,"y_value":"2022-01-01","operator":"gte"}},{"type":' .
'"condition","query":{"field":"attr_1011_","field_type":"date_field","x_value":null,"y_value":"2021-06-01' .
'","operator":"lte"}}]}}]}}'
;
self::assertEquals(json_encode($result), $testResult);
self::assertEquals($testResult, json_encode($result));
$result = $service->format([1, 2]);
$result = $service->format([1, 2], $this->recordData);
$testResult =
'{"type":"condition_group","query":{"logical_operator":"and","children":[{"type":"condition_group","query' .
'":{"logical_operator":"and","children":[{"type":"condition","query":{"field":"attr_1000_","value":"str1"' .
',"filter_type":"constant","operator":"eq","field_type":"string_field"}},{"type":"condition","query":{"fi' .
'eld":"attr_1001_","value":"314zdec","filter_type":"constant","operator":"eq","field_type":"string_field"' .
'}},{"type":"condition","query":{"field":"attr_1002_","value":null,"filter_type":"constant","operator":"i' .
's_null","field_type":"string_field"}},{"type":"condition","query":{"field":"attr_1003_","value":null,"fi' .
'lter_type":"constant","operator":"eq","field_type":"string_field"}}]}},{"type":"condition_group","query"' .
':{"logical_operator":"and","children":[{"type":"condition","query":{"field":"attr_1010_","filter_type":"' .
'constant","operator":"eq","value":"2021-07-15","field_type":"date_field"}},{"type":"condition_group","qu' .
'ery":{"logical_operator":"or","children":[{"type":"condition","query":{"field":"attr_1011_","filter_type' .
'":"constant","operator":"gte","value":"2022-01-01","field_type":"date_field"}},{"type":"condition","quer' .
'y":{"field":"attr_1011_","filter_type":"constant","operator":"lte","value":"2021-06-01","field_type":"da' .
'te_field"}}]}}]}}]}}'
'":{"logical_operator":"and","children":[{"type":"condition","query":{"field":"attr_1000_","field_type":"' .
'string_field","x_value":"str1","y_value":"str1","operator":"eq"}},{"type":"condition","query":{"field":"' .
'attr_1001_","field_type":"string_field","x_value":"314zdec","y_value":"314zdec","operator":"eq"}},{"type' .
'":"condition","query":{"field":"attr_1002_","field_type":"string_field","x_value":"str3","y_value":null,' .
'"operator":"is_not_null"}},{"type":"condition","query":{"field":"attr_1003_","field_type":"string_field"' .
',"x_value":"str3","y_value":"str3","operator":"eq"}}]}},{"type":"condition_group","query":{"logical_oper' .
'ator":"and","children":[{"type":"condition","query":{"field":"attr_1010_","field_type":"date_field","x_v' .
'alue":null,"y_value":"2021-07-15","operator":"eq"}},{"type":"condition_group","query":{"logical_operator' .
'":"or","children":[{"type":"condition","query":{"field":"attr_1011_","field_type":"date_field","x_value"' .
':null,"y_value":"2022-01-01","operator":"gte"}},{"type":"condition","query":{"field":"attr_1011_","field' .
'_type":"date_field","x_value":null,"y_value":"2021-06-01","operator":"lte"}}]}}]}}]}}'
;
self::assertEquals(json_encode($result), $testResult);
self::assertEquals($testResult, json_encode($result));
}
public function testStateConvertingToApiQl(): void
{
$service = new StateService($this->repository, $this->userInfo);
$result = $service->convertToApiQl(1, $this->recordData);
$result = $service->convertToApiQl([1], $this->recordData, 'and', false);
$testResult =
'{"where":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_null":"attr_1002_"},' .
'{"eq":{"attr_1003_":"value of the current table field"}}]}]}'
'{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_not_null":"attr_1002_"},{"eq":' .
'{"attr_1003_":"str3"}}]}'
;
self::assertEquals(json_encode($result), $testResult);
self::assertEquals($testResult, json_encode($result));
$result = $service->convertToApiQl(2, $this->recordData);
$result = $service->convertToApiQl([1], $this->recordData);
$testResult =
'{"where":[{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{' .
'"attr_1011_":"2021-06-01"}}]}]}]}'
'{"where":{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_not_null":"attr_1002_' .
'"},{"eq":{"attr_1003_":"str3"}}]}}'
;
self::assertEquals(json_encode($result), $testResult);
self::assertEquals($testResult, json_encode($result));
$result = $service->convertToApiQl([2], $this->recordData);
$testResult =
'{"where":{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{"' .
'attr_1011_":"2021-06-01"}}]}]}}'
;
self::assertEquals($testResult, json_encode($result));
$result = $service->convertToApiQl([1, 2], $this->recordData);
$testResult =
'{"where":[{"and":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_null":"attr_' .
'1002_"},{"eq":{"attr_1003_":"value of the current table field"}}]},{"and":[{"eq":{"attr_1010_":"2021-07-' .
'15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{"attr_1011_":"2021-06-01"}}]}]}]}]}'
'{"where":{"and":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_not_null":"at' .
'tr_1002_"},{"eq":{"attr_1003_":"str3"}}]},{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"att' .
'r_1011_":"2022-01-01"}},{"lte":{"attr_1011_":"2021-06-01"}}]}]}]}}'
;
self::assertEquals(json_encode($result), $testResult);
self::assertEquals($testResult, json_encode($result));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment