PageRenderTime 28ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/core/lib/Drupal/Core/Entity/Query/QueryBase.php

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