QueryBase.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. <?php
  2. namespace Drupal\Core\Entity\Query;
  3. use Drupal\Core\Database\Query\PagerSelectExtender;
  4. use Drupal\Core\Entity\EntityTypeInterface;
  5. /**
  6. * The base entity query class.
  7. */
  8. abstract class QueryBase implements QueryInterface {
  9. /**
  10. * The entity type this query runs against.
  11. *
  12. * @var string
  13. */
  14. protected $entityTypeId;
  15. /**
  16. * Information about the entity type.
  17. *
  18. * @var \Drupal\Core\Entity\EntityTypeInterface
  19. */
  20. protected $entityType;
  21. /**
  22. * The list of sorts.
  23. *
  24. * @var array
  25. */
  26. protected $sort = [];
  27. /**
  28. * TRUE if this is a count query, FALSE if it isn't.
  29. *
  30. * @var bool
  31. */
  32. protected $count = FALSE;
  33. /**
  34. * Conditions.
  35. *
  36. * @var \Drupal\Core\Entity\Query\ConditionInterface
  37. */
  38. protected $condition;
  39. /**
  40. * The list of aggregate expressions.
  41. *
  42. * @var array
  43. */
  44. protected $aggregate = [];
  45. /**
  46. * The list of columns to group on.
  47. *
  48. * @var array
  49. */
  50. protected $groupBy = [];
  51. /**
  52. * Aggregate Conditions
  53. *
  54. * @var \Drupal\Core\Entity\Query\ConditionAggregateInterface
  55. */
  56. protected $conditionAggregate;
  57. /**
  58. * The list of sorts over the aggregate results.
  59. *
  60. * @var array
  61. */
  62. protected $sortAggregate = [];
  63. /**
  64. * The query range.
  65. *
  66. * @var array
  67. */
  68. protected $range = [];
  69. /**
  70. * The query metadata for alter purposes.
  71. *
  72. * @var array
  73. */
  74. protected $alterMetaData;
  75. /**
  76. * The query tags.
  77. *
  78. * @var array
  79. */
  80. protected $alterTags;
  81. /**
  82. * Whether access check is requested or not. Defaults to TRUE.
  83. *
  84. * @var bool
  85. */
  86. protected $accessCheck = TRUE;
  87. /**
  88. * Flag indicating whether to query the current revision or all revisions.
  89. *
  90. * @var bool
  91. */
  92. protected $allRevisions = FALSE;
  93. /**
  94. * Flag indicating whether to query the latest revision.
  95. *
  96. * @var bool
  97. */
  98. protected $latestRevision = FALSE;
  99. /**
  100. * The query pager data.
  101. *
  102. * @var array
  103. *
  104. * @see Query::pager()
  105. */
  106. protected $pager = [];
  107. /**
  108. * List of potential namespaces of the classes belonging to this query.
  109. *
  110. * @var array
  111. */
  112. protected $namespaces = [];
  113. /**
  114. * Constructs this object.
  115. *
  116. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
  117. * The entity type definition.
  118. * @param string $conjunction
  119. * - AND: all of the conditions on the query need to match.
  120. * - OR: at least one of the conditions on the query need to match.
  121. * @param array $namespaces
  122. * List of potential namespaces of the classes belonging to this query.
  123. */
  124. public function __construct(EntityTypeInterface $entity_type, $conjunction, array $namespaces) {
  125. $this->entityTypeId = $entity_type->id();
  126. $this->entityType = $entity_type;
  127. $this->conjunction = $conjunction;
  128. $this->namespaces = $namespaces;
  129. $this->condition = $this->conditionGroupFactory($conjunction);
  130. if ($this instanceof QueryAggregateInterface) {
  131. $this->conditionAggregate = $this->conditionAggregateGroupFactory($conjunction);
  132. }
  133. }
  134. /**
  135. * {@inheritdoc}
  136. */
  137. public function getEntityTypeId() {
  138. return $this->entityTypeId;
  139. }
  140. /**
  141. * {@inheritdoc}
  142. */
  143. public function condition($property, $value = NULL, $operator = NULL, $langcode = NULL) {
  144. $this->condition->condition($property, $value, $operator, $langcode);
  145. return $this;
  146. }
  147. /**
  148. * {@inheritdoc}
  149. */
  150. public function exists($property, $langcode = NULL) {
  151. $this->condition->exists($property, $langcode);
  152. return $this;
  153. }
  154. /**
  155. * {@inheritdoc}
  156. */
  157. public function notExists($property, $langcode = NULL) {
  158. $this->condition->notExists($property, $langcode);
  159. return $this;
  160. }
  161. /**
  162. * {@inheritdoc}
  163. */
  164. public function range($start = NULL, $length = NULL) {
  165. $this->range = [
  166. 'start' => $start,
  167. 'length' => $length,
  168. ];
  169. return $this;
  170. }
  171. /**
  172. * Creates an object holding a group of conditions.
  173. *
  174. * See andConditionGroup() and orConditionGroup() for more.
  175. *
  176. * @param string $conjunction
  177. * - AND (default): this is the equivalent of andConditionGroup().
  178. * - OR: this is the equivalent of orConditionGroup().
  179. *
  180. * @return \Drupal\Core\Entity\Query\ConditionInterface
  181. * An object holding a group of conditions.
  182. */
  183. protected function conditionGroupFactory($conjunction = 'AND') {
  184. $class = static::getClass($this->namespaces, 'Condition');
  185. return new $class($conjunction, $this, $this->namespaces);
  186. }
  187. /**
  188. * {@inheritdoc}
  189. */
  190. public function andConditionGroup() {
  191. return $this->conditionGroupFactory('and');
  192. }
  193. /**
  194. * {@inheritdoc}
  195. */
  196. public function orConditionGroup() {
  197. return $this->conditionGroupFactory('or');
  198. }
  199. /**
  200. * {@inheritdoc}
  201. */
  202. public function sort($field, $direction = 'ASC', $langcode = NULL) {
  203. $this->sort[] = [
  204. 'field' => $field,
  205. 'direction' => strtoupper($direction),
  206. 'langcode' => $langcode,
  207. ];
  208. return $this;
  209. }
  210. /**
  211. * {@inheritdoc}
  212. */
  213. public function count() {
  214. $this->count = TRUE;
  215. return $this;
  216. }
  217. /**
  218. * {@inheritdoc}
  219. */
  220. public function accessCheck($access_check = TRUE) {
  221. $this->accessCheck = $access_check;
  222. return $this;
  223. }
  224. /**
  225. * {@inheritdoc}
  226. */
  227. public function currentRevision() {
  228. $this->allRevisions = FALSE;
  229. $this->latestRevision = FALSE;
  230. return $this;
  231. }
  232. /**
  233. * {@inheritdoc}
  234. */
  235. public function latestRevision() {
  236. $this->allRevisions = TRUE;
  237. $this->latestRevision = TRUE;
  238. return $this;
  239. }
  240. /**
  241. * {@inheritdoc}
  242. */
  243. public function allRevisions() {
  244. $this->allRevisions = TRUE;
  245. $this->latestRevision = FALSE;
  246. return $this;
  247. }
  248. /**
  249. * {@inheritdoc}
  250. */
  251. public function pager($limit = 10, $element = NULL) {
  252. // Even when not using SQL, storing the element PagerSelectExtender is as
  253. // good as anywhere else.
  254. if (!isset($element)) {
  255. $element = PagerSelectExtender::$maxElement++;
  256. }
  257. elseif ($element >= PagerSelectExtender::$maxElement) {
  258. PagerSelectExtender::$maxElement = $element + 1;
  259. }
  260. $this->pager = [
  261. 'limit' => $limit,
  262. 'element' => $element,
  263. ];
  264. return $this;
  265. }
  266. /**
  267. * Gets the total number of results and initialize a pager for the query.
  268. *
  269. * The pager can be disabled by either setting the pager limit to 0, or by
  270. * setting this query to be a count query.
  271. */
  272. protected function initializePager() {
  273. if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
  274. $page = pager_find_page($this->pager['element']);
  275. $count_query = clone $this;
  276. $this->pager['total'] = $count_query->count()->execute();
  277. $this->pager['start'] = $page * $this->pager['limit'];
  278. pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
  279. $this->range($this->pager['start'], $this->pager['limit']);
  280. }
  281. }
  282. /**
  283. * {@inheritdoc}
  284. */
  285. public function tableSort(&$headers) {
  286. // If 'field' is not initialized, the header columns aren't clickable.
  287. foreach ($headers as $key => $header) {
  288. if (is_array($header) && isset($header['specifier'])) {
  289. $headers[$key]['field'] = '';
  290. }
  291. }
  292. $order = tablesort_get_order($headers);
  293. $direction = tablesort_get_sort($headers);
  294. foreach ($headers as $header) {
  295. if (is_array($header) && ($header['data'] == $order['name'])) {
  296. $this->sort($header['specifier'], $direction, isset($header['langcode']) ? $header['langcode'] : NULL);
  297. }
  298. }
  299. return $this;
  300. }
  301. /**
  302. * Makes sure that the Condition object is cloned as well.
  303. */
  304. public function __clone() {
  305. $this->condition = clone $this->condition;
  306. }
  307. /**
  308. * {@inheritdoc}
  309. */
  310. public function addTag($tag) {
  311. $this->alterTags[$tag] = 1;
  312. return $this;
  313. }
  314. /**
  315. * {@inheritdoc}
  316. */
  317. public function hasTag($tag) {
  318. return isset($this->alterTags[$tag]);
  319. }
  320. /**
  321. * {@inheritdoc}
  322. */
  323. public function hasAllTags() {
  324. return !(boolean) array_diff(func_get_args(), array_keys($this->alterTags));
  325. }
  326. /**
  327. * {@inheritdoc}
  328. */
  329. public function hasAnyTag() {
  330. return (boolean) array_intersect(func_get_args(), array_keys($this->alterTags));
  331. }
  332. /**
  333. * {@inheritdoc}
  334. */
  335. public function addMetaData($key, $object) {
  336. $this->alterMetaData[$key] = $object;
  337. return $this;
  338. }
  339. /**
  340. * {@inheritdoc}
  341. */
  342. public function getMetaData($key) {
  343. return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
  344. }
  345. /**
  346. * {@inheritdoc}
  347. */
  348. public function aggregate($field, $function, $langcode = NULL, &$alias = NULL) {
  349. if (!isset($alias)) {
  350. $alias = $this->getAggregationAlias($field, $function);
  351. }
  352. $this->aggregate[$alias] = [
  353. 'field' => $field,
  354. 'function' => $function,
  355. 'alias' => $alias,
  356. 'langcode' => $langcode,
  357. ];
  358. return $this;
  359. }
  360. /**
  361. * {@inheritdoc}
  362. */
  363. public function conditionAggregate($field, $function = NULL, $value = NULL, $operator = '=', $langcode = NULL) {
  364. $this->aggregate($field, $function, $langcode);
  365. $this->conditionAggregate->condition($field, $function, $value, $operator, $langcode);
  366. return $this;
  367. }
  368. /**
  369. * {@inheritdoc}
  370. */
  371. public function sortAggregate($field, $function, $direction = 'ASC', $langcode = NULL) {
  372. $alias = $this->getAggregationAlias($field, $function);
  373. $this->sortAggregate[$alias] = [
  374. 'field' => $field,
  375. 'function' => $function,
  376. 'direction' => $direction,
  377. 'langcode' => $langcode,
  378. ];
  379. $this->aggregate($field, $function, $langcode, $alias);
  380. return $this;
  381. }
  382. /**
  383. * {@inheritdoc}
  384. */
  385. public function groupBy($field, $langcode = NULL) {
  386. $this->groupBy[] = [
  387. 'field' => $field,
  388. 'langcode' => $langcode,
  389. ];
  390. return $this;
  391. }
  392. /**
  393. * Generates an alias for a field and its aggregated function.
  394. *
  395. * @param string $field
  396. * The field name used in the alias.
  397. * @param string $function
  398. * The aggregation function used in the alias.
  399. *
  400. * @return string
  401. * The alias for the field.
  402. */
  403. protected function getAggregationAlias($field, $function) {
  404. return strtolower($field . '_' . $function);
  405. }
  406. /**
  407. * Gets a list of namespaces of the ancestors of a class.
  408. *
  409. * @param $object
  410. * An object within a namespace.
  411. *
  412. * @return array
  413. * A list containing the namespace of the class, the namespace of the
  414. * parent of the class and so on and so on.
  415. */
  416. public static function getNamespaces($object) {
  417. $namespaces = [];
  418. for ($class = get_class($object); $class; $class = get_parent_class($class)) {
  419. $namespaces[] = substr($class, 0, strrpos($class, '\\'));
  420. }
  421. return $namespaces;
  422. }
  423. /**
  424. * Finds a class in a list of namespaces.
  425. *
  426. * @param array $namespaces
  427. * A list of namespaces.
  428. * @param string $short_class_name
  429. * A class name without namespace.
  430. *
  431. * @return string
  432. * The fully qualified name of the class.
  433. */
  434. public static function getClass(array $namespaces, $short_class_name) {
  435. foreach ($namespaces as $namespace) {
  436. $class = $namespace . '\\' . $short_class_name;
  437. if (class_exists($class)) {
  438. return $class;
  439. }
  440. }
  441. }
  442. }