fox-1.1.2/src/FoxEntityQuery.php
src/FoxEntityQuery.php
<?php
declare(strict_types=1);
namespace Drupal\fox;
use Drupal\Component\Serialization\Json;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\fox\FoxCommandsHelper as Helper;
use PHPSQLParser\PHPSQLParser;
/**
* Fox entity query service.
*/
class FoxEntityQuery {
use StringTranslationTrait;
use FoxCommandsHelperTrait;
use FoxCommonFunctionsTrait;
/**
* Do entity query.
*
* @param string $input
* User input.
* @param array $variables
* Current variables.
* @param array $options
* Options values.
*
* @return array
* Result data.
*/
public function doEntityQuery(string $input, array $variables, array $options): array {
$helper = $this->foxCommandsHelper();
$query_string = $helper->stringRender($input, $variables);
$parser = new PHPSQLParser($query_string);
$parsed = $parser->parsed;
if (empty($parsed)) {
return $this->errorReturn($this->t('Wrong query or command: "@input"', [
'@input' => $input,
]));
}
$debug = $options['mode'] === 'debug';
$entity_type = $variables['entity_type'] ?? NULL;
$bundle = $variables['bundle'] ?? NULL;
// FROM clause.
if (isset($parsed['FROM'])) {
$from = current($parsed['FROM'])['no_quotes']['parts'];
[$entity_type, $bundle] = Helper::getTypeBundle($from);
}
else {
if (empty($entity_type)) {
return $this->errorReturn($this->t('Empty entity type'));
}
}
try {
$query = $helper->getEntityQuery($entity_type, $bundle);
// WHERE clause.
$where = $parsed['WHERE'] ?? NULL;
if ($where) {
$groups = $this->parseWhere($where);
foreach ($groups as $group) {
$conditionGroup = NULL;
if ($group['group_operator'] === 'AND') {
$conditionGroup = $query->andConditionGroup();
}
elseif ($group['group_operator'] === 'OR') {
$conditionGroup = $query->orConditionGroup();
}
if (empty($conditionGroup)) {
continue;
}
foreach ($group['conditions'] as $condition) {
$value = Helper::prepareValue($condition['value']);
$conditionGroup->condition($condition['field'], $value, $condition['operator']);
}
$query->condition($conditionGroup);
}
}
// ORDER clause.
$orders = $parsed['ORDER'] ?? NULL;
if ($orders) {
foreach ($orders as $order) {
$query->sort($order['base_expr'], $order['direction']);
}
}
// LIMIT clause.
$limits = $parsed['LIMIT'] ?? NULL;
if ($limits) {
$offset = 0;
if (is_numeric($limits['offset'])) {
$offset = $limits['offset'];
}
$limit = $limits['rowcount'];
$query->range($offset, $limit);
}
$ids = $query->execute();
}
catch (\Exception $e) {
return $this->errorReturn($e->getMessage());
}
// Format data.
$data = $header = $entities = [];
$header[] = '#';
$number = 1;
// SELECT clause.
$select = $parsed['SELECT'] ?? NULL;
if ($select) {
if (!empty($ids)) {
$entities = $helper->entityTypeManager()
->getStorage($entity_type)
->loadMultiple($ids);
}
$all_fields = FALSE;
foreach ($select as $id => $field) {
$name = $field['base_expr'];
if ($name === '*') {
$all_fields = TRUE;
unset($select[$id]);
continue;
}
$header[] = ucfirst($name);
}
// Get fields from entity type definition.
if ($all_fields) {
$entity_types_definitions = $helper
->entityTypeManager()
->getDefinitions();
$entity_type_definition = $entity_types_definitions[$entity_type] ?? NULL;
if ($entity_type_definition) {
$entity_type_group = $entity_type_definition->getGroup();
if ($entity_type_group === 'content') {
$definitions = $helper
->entityFieldManager()
->getFieldDefinitions($entity_type, $bundle ?? $entity_type);
}
else {
$definitions = $entity_type_definition->getPropertiesToExport();
}
foreach (array_keys($definitions) as $field) {
$header[] = ucfirst($field);
$select[] = ['base_expr' => $field];
}
}
}
foreach ($entities as $entity) {
$row = [];
$row[] = $number++;
foreach ($select as $field) {
$name = $field['base_expr'];
if (method_exists($entity, 'hasField') && !$entity->hasField($name)) {
return $this->errorReturn($this->t('Wrong field "@name"', [
'@name' => $name,
]));
}
$value = $entity->get($name);
if (!$value) {
$value = '';
}
if (is_object($value)) {
$value = $value->getString();
}
elseif (is_array($value)) {
$value = Json::encode($value);
}
$row[$name] = $value;
}
$data[] = $row;
}
}
else {
$header[] = $this->t('Id');
foreach ($ids as $id) {
$data[] = [$number++, $id];
}
}
$result = [
'message' => [
'header' => $header,
'data' => $data,
],
'variables' => [
'entity_type' => $entity_type,
'bundle' => $bundle,
'count' => count($data),
'id' => NULL,
],
];
// Set first occurency to record.
if (!empty($ids)) {
$first_id = reset($ids);
$recno = $helper->getRecnoById($first_id, $result['variables']);
if ($recno) {
$result['variables']['id'] = $first_id;
$result['variables']['recno'] = $recno;
}
}
else {
$result['variables']['id'] = NULL;
$result['variables']['recno'] = NULL;
$result['warning'] = $this->t('Empty data');
}
// INTO clause.
$into = $parsed['INTO'] ?? NULL;
if ($into) {
$name = $into[1];
$result['variables']['into'][$name] = $data ?? [];
$result['message'] = [];
}
if ($debug) {
if (method_exists($query, '__toString')) {
$result['debug'] = (string) $query;
}
}
return $result;
}
/**
* Where parsing.
*
* @param array $where
* Where data.
*
* @return array
* Condition groups.
*/
protected function parseWhere(array $where): array {
$groups = [];
$current_group_operator = NULL;
$group_index = -1;
for ($i = 0; $i < count($where);) {
if ($where[$i]['expr_type'] == 'colref') {
$value = $where[$i + 2]['base_expr'];
if ($where[$i + 2]['expr_type'] === 'in-list') {
$value = [];
foreach ($where[$i + 2]['sub_tree'] as $item) {
$value[] = $item['base_expr'];
}
}
$condition = [
'field' => $where[$i]['base_expr'],
'operator' => $where[$i + 1]['base_expr'],
'value' => $value,
];
$i += 3;
}
if ($i >= count($where)) {
if (empty($current_group_operator)) {
$group_operator = 'AND';
$groups[++$group_index] = [
'group_operator' => $group_operator,
'conditions' => [0 => $condition],
];
}
else {
$groups[$group_index]['conditions'][] = $condition;
}
break;
}
if ($where[$i]['expr_type'] == 'operator' && !empty($condition)) {
$group_operator = strtoupper($where[$i]['base_expr']);
if ($group_operator != $current_group_operator) {
$groups[++$group_index] = [
'group_operator' => $group_operator,
'conditions' => [0 => $condition],
];
$current_group_operator = $group_operator;
}
else {
$groups[$group_index]['conditions'][] = $condition;
}
}
if ($where[$i]['expr_type'] == 'bracket_expression') {
$sub_tree = $where[$i]['sub_tree'];
$sub_group = $this->parseWhere($sub_tree);
$groups = array_merge($groups, $sub_group);
}
$i++;
}
return $groups;
}
}
