custom/plugins/CrswCleverReachOfficial/src/Entity/Base/Repositories/BaseRepository.php line 86

Open in your IDE?
  1. <?php
  2. namespace Crsw\CleverReachOfficial\Entity\Base\Repositories;
  3. use Crsw\CleverReachOfficial\Core\Infrastructure\Logger\Logger;
  4. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\Entity;
  5. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\Exceptions\QueryFilterInvalidParamException;
  6. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\Interfaces\RepositoryInterface;
  7. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\QueryFilter\Operators;
  8. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\QueryFilter\QueryCondition;
  9. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\QueryFilter\QueryFilter;
  10. use Crsw\CleverReachOfficial\Core\Infrastructure\ORM\Utility\IndexHelper;
  11. use Crsw\CleverReachOfficial\Core\Infrastructure\ServiceRegister;
  12. use Crsw\CleverReachOfficial\Entity\Base\BaseEntity;
  13. use Doctrine\DBAL\Connection;
  14. use Doctrine\DBAL\DBALException;
  15. use Doctrine\DBAL\Query\QueryBuilder;
  16. use JsonException;
  17. use Shopware\Core\Framework\Context;
  18. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  21. /**
  22.  * Class BaseRepository
  23.  * @package Crsw\CleverReachOfficial\Entity\Base\Repositories
  24.  */
  25. class BaseRepository implements RepositoryInterface
  26. {
  27.     /**
  28.      * @var string
  29.      */
  30.     protected static $doctrineModel 'cleverreach_entity';
  31.     /**
  32.      * @var string
  33.      */
  34.     protected $entityClass;
  35.     /**
  36.      * Returns full class name.
  37.      *
  38.      * @return string Full class name.
  39.      */
  40.     public static function getClassName(): string
  41.     {
  42.         return static::class;
  43.     }
  44.     /**
  45.      * Sets repository entity.
  46.      *
  47.      * @param string $entityClass Repository entity class.
  48.      */
  49.     public function setEntityClass($entityClass): void
  50.     {
  51.         $this->entityClass $entityClass;
  52.     }
  53.     /**
  54.      * Executes select query.
  55.      *
  56.      * @param QueryFilter|null $filter Filter for query.
  57.      *
  58.      * @return Entity[] A list of found entities ot empty array.
  59.      *
  60.      * @throws QueryFilterInvalidParamException
  61.      * @throws JsonException
  62.      */
  63.     public function select(QueryFilter $filter null): array
  64.     {
  65.         $query $this->getBaseDoctrineQuery($filter);
  66.         return $this->getResult($query);
  67.     }
  68.     /**
  69.      * Executes select query and returns first result.
  70.      *
  71.      * @param QueryFilter|null $filter Filter for query.
  72.      *
  73.      * @return Entity|null First found entity or NULL.
  74.      *
  75.      * @throws QueryFilterInvalidParamException
  76.      * @throws JsonException
  77.      */
  78.     public function selectOne(QueryFilter $filter null): ?Entity
  79.     {
  80.         $query $this->getBaseDoctrineQuery($filter);
  81.         $query->setMaxResults(1);
  82.         $result $this->getResult($query);
  83.         return !empty($result[0]) ? $result[0] : null;
  84.     }
  85.     /**
  86.      * Executes insert query and returns ID of created entity. Entity will be updated with new ID.
  87.      *
  88.      * @param Entity $entity Entity to be saved.
  89.      *
  90.      * @return int|string|null Identifier of saved entity.
  91.      */
  92.     public function save(Entity $entity)
  93.     {
  94.         $data $this->transformEntityToArray($entity);
  95.         if ($entity->getId()) {
  96.             /** @var BaseEntity $baseEntity */
  97.             $baseEntity $this->getEntityRepository()->search(
  98.                 (new Criteria())->addFilter(new EqualsFilter('id'$entity->getId())),
  99.                 Context::createDefaultContext()
  100.             )->first();
  101.             if ($baseEntity) {
  102.                 $data['shopware_id'] = $baseEntity->getShopware_id();
  103.                 $this->getEntityRepository()->update([$data], Context::createDefaultContext());
  104.                 return $baseEntity->getId();
  105.             }
  106.         }
  107.         $event $this->getEntityRepository()->create([$data], Context::createDefaultContext())
  108.             ->getEventByEntityName(static::$doctrineModel);
  109.         if (!$event) {
  110.             return null;
  111.         }
  112.         $baseEntity $this->getEntityRepository()->search(
  113.             new Criteria([$event->getWriteResults()[0]->getPrimaryKey()]),
  114.             Context::createDefaultContext()
  115.         )->first();
  116.         $entity->setId($baseEntity->getId());
  117.         return $entity->getId();
  118.     }
  119.     /**
  120.      * Executes update query and returns success flag.
  121.      *
  122.      * @param Entity $entity Entity to be updated.
  123.      *
  124.      * @return bool TRUE if operation succeeded; otherwise, FALSE.
  125.      */
  126.     public function update(Entity $entity): bool
  127.     {
  128.         $result true;
  129.         /** @var BaseEntity $doctrineEntity */
  130.         $doctrineEntity $this->getEntityRepository()
  131.             ->search(
  132.                 (new Criteria())->addFilter(new EqualsFilter('id'$entity->getId())),
  133.                 Context::createDefaultContext()
  134.             )->first();
  135.         if ($doctrineEntity) {
  136.             $data $this->transformEntityToArray($entity);
  137.             $data['shopware_id'] = $doctrineEntity->getShopware_id();
  138.             $this->getEntityRepository()->update([$data], Context::createDefaultContext());
  139.         } else {
  140.             $result false;
  141.         }
  142.         return $result;
  143.     }
  144.     /**
  145.      * Executes delete query and returns success flag.
  146.      *
  147.      * @param Entity $entity Entity to be deleted.
  148.      *
  149.      * @return bool TRUE if operation succeeded; otherwise, FALSE.
  150.      */
  151.     public function delete(Entity $entity): bool
  152.     {
  153.         $sql "DELETE FROM cleverreach_entity WHERE id=:id";
  154.         try {
  155.             $this->getConnection()->executeUpdate($sql, ['id' => $entity->getId()]);
  156.             return true;
  157.         } catch (DBALException $e) {
  158.             Logger::logError($e->getMessage());
  159.             return false;
  160.         }
  161.     }
  162.     /**
  163.      * Counts records that match filter criteria.
  164.      *
  165.      * @param QueryFilter|null $filter Filter for query.
  166.      *
  167.      * @return int Number of records that match filter criteria.
  168.      * @throws QueryFilterInvalidParamException
  169.      */
  170.     public function count(QueryFilter $filter null): int
  171.     {
  172.         $query $this->getBaseDoctrineQuery($filtertrue);
  173.         $result $query->execute()->fetch();
  174.         return (int)$result['num'];
  175.     }
  176.     /**
  177.      * @param QueryFilter|null $filter
  178.      * @param bool $isCount
  179.      *
  180.      * @return QueryBuilder
  181.      * @throws QueryFilterInvalidParamException
  182.      */
  183.     protected function getBaseDoctrineQuery(QueryFilter $filter null$isCount false): QueryBuilder
  184.     {
  185.         /** @var Entity $entity */
  186.         $entity = new $this->entityClass;
  187.         $type $entity->getConfig()->getType();
  188.         $indexMap IndexHelper::mapFieldsToIndexes($entity);
  189.         $query $this->getConnection()->createQueryBuilder();
  190.         $alias 'p';
  191.         $baseSelect $isCount "count($alias.id) as num" $alias '.*';
  192.         $query->select($baseSelect)
  193.             ->from(static::$doctrineModel$alias)
  194.             ->where("$alias.type = '$type'");
  195.         $groups $filter $this->buildConditionGroups($filter$indexMap) : [];
  196.         $queryParts $this->getQueryParts($groups$indexMap$alias);
  197.         $where $this->generateWhereStatement($queryParts);
  198.         if (!empty($where)) {
  199.             $query->andWhere($where);
  200.         }
  201.         if ($filter) {
  202.             $this->setLimit($filter$query);
  203.             $this->setOffset($filter$query);
  204.             $this->setOrderBy($filter$indexMap$alias$query);
  205.         }
  206.         return $query;
  207.     }
  208.     /**
  209.      * Builds condition groups (each group is chained with OR internally, and with AND externally) based on query
  210.      * filter.
  211.      *
  212.      * @param QueryFilter $filter Query filter object.
  213.      * @param array $fieldIndexMap Map of property indexes.
  214.      *
  215.      * @return array Array of condition groups..
  216.      *
  217.      * @throws QueryFilterInvalidParamException
  218.      */
  219.     protected function buildConditionGroups(QueryFilter $filter, array $fieldIndexMap): array
  220.     {
  221.         $groups = [];
  222.         $counter 0;
  223.         $fieldIndexMap['id'] = 0;
  224.         foreach ($filter->getConditions() as $condition) {
  225.             if (!empty($groups[$counter]) && $condition->getChainOperator() === 'OR') {
  226.                 $counter++;
  227.             }
  228.             // Only index columns can be filtered.
  229.             if (!array_key_exists($condition->getColumn(), $fieldIndexMap)) {
  230.                 throw new QueryFilterInvalidParamException("Field [{$condition->getColumn()}] is not indexed.");
  231.             }
  232.             $groups[$counter][] = $condition;
  233.         }
  234.         return $groups;
  235.     }
  236.     /**
  237.      * Retrieves group query parts.
  238.      *
  239.      * @param array $conditionGroups
  240.      * @param array $indexMap
  241.      * @param string $alias
  242.      *
  243.      * @return array
  244.      */
  245.     protected function getQueryParts(array $conditionGroups, array $indexMapstring $alias): array
  246.     {
  247.         $parts = [];
  248.         foreach ($conditionGroups as $group) {
  249.             $subPart = [];
  250.             foreach ($group as $condition) {
  251.                 if ($condition->getValue() === '') {
  252.                     continue;
  253.                 }
  254.                 $subPart[] = $this->getQueryPart($condition$indexMap$alias);
  255.             }
  256.             if (!empty($subPart)) {
  257.                 $parts[] = $subPart;
  258.             }
  259.         }
  260.         return $parts;
  261.     }
  262.     /**
  263.      * Retrieves query part.
  264.      *
  265.      * @param QueryCondition $condition
  266.      * @param array $indexMap
  267.      * @param string $alias
  268.      *
  269.      * @return string
  270.      */
  271.     protected function getQueryPart(QueryCondition $condition, array $indexMapstring $alias): string
  272.     {
  273.         $column $condition->getColumn();
  274.         if ($column === 'id') {
  275.             return "$alias.id=" $condition->getValue();
  276.         }
  277.         $part "$alias.index_" $indexMap[$column] . ' ' $condition->getOperator();
  278.         if (!in_array($condition->getOperator(), array(Operators::NULLOperators::NOT_NULL), true)) {
  279.             if (in_array($condition->getOperator(), array(Operators::NOT_INOperators::IN), true)) {
  280.                 $part .= $this->getInOperatorValues($condition);
  281.             } else {
  282.                 $part .= " '" IndexHelper::castFieldValue($condition->getValue(), $condition->getValueType()) . "'";
  283.             }
  284.         }
  285.         return $part;
  286.     }
  287.     /**
  288.      * Handles values for the IN and NOT IN operators.
  289.      *
  290.      * @param QueryCondition $condition
  291.      *
  292.      * @return string
  293.      */
  294.     protected function getInOperatorValues(QueryCondition $condition): string
  295.     {
  296.         $values array_map(
  297.             function ($item) {
  298.                 if (is_string($item)) {
  299.                     return "'$item'";
  300.                 }
  301.                 return "'" IndexHelper::castFieldValue($itemis_int($item) ? 'integer' 'double') . "'";
  302.             },
  303.             $condition->getValue()
  304.         );
  305.         return '(' implode(','$values) . ')';
  306.     }
  307.     /**
  308.      * Generates where statement.
  309.      *
  310.      * @param array $queryParts
  311.      *
  312.      * @return string
  313.      */
  314.     protected function generateWhereStatement(array $queryParts): string
  315.     {
  316.         $where '';
  317.         foreach ($queryParts as $index => $part) {
  318.             $subWhere '';
  319.             if ($index 0) {
  320.                 $subWhere .= ' OR ';
  321.             }
  322.             $subWhere .= $part[0];
  323.             $count count($part);
  324.             for ($i 1$i $count$i++) {
  325.                 $subWhere .= ' AND ' $part[$i];
  326.             }
  327.             $where .= $subWhere;
  328.         }
  329.         return $where;
  330.     }
  331.     /**
  332.      * Sets limit.
  333.      *
  334.      * @param QueryFilter $filter
  335.      * @param $query
  336.      */
  337.     protected function setLimit(QueryFilter $filterQueryBuilder $query): void
  338.     {
  339.         if ($filter->getLimit()) {
  340.             $query->setMaxResults($filter->getLimit());
  341.         }
  342.     }
  343.     /**
  344.      * @param QueryFilter $filter
  345.      * @param QueryBuilder $query
  346.      */
  347.     protected function setOffset(QueryFilter $filterQueryBuilder $query): void
  348.     {
  349.         if ($filter->getOffset()) {
  350.             $query->setFirstResult($filter->getOffset());
  351.         }
  352.     }
  353.     /**
  354.      * Sets order by.
  355.      *
  356.      * @param QueryFilter $filter
  357.      * @param array $indexMap
  358.      * @param $alias
  359.      * @param QueryBuilder $query
  360.      */
  361.     protected function setOrderBy(QueryFilter $filter, array $indexMap$aliasQueryBuilder $query): void
  362.     {
  363.         if ($filter->getOrderByColumn()) {
  364.             $orderByColumn $filter->getOrderByColumn();
  365.             if ($orderByColumn === 'id' || !empty($indexMap[$orderByColumn])) {
  366.                 $columnName $orderByColumn === 'id'
  367.                     "$alias.id" "$alias.index_" $indexMap[$orderByColumn];
  368.                 $query->orderBy($columnName$filter->getOrderDirection());
  369.             }
  370.         }
  371.     }
  372.     /**
  373.      * Retrieves query result.
  374.      *
  375.      * @param $builder
  376.      *
  377.      * @return Entity[]
  378.      *
  379.      */
  380.     protected function getResult(QueryBuilder $builder): array
  381.     {
  382.         $doctrineEntities $builder->execute()->fetchAll();
  383.         $result = [];
  384.         foreach ($doctrineEntities as $doctrineEntity) {
  385.             if (!$doctrineEntity) {
  386.                 continue;
  387.             }
  388.             $entity $this->unserializeEntity($doctrineEntity['data']);
  389.             if ($entity) {
  390.                 $entity->setId($doctrineEntity['id']);
  391.                 $result[] = $entity;
  392.             }
  393.         }
  394.         return $result;
  395.     }
  396.     /**
  397.      * Unserializes ORM entity.
  398.      *
  399.      * @param string $data
  400.      *
  401.      * @return Entity
  402.      */
  403.     protected function unserializeEntity($data): Entity
  404.     {
  405.         $jsonEntity json_decode($datatrue);
  406.         if (array_key_exists('class_name'$jsonEntity)) {
  407.             $entity = new $jsonEntity['class_name'];
  408.         } else {
  409.             $entity = new $this->entityClass;
  410.         }
  411.         /** @var Entity $entity */
  412.         $entity->inflate($jsonEntity);
  413.         return $entity;
  414.     }
  415.     /**
  416.      * Transforms entity to array.
  417.      *
  418.      * @param Entity $entity
  419.      *
  420.      * @return array
  421.      */
  422.     protected function transformEntityToArray($entity): array
  423.     {
  424.         $data['type'] = $entity->getConfig()->getType();
  425.         $values IndexHelper::transformFieldsToIndexes($entity);
  426.         foreach ($values as $key => $value) {
  427.             $data["index_{$key}"] = $value;
  428.         }
  429.         $entityValues $entity->toArray();
  430.         if ($data['type'] === 'Form') {
  431.             $entityValues['content'] = htmlentities($entityValues['content']);
  432.         }
  433.         $data['data'] = json_encode($entityValues);
  434.         return $data;
  435.     }
  436.     /**
  437.      * @return Connection
  438.      */
  439.     protected function getConnection(): Connection
  440.     {
  441.         /** @noinspection PhpIncompatibleReturnTypeInspection */
  442.         return ServiceRegister::getService(Connection::class);
  443.     }
  444.     /**
  445.      * @return EntityRepositoryInterface
  446.      */
  447.     protected function getEntityRepository(): EntityRepositoryInterface
  448.     {
  449.         /** @noinspection PhpIncompatibleReturnTypeInspection */
  450.         return ServiceRegister::getService(EntityRepositoryInterface::class);
  451.     }
  452. }