/src/Mandango/Query.php

https://github.com/brikou/mandango · PHP · 606 lines · 265 code · 75 blank · 266 comment · 44 complexity · e71b6c8504bbc226575e0fcd66cb6344 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of Mandango.
  4. *
  5. * (c) Pablo Díez <pablodip@gmail.com>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Mandango;
  11. use Mandango\Repository;
  12. /**
  13. * Query.
  14. *
  15. * @author Pablo Díez <pablodip@gmail.com>
  16. *
  17. * @api
  18. */
  19. abstract class Query implements \Countable, \IteratorAggregate
  20. {
  21. private $repository;
  22. private $hash;
  23. private $criteria;
  24. private $fields;
  25. private $references;
  26. private $sort;
  27. private $limit;
  28. private $skip;
  29. private $batchSize;
  30. private $hint;
  31. private $slaveOkay;
  32. private $snapshot;
  33. private $timeout;
  34. /**
  35. * Constructor.
  36. *
  37. * @param string Mandango\Repository The repository of the document class to query.
  38. *
  39. * @api
  40. */
  41. public function __construct(Repository $repository)
  42. {
  43. $this->repository = $repository;
  44. $hash = $this->repository->getDocumentClass();
  45. if (version_compare(PHP_VERSION, '5.3.6', '=>')) {
  46. $debugBacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  47. } else {
  48. $debugBacktrace = debug_backtrace();
  49. }
  50. foreach ($debugBacktrace as $value) {
  51. if (isset($value['function'])) {
  52. $hash .= $value['function'];
  53. }
  54. if (isset($value['class'])) {
  55. $hash .= $value['class'];
  56. }
  57. if (isset($value['type'])) {
  58. $hash .= $value['type'];
  59. }
  60. if (isset($value['file'])) {
  61. $hash .= $value['file'];
  62. }
  63. if (isset($value['line'])) {
  64. $hash .= $value['line'];
  65. }
  66. }
  67. $this->hash = md5($hash);
  68. $this->criteria = array();
  69. $this->fields = array('_id' => 1);
  70. $this->references = array();
  71. $this->snapshot = false;
  72. if ($fields = $this->getFieldsCache()) {
  73. $this->fields = $fields;
  74. }
  75. if ($references = $this->getReferencesCache()) {
  76. $this->references = $references;
  77. }
  78. }
  79. /**
  80. * Returns the repository.
  81. *
  82. * @return Mandango\Repository The repository.
  83. *
  84. * @api
  85. */
  86. public function getRepository()
  87. {
  88. return $this->repository;
  89. }
  90. /**
  91. * Returns the query hash.
  92. *
  93. * @return string The query hash.
  94. */
  95. public function getHash()
  96. {
  97. return $this->hash;
  98. }
  99. /**
  100. * Returns the fields in cache.
  101. *
  102. * @return array|null The fields in cache, or null if there is not.
  103. */
  104. public function getFieldsCache()
  105. {
  106. $cache = $this->repository->getMandango()->getCache()->get($this->hash);
  107. return ($cache && isset($cache['fields'])) ? $cache['fields'] : null;
  108. }
  109. /**
  110. * Returns the references in cache.
  111. *
  112. * @return array|null The references in cache, or null if there is not.
  113. */
  114. public function getReferencesCache()
  115. {
  116. $cache = $this->repository->getMandango()->getCache()->get($this->hash);
  117. return ($cache && isset($cache['references'])) ? $cache['references'] : null;
  118. }
  119. /**
  120. * Set the criteria.
  121. *
  122. * @param array $criteria The criteria.
  123. *
  124. * @return Mandango\Query The query instance (fluent interface).
  125. *
  126. * @api
  127. */
  128. public function criteria(array $criteria)
  129. {
  130. $this->criteria = $criteria;
  131. return $this;
  132. }
  133. /**
  134. * Merges a criteria with the current one.
  135. *
  136. * @param array $criteria The criteria.
  137. *
  138. * @return Mandango\Query The query instance (fluent interface).
  139. *
  140. * @api
  141. */
  142. public function mergeCriteria(array $criteria)
  143. {
  144. $this->criteria = null === $this->criteria ? $criteria : array_merge($this->criteria, $criteria);
  145. return $this;
  146. }
  147. /**
  148. * Returns the criteria.
  149. *
  150. * @return array The criteria.
  151. *
  152. * @api
  153. */
  154. public function getCriteria()
  155. {
  156. return $this->criteria;
  157. }
  158. /**
  159. * Set the fields.
  160. *
  161. * @param array $fields The fields.
  162. *
  163. * @return Mandango\Query The query instance (fluent interface).
  164. *
  165. * @api
  166. */
  167. public function fields($fields)
  168. {
  169. $this->fields = $fields;
  170. return $this;
  171. }
  172. /**
  173. * Returns the fields.
  174. *
  175. * @return array The fields.
  176. *
  177. * @api
  178. */
  179. public function getFields()
  180. {
  181. return $this->fields;
  182. }
  183. /**
  184. * Set the references.
  185. *
  186. * @param array $references The references.
  187. *
  188. * @return Mandango\Query The query instance (fluent interface).
  189. *
  190. * @throws \InvalidArgumentException If the references are not an array or null.
  191. *
  192. * @api
  193. */
  194. public function references($references)
  195. {
  196. if (null !== $references && !is_array($references)) {
  197. throw new \InvalidArgumentException(sprintf('The references "%s" are not valid.', $references));
  198. }
  199. $this->references = $references;
  200. return $this;
  201. }
  202. /**
  203. * Returns the references.
  204. *
  205. * @return array The references.
  206. *
  207. * @api
  208. */
  209. public function getReferences()
  210. {
  211. return $this->references;
  212. }
  213. /**
  214. * Set the sort.
  215. *
  216. * @param array|null $sort The sort.
  217. *
  218. * @return Mandango\Query The query instance (fluent interface).
  219. *
  220. * @throws \InvalidArgumentException If the sort is not an array or null.
  221. *
  222. * @api
  223. */
  224. public function sort($sort)
  225. {
  226. if (null !== $sort && !is_array($sort)) {
  227. throw new \InvalidArgumentException(sprintf('The sort "%s" is not valid.', $sort));
  228. }
  229. $this->sort = $sort;
  230. return $this;
  231. }
  232. /**
  233. * Returns the sort.
  234. *
  235. * @return array The sort.
  236. *
  237. * @api
  238. */
  239. public function getSort()
  240. {
  241. return $this->sort;
  242. }
  243. /**
  244. * Set the limit.
  245. *
  246. * @param int|null $limit The limit.
  247. *
  248. * @return Mandango\Query The query instance (fluent interface).
  249. *
  250. * @throws \InvalidArgumentException If the limit is not a valid integer or null.
  251. *
  252. * @api
  253. */
  254. public function limit($limit)
  255. {
  256. if (null !== $limit) {
  257. if (!is_numeric($limit) || $limit != (int) $limit) {
  258. throw new \InvalidArgumentException(sprintf('The limit "%s" is not valid.', $limit));
  259. }
  260. $limit = (int) $limit;
  261. }
  262. $this->limit = $limit;
  263. return $this;
  264. }
  265. /**
  266. * Returns the limit.
  267. *
  268. * @return int|null The limit.
  269. *
  270. * @api
  271. */
  272. public function getLimit()
  273. {
  274. return $this->limit;
  275. }
  276. /**
  277. * Set the skip.
  278. *
  279. * @param int|null $skip The skip.
  280. *
  281. * @return Mandango\Query The query instance (fluent interface).
  282. *
  283. * @throws \InvalidArgumentException If the skip is not a valid integer, or null.
  284. *
  285. * @api
  286. */
  287. public function skip($skip)
  288. {
  289. if (null !== $skip) {
  290. if (!is_numeric($skip) || $skip != (int) $skip) {
  291. throw new \InvalidArgumentException(sprintf('The skip "%s" is not valid.', $skip));
  292. }
  293. $skip = (int) $skip;
  294. }
  295. $this->skip = $skip;
  296. return $this;
  297. }
  298. /**
  299. * Returns the skip.
  300. *
  301. * @return int|null The skip.
  302. *
  303. * @api
  304. */
  305. public function getSkip()
  306. {
  307. return $this->skip;
  308. }
  309. /**
  310. * Set the batch size.
  311. *
  312. * @param int|null $batchSize The batch size.
  313. *
  314. * @return Mandango\Query The query instance (fluent interface).
  315. *
  316. * @api
  317. */
  318. public function batchSize($batchSize)
  319. {
  320. if (null !== $batchSize) {
  321. if (!is_numeric($batchSize) || $batchSize != (int) $batchSize) {
  322. throw new \InvalidArgumentException(sprintf('The batchSize "%s" is not valid.', $batchSize));
  323. }
  324. $batchSize = (int) $batchSize;
  325. }
  326. $this->batchSize = $batchSize;
  327. return $this;
  328. }
  329. /**
  330. * Returns the batch size.
  331. *
  332. * @return int|null The batch size.
  333. *
  334. * @api
  335. */
  336. public function getBatchSize()
  337. {
  338. return $this->batchSize;
  339. }
  340. /**
  341. * Set the hint.
  342. *
  343. * @param array|null The hint.
  344. *
  345. * @return Mandango\Query The query instance (fluent interface).
  346. *
  347. * @api
  348. */
  349. public function hint($hint)
  350. {
  351. if (null !== $hint && !is_array($hint)) {
  352. throw new \InvalidArgumentException(sprintf('The hint "%s" is not valid.', $hint));
  353. }
  354. $this->hint = $hint;
  355. return $this;
  356. }
  357. /**
  358. * Returns the hint.
  359. *
  360. * @return array|null The hint.
  361. *
  362. * @api
  363. */
  364. public function getHint()
  365. {
  366. return $this->hint;
  367. }
  368. /**
  369. * Sets the slave okay.
  370. *
  371. * @param Boolean|null $okay If it is okay to query the slave (true by default).
  372. *
  373. * @return Mandango\Query The query instance (fluent interface).
  374. */
  375. public function slaveOkay($okay = true)
  376. {
  377. if (null !== $okay) {
  378. if (!is_bool($okay)) {
  379. throw new \InvalidArgumentException('The slave okay is not a boolean.');
  380. }
  381. }
  382. $this->slaveOkay = $okay;
  383. return $this;
  384. }
  385. /**
  386. * Returns the slave okay.
  387. *
  388. * @return Boolean|null The slave okay.
  389. */
  390. public function getSlaveOkay()
  391. {
  392. return $this->slaveOkay;
  393. }
  394. /**
  395. * Set if the snapshot mode is used.
  396. *
  397. * @param bool $snapshot If the snapshot mode is used.
  398. *
  399. * @return Mandango\Query The query instance (fluent interface).
  400. *
  401. * @api
  402. */
  403. public function snapshot($snapshot)
  404. {
  405. if (!is_bool($snapshot)) {
  406. throw new \InvalidArgumentException('The snapshot is not a boolean.');
  407. }
  408. $this->snapshot = $snapshot;
  409. return $this;
  410. }
  411. /**
  412. * Returns if the snapshot mode is used.
  413. *
  414. * @return bool If the snapshot mode is used.
  415. *
  416. * @api
  417. */
  418. public function getSnapshot()
  419. {
  420. return $this->snapshot;
  421. }
  422. /**
  423. * Set the timeout.
  424. *
  425. * @param int|null $timeout The timeout of the cursor.
  426. *
  427. * @return Mandango\Query The query instance (fluent interface).
  428. *
  429. * @api
  430. */
  431. public function timeout($timeout)
  432. {
  433. if (null !== $timeout) {
  434. if (!is_numeric($timeout) || $timeout != (int) $timeout) {
  435. throw new \InvalidArgumentException(sprintf('The limit "%s" is not valid.', $timeout));
  436. }
  437. $timeout = (int) $timeout;
  438. }
  439. $this->timeout = $timeout;
  440. return $this;
  441. }
  442. /**
  443. * Returns the timeout.
  444. *
  445. * @return int|null The timeout.
  446. *
  447. * @api
  448. */
  449. public function getTimeout()
  450. {
  451. return $this->timeout;
  452. }
  453. /**
  454. * Returns all the results.
  455. *
  456. * @return array An array with all the results.
  457. *
  458. * @api
  459. */
  460. abstract public function all();
  461. /**
  462. * Returns an \ArrayIterator with all results (implements \IteratorAggregate interface).
  463. *
  464. * @return \ArrayIterator An \ArrayIterator with all results.
  465. *
  466. * @api
  467. */
  468. public function getIterator()
  469. {
  470. return new \ArrayIterator($this->all());
  471. }
  472. /**
  473. * Returns one result.
  474. *
  475. * @return Mandango\Document\Document|null A document or null if there is no any result.
  476. *
  477. * @api
  478. */
  479. public function one()
  480. {
  481. $currentLimit = $this->limit;
  482. $results = $this->limit(1)->all();
  483. $this->limit = $currentLimit;
  484. return $results ? array_shift($results) : null;
  485. }
  486. /**
  487. * Count the number of results of the query.
  488. *
  489. * @return int The number of results of the query.
  490. *
  491. * @api
  492. */
  493. public function count()
  494. {
  495. return $this->createCursor()->count();
  496. }
  497. /**
  498. * Create a cursor with the data of the query.
  499. *
  500. * @return \MongoCursor A cursor with the data of the query.
  501. */
  502. public function createCursor()
  503. {
  504. $cursor = $this->repository->getCollection()->find($this->criteria, $this->fields);
  505. if (null !== $this->sort) {
  506. $cursor->sort($this->sort);
  507. }
  508. if (null !== $this->limit) {
  509. $cursor->limit($this->limit);
  510. }
  511. if (null !== $this->skip) {
  512. $cursor->skip($this->skip);
  513. }
  514. if (null !== $this->batchSize) {
  515. $cursor->batchSize($this->batchSize);
  516. }
  517. if (null !== $this->hint) {
  518. $cursor->hint($this->hint);
  519. }
  520. if (null !== $this->slaveOkay) {
  521. $cursor->slaveOkay($this->slaveOkay);
  522. }
  523. if ($this->snapshot) {
  524. $cursor->snapshot();
  525. }
  526. if (null !== $this->timeout) {
  527. $cursor->timeout($this->timeout);
  528. }
  529. return $cursor;
  530. }
  531. }