選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

EntityRepository.php 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. namespace Lc\SovBundle\Repository;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Doctrine\ORM\QueryBuilder;
  5. use Doctrine\Persistence\ManagerRegistry;
  6. use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
  7. use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
  8. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Orm\EntityRepositoryInterface;
  9. use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
  10. use EasyCorp\Bundle\EasyAdminBundle\Dto\FilterDataDto;
  11. use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
  12. use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
  13. use EasyCorp\Bundle\EasyAdminBundle\Factory\FormFactory;
  14. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\ComparisonType;
  15. use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
  16. use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityRepository as EaEntityRepository;
  17. use Knp\Component\Pager\PaginatorInterface;
  18. use function Symfony\Component\Translation\t;
  19. class EntityRepository
  20. {
  21. //protected EaEntityRepository $parent;
  22. protected EntityManagerInterface $entityManager;
  23. protected PaginatorInterface $paginator;
  24. public function __construct(
  25. //EaEntityRepository $entityRepository,
  26. EntityManagerInterface $entityManager,
  27. PaginatorInterface $paginator
  28. ) {
  29. //$this->parent = $entityRepository;
  30. $this->entityManager = $entityManager;
  31. $this->paginator = $paginator;
  32. }
  33. public function createRepositoryQuery(RepositoryQueryInterface $repositoryQuery,SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters)
  34. {
  35. // if (!empty($searchDto->getQuery())) {
  36. // $this->addSearchClause($repositoryQuery->getQuery(), $searchDto, $entityDto);
  37. // }
  38. //
  39. // if (!empty($searchDto->getAppliedFilters())) {
  40. // $this->addFilterClause($repositoryQuery->getQuery(), $searchDto, $entityDto, $filters, $fields);
  41. // }
  42. $this->addOrderClause($repositoryQuery, $searchDto, $entityDto);
  43. return $repositoryQuery;
  44. }
  45. private function addSearchClause(QueryBuilder $queryBuilder, SearchDto $searchDto, EntityDto $entityDto): void
  46. {
  47. $query = $searchDto->getQuery();
  48. $lowercaseQuery = mb_strtolower($query);
  49. $isNumericQuery = is_numeric($query);
  50. $isSmallIntegerQuery = ctype_digit($query) && $query >= -32768 && $query <= 32767;
  51. $isIntegerQuery = ctype_digit($query) && $query >= -2147483648 && $query <= 2147483647;
  52. $isUuidQuery = 1 === preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $query);
  53. $dqlParameters = [
  54. // adding '0' turns the string into a numeric value
  55. 'numeric_query' => is_numeric($query) ? 0 + $query : $query,
  56. 'uuid_query' => $query,
  57. 'text_query' => '%'.$lowercaseQuery.'%',
  58. 'words_query' => explode(' ', $lowercaseQuery),
  59. ];
  60. $entitiesAlreadyJoined = [];
  61. $configuredSearchableProperties = $searchDto->getSearchableProperties();
  62. $searchableProperties = empty($configuredSearchableProperties) ? $entityDto->getAllPropertyNames() : $configuredSearchableProperties;
  63. foreach ($searchableProperties as $propertyName) {
  64. if ($entityDto->isAssociation($propertyName)) {
  65. // support arbitrarily nested associations (e.g. foo.bar.baz.qux)
  66. $associatedProperties = explode('.', $propertyName);
  67. $numAssociatedProperties = \count($associatedProperties);
  68. if (1 === $numAssociatedProperties) {
  69. throw new \InvalidArgumentException(sprintf('The "%s" property included in the setSearchFields() method is not a valid search field. When using associated properties in search, you must also define the exact field used in the search (e.g. \'%s.id\', \'%s.name\', etc.)', $propertyName, $propertyName, $propertyName));
  70. }
  71. $originalPropertyName = $associatedProperties[0];
  72. $originalPropertyMetadata = $entityDto->getPropertyMetadata($originalPropertyName);
  73. $associatedEntityDto = $this->entityFactory->create($originalPropertyMetadata->get('targetEntity'));
  74. for ($i = 0; $i < $numAssociatedProperties - 1; ++$i) {
  75. $associatedEntityName = $associatedProperties[$i];
  76. $associatedPropertyName = $associatedProperties[$i + 1];
  77. if (!\in_array($associatedEntityName, $entitiesAlreadyJoined, true)) {
  78. $parentEntityName = 0 === $i ? 'entity' : $associatedProperties[$i - 1];
  79. $queryBuilder->leftJoin($parentEntityName.'.'.$associatedEntityName, $associatedEntityName);
  80. $entitiesAlreadyJoined[] = $associatedEntityName;
  81. }
  82. if ($i < $numAssociatedProperties - 2) {
  83. $propertyMetadata = $associatedEntityDto->getPropertyMetadata($associatedPropertyName);
  84. $targetEntity = $propertyMetadata->get('targetEntity');
  85. $associatedEntityDto = $this->entityFactory->create($targetEntity);
  86. }
  87. }
  88. $entityName = $associatedEntityName;
  89. $propertyName = $associatedPropertyName;
  90. $propertyDataType = $associatedEntityDto->getPropertyDataType($propertyName);
  91. } else {
  92. $entityName = 'entity';
  93. $propertyDataType = $entityDto->getPropertyDataType($propertyName);
  94. }
  95. $isSmallIntegerProperty = 'smallint' === $propertyDataType;
  96. $isIntegerProperty = 'integer' === $propertyDataType;
  97. $isNumericProperty = \in_array($propertyDataType, ['number', 'bigint', 'decimal', 'float']);
  98. // 'citext' is a PostgreSQL extension (https://github.com/EasyCorp/EasyAdminBundle/issues/2556)
  99. $isTextProperty = \in_array($propertyDataType, ['string', 'text', 'citext', 'array', 'simple_array']);
  100. $isGuidProperty = \in_array($propertyDataType, ['guid', 'uuid']);
  101. // this complex condition is needed to avoid issues on PostgreSQL databases
  102. if (
  103. ($isSmallIntegerProperty && $isSmallIntegerQuery) ||
  104. ($isIntegerProperty && $isIntegerQuery) ||
  105. ($isNumericProperty && $isNumericQuery)
  106. ) {
  107. $queryBuilder->orWhere(sprintf('%s.%s = :query_for_numbers', $entityName, $propertyName))
  108. ->setParameter('query_for_numbers', $dqlParameters['numeric_query']);
  109. } elseif ($isGuidProperty && $isUuidQuery) {
  110. $queryBuilder->orWhere(sprintf('%s.%s = :query_for_uuids', $entityName, $propertyName))
  111. ->setParameter('query_for_uuids', $dqlParameters['uuid_query']);
  112. } elseif ($isTextProperty) {
  113. $queryBuilder->orWhere(sprintf('LOWER(%s.%s) LIKE :query_for_text', $entityName, $propertyName))
  114. ->setParameter('query_for_text', $dqlParameters['text_query']);
  115. $queryBuilder->orWhere(sprintf('LOWER(%s.%s) IN (:query_as_words)', $entityName, $propertyName))
  116. ->setParameter('query_as_words', $dqlParameters['words_query']);
  117. }
  118. }
  119. }
  120. private function addOrderClause(RepositoryQueryInterface $repositoryQuery, SearchDto $searchDto, EntityDto $entityDto): void
  121. {
  122. foreach ($searchDto->getSort() as $sortProperty => $sortOrder) {
  123. $repositoryQuery->addOrderBy('.'.$sortProperty, $sortOrder);
  124. // $aliases = $queryBuilder->getAllAliases();
  125. // $sortFieldIsDoctrineAssociation = $entityDto->isAssociation($sortProperty);
  126. //
  127. // if ($sortFieldIsDoctrineAssociation) {
  128. // $sortFieldParts = explode('.', $sortProperty, 2);
  129. // // check if join has been added once before.
  130. // if (!\in_array($sortFieldParts[0], $aliases)) {
  131. // $queryBuilder->leftJoin('entity.'.$sortFieldParts[0], $sortFieldParts[0]);
  132. // }
  133. //
  134. // if (1 === \count($sortFieldParts)) {
  135. // $queryBuilder->addOrderBy('entity.'.$sortProperty, $sortOrder);
  136. // } else {
  137. // $queryBuilder->addOrderBy($sortProperty, $sortOrder);
  138. // }
  139. // } else {
  140. // $queryBuilder->addOrderBy('entity.'.$sortProperty, $sortOrder);
  141. // }
  142. }
  143. }
  144. private function addFilterClause(QueryBuilder $queryBuilder, SearchDto $searchDto, EntityDto $entityDto, FilterCollection $configuredFilters, FieldCollection $fields): void
  145. {
  146. $filtersForm = $this->formFactory->createFiltersForm($configuredFilters, $this->adminContextProvider->getContext()->getRequest());
  147. if (!$filtersForm->isSubmitted()) {
  148. return;
  149. }
  150. $appliedFilters = $searchDto->getAppliedFilters();
  151. $i = 0;
  152. foreach ($filtersForm as $filterForm) {
  153. $propertyName = $filterForm->getName();
  154. $filter = $configuredFilters->get($propertyName);
  155. // this filter is not defined or not applied
  156. if (null === $filter || !isset($appliedFilters[$propertyName])) {
  157. continue;
  158. }
  159. // if the form filter is not valid then we should not apply the filter
  160. if (!$filterForm->isValid()) {
  161. continue;
  162. }
  163. $submittedData = $filterForm->getData();
  164. if (!\is_array($submittedData)) {
  165. $submittedData = [
  166. 'comparison' => ComparisonType::EQ,
  167. 'value' => $submittedData,
  168. ];
  169. }
  170. $filterDataDto = FilterDataDto::new($i, $filter, current($queryBuilder->getRootAliases()), $submittedData);
  171. $filter->apply($queryBuilder, $filterDataDto, $fields->getByProperty($propertyName), $entityDto);
  172. ++$i;
  173. }
  174. }
  175. }