/lib/Model/Listing/AbstractListing.php

https://github.com/pimcore/pimcore · PHP · 607 lines · 314 code · 94 blank · 199 comment · 35 complexity · fb5a86a31b08ca65495713a48af8edd8 MD5 · raw file

  1. <?php
  2. /**
  3. * Pimcore
  4. *
  5. * This source file is available under two different licenses:
  6. * - GNU General Public License version 3 (GPLv3)
  7. * - Pimcore Commercial License (PCL)
  8. * Full copyright and license information is available in
  9. * LICENSE.md which is distributed with this source code.
  10. *
  11. * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12. * @license http://www.pimcore.org/license GPLv3 and PCL
  13. */
  14. namespace Pimcore\Model\Listing;
  15. use Doctrine\DBAL\Connection;
  16. use Doctrine\DBAL\Query\QueryBuilder;
  17. use Pimcore\Db;
  18. use Pimcore\Db\Helper;
  19. use Pimcore\Model\AbstractModel;
  20. use Pimcore\Model\Listing\Dao\AbstractDao;
  21. /**
  22. * @method AbstractDao getDao()
  23. * @method QueryBuilder getQueryBuilder()
  24. */
  25. abstract class AbstractListing extends AbstractModel implements \Iterator, \Countable
  26. {
  27. /**
  28. * @var array
  29. */
  30. protected $order = [];
  31. /**
  32. * @var array
  33. */
  34. protected $orderKey = [];
  35. /**
  36. * @var int|null
  37. */
  38. protected $limit;
  39. /**
  40. * @var int|null
  41. */
  42. protected $offset;
  43. /**
  44. * @var string|null
  45. */
  46. protected $condition;
  47. /**
  48. * @var array
  49. */
  50. protected $conditionVariables = [];
  51. /**
  52. * @var array|null
  53. */
  54. protected $conditionVariablesFromSetCondition;
  55. /**
  56. * @var string|null
  57. */
  58. protected $groupBy;
  59. /**
  60. * @var array
  61. */
  62. protected $validOrders = [
  63. 'ASC',
  64. 'DESC',
  65. ];
  66. /**
  67. * @var array
  68. */
  69. protected $conditionParams = [];
  70. /**
  71. * @var array
  72. */
  73. protected $conditionVariableTypes = [];
  74. /**
  75. * @var array|null
  76. */
  77. protected $data;
  78. /**
  79. * @return array
  80. */
  81. public function getConditionVariableTypes(): array
  82. {
  83. if (!$this->conditionVariables) {
  84. $this->getCondition();
  85. }
  86. return $this->conditionVariableTypes;
  87. }
  88. /**
  89. * @param array $conditionVariableTypes
  90. */
  91. public function setConditionVariableTypes(array $conditionVariableTypes): void
  92. {
  93. $this->conditionVariableTypes = $conditionVariableTypes;
  94. }
  95. /**
  96. * @param string $key
  97. *
  98. * @return bool
  99. */
  100. public function isValidOrderKey($key)
  101. {
  102. return true;
  103. }
  104. /**
  105. * @return int|null
  106. */
  107. public function getLimit()
  108. {
  109. return $this->limit;
  110. }
  111. /**
  112. * @return int|null
  113. */
  114. public function getOffset()
  115. {
  116. return $this->offset;
  117. }
  118. /**
  119. * @return array
  120. */
  121. public function getOrder()
  122. {
  123. return $this->order;
  124. }
  125. /**
  126. * @param int $limit
  127. *
  128. * @return $this
  129. */
  130. public function setLimit($limit)
  131. {
  132. $this->setData(null);
  133. if ((int)$limit > 0) {
  134. $this->limit = (int)$limit;
  135. }
  136. return $this;
  137. }
  138. /**
  139. * @param int $offset
  140. *
  141. * @return $this
  142. */
  143. public function setOffset($offset)
  144. {
  145. $this->setData(null);
  146. if ((int)$offset >= 0) {
  147. $this->offset = (int)$offset;
  148. }
  149. return $this;
  150. }
  151. /**
  152. * @param array|string $order
  153. *
  154. * @return $this
  155. */
  156. public function setOrder($order)
  157. {
  158. $this->setData(null);
  159. $this->order = [];
  160. if (!empty($order)) {
  161. if (is_string($order)) {
  162. $order = strtoupper($order);
  163. if (in_array($order, $this->validOrders)) {
  164. $this->order[] = $order;
  165. }
  166. } elseif (is_array($order)) {
  167. foreach ($order as $o) {
  168. $o = strtoupper($o);
  169. if (in_array($o, $this->validOrders)) {
  170. $this->order[] = $o;
  171. }
  172. }
  173. }
  174. }
  175. return $this;
  176. }
  177. /**
  178. * @return array
  179. */
  180. public function getOrderKey()
  181. {
  182. return $this->orderKey;
  183. }
  184. /**
  185. * @param string|array $orderKey
  186. * @param bool $quote
  187. *
  188. * @return $this
  189. */
  190. public function setOrderKey($orderKey, $quote = true)
  191. {
  192. $this->setData(null);
  193. $this->orderKey = [];
  194. if (is_string($orderKey) && !empty($orderKey)) {
  195. $orderKey = [$orderKey];
  196. }
  197. if (is_array($orderKey)) {
  198. foreach ($orderKey as $o) {
  199. if ($quote === false) {
  200. $this->orderKey[] = $o;
  201. } elseif ($this->isValidOrderKey($o)) {
  202. $this->orderKey[] = $this->quoteIdentifier($o);
  203. }
  204. }
  205. }
  206. return $this;
  207. }
  208. /**
  209. * @param string $condition
  210. * @param mixed $value
  211. * @param string $concatenator
  212. *
  213. * @return $this
  214. */
  215. public function addConditionParam($condition, $value = null, $concatenator = 'AND')
  216. {
  217. $this->setData(null);
  218. $condition = '('.$condition.')';
  219. $ignoreParameter = true;
  220. $conditionWithoutQuotedStrings = preg_replace('/["\'][^"\']*?["\']/', '', $condition);
  221. if (str_contains($conditionWithoutQuotedStrings, '?') || str_contains($conditionWithoutQuotedStrings, ':')) {
  222. $ignoreParameter = false;
  223. }
  224. $this->conditionParams[$condition] = [
  225. 'value' => $value,
  226. 'concatenator' => $concatenator,
  227. 'ignore-value' => $ignoreParameter, // If there is not a placeholder, ignore value!
  228. ];
  229. return $this;
  230. }
  231. /**
  232. * @return array
  233. */
  234. public function getConditionParams()
  235. {
  236. return $this->conditionParams;
  237. }
  238. /**
  239. * @return $this
  240. */
  241. public function resetConditionParams()
  242. {
  243. $this->setData(null);
  244. $this->conditionParams = [];
  245. return $this;
  246. }
  247. /**
  248. * @return string
  249. */
  250. public function getCondition()
  251. {
  252. $conditionString = '';
  253. $conditionVariableTypes = [];
  254. $conditionParams = $this->getConditionParams();
  255. $params = [];
  256. if (!empty($conditionParams)) {
  257. $i = 0;
  258. foreach ($conditionParams as $key => $value) {
  259. if (!$this->condition && $i == 0) {
  260. $conditionString .= $key . ' ';
  261. } else {
  262. $conditionString .= ' ' . $value['concatenator'] . ' ' . $key . ' ';
  263. }
  264. // If there is not a placeholder, ignore value!
  265. if (!$value['ignore-value']) {
  266. if (is_array($value['value'])) {
  267. foreach ($value['value'] as $k => $v) {
  268. if (is_int($k)) {
  269. $params[] = $v;
  270. } else {
  271. $params[$k] = $v;
  272. }
  273. }
  274. } else {
  275. $params[] = $value['value'];
  276. }
  277. }
  278. $i++;
  279. }
  280. }
  281. $params = array_merge((array) $this->getConditionVariablesFromSetCondition(), $params);
  282. $this->setConditionVariables($params);
  283. foreach ($params as $pkey => $param) {
  284. if (is_array($param)) {
  285. if (isset($param[0]) && is_string($param[0])) {
  286. $conditionVariableTypes[$pkey] = Connection::PARAM_STR_ARRAY;
  287. } else {
  288. $conditionVariableTypes[$pkey] = Connection::PARAM_INT_ARRAY;
  289. }
  290. } else {
  291. if (is_bool($param)) {
  292. $type = \PDO::PARAM_BOOL;
  293. } elseif (is_int($param)) {
  294. $type = \PDO::PARAM_INT;
  295. } elseif (is_null($param)) {
  296. $type = \PDO::PARAM_NULL;
  297. } else {
  298. $type = \PDO::PARAM_STR;
  299. }
  300. $conditionVariableTypes[$pkey] = $type;
  301. }
  302. }
  303. $this->setConditionVariableTypes($conditionVariableTypes);
  304. return $this->condition . $conditionString;
  305. }
  306. /**
  307. * @param string $condition
  308. * @param array|scalar $conditionVariables
  309. *
  310. * @return $this
  311. */
  312. public function setCondition($condition, $conditionVariables = null)
  313. {
  314. $this->setData(null);
  315. $this->condition = $condition;
  316. // statement variables
  317. if (is_array($conditionVariables)) {
  318. $this->setConditionVariablesFromSetCondition($conditionVariables);
  319. } elseif ($conditionVariables !== null) {
  320. $this->setConditionVariablesFromSetCondition([$conditionVariables]);
  321. }
  322. return $this;
  323. }
  324. /**
  325. * @return string|null
  326. */
  327. public function getGroupBy()
  328. {
  329. return $this->groupBy;
  330. }
  331. /**
  332. * @return array
  333. */
  334. public function getValidOrders()
  335. {
  336. return $this->validOrders;
  337. }
  338. /**
  339. * @param string $groupBy
  340. * @param bool $qoute
  341. *
  342. * @return $this
  343. */
  344. public function setGroupBy($groupBy, $qoute = true)
  345. {
  346. $this->setData(null);
  347. if ($groupBy) {
  348. $this->groupBy = $groupBy;
  349. if ($qoute) {
  350. $quotedParts = [];
  351. $parts = explode(',', trim($groupBy, '`'));
  352. foreach ($parts as $part) {
  353. $quotedParts[] = $this->quoteIdentifier(trim($part));
  354. }
  355. $this->groupBy = implode(', ', $quotedParts);
  356. }
  357. }
  358. return $this;
  359. }
  360. /**
  361. * @param array $validOrders
  362. *
  363. * @return $this
  364. */
  365. public function setValidOrders($validOrders)
  366. {
  367. $this->validOrders = $validOrders;
  368. return $this;
  369. }
  370. public function quoteIdentifier(string $value): string
  371. {
  372. $db = Db::get();
  373. return $db->quoteIdentifier($value);
  374. }
  375. /**
  376. * @param mixed $value
  377. * @param int|null $type
  378. *
  379. * @return string
  380. */
  381. public function quote($value, $type = null)
  382. {
  383. $db = Db::get();
  384. return $db->quote($value, $type);
  385. }
  386. /**
  387. * @param string $value
  388. *
  389. * @return string
  390. */
  391. public function escapeLike(string $value): string
  392. {
  393. return Helper::escapeLike($value);
  394. }
  395. /**
  396. * @param array $conditionVariables
  397. *
  398. * @return $this
  399. */
  400. public function setConditionVariables($conditionVariables)
  401. {
  402. $this->conditionVariables = $conditionVariables;
  403. return $this;
  404. }
  405. /**
  406. * @return array
  407. */
  408. public function getConditionVariables()
  409. {
  410. if (!$this->conditionVariables) {
  411. $this->getCondition();
  412. }
  413. return $this->conditionVariables;
  414. }
  415. /**
  416. * @param array $conditionVariables
  417. *
  418. * @return $this
  419. */
  420. public function setConditionVariablesFromSetCondition($conditionVariables)
  421. {
  422. $this->setData(null);
  423. $this->conditionVariablesFromSetCondition = $conditionVariables;
  424. return $this;
  425. }
  426. /**
  427. * @return array|null
  428. */
  429. public function getConditionVariablesFromSetCondition()
  430. {
  431. return $this->conditionVariablesFromSetCondition;
  432. }
  433. /**
  434. * @return bool
  435. */
  436. public function isLoaded()
  437. {
  438. return $this->data !== null;
  439. }
  440. /**
  441. * @return array
  442. */
  443. public function getData()
  444. {
  445. if ($this->data === null) {
  446. $this->getDao()->load();
  447. }
  448. return $this->data;
  449. }
  450. /**
  451. * @param array|null $data
  452. *
  453. * @return static
  454. */
  455. public function setData(?array $data): self
  456. {
  457. $this->data = $data;
  458. return $this;
  459. }
  460. /**
  461. * @return mixed
  462. */
  463. #[\ReturnTypeWillChange]
  464. public function current()// : mixed
  465. {
  466. $this->getData();
  467. return current($this->data);
  468. }
  469. /**
  470. * @return int|string|null
  471. */
  472. #[\ReturnTypeWillChange]
  473. public function key()// : mixed
  474. {
  475. $this->getData();
  476. return key($this->data);
  477. }
  478. /**
  479. * @return void
  480. */
  481. #[\ReturnTypeWillChange]
  482. public function next()// : void
  483. {
  484. $this->getData();
  485. next($this->data);
  486. }
  487. /**
  488. * @return bool
  489. */
  490. #[\ReturnTypeWillChange]
  491. public function valid()// : bool
  492. {
  493. $this->getData();
  494. return $this->current() !== false;
  495. }
  496. /**
  497. * @return void
  498. */
  499. #[\ReturnTypeWillChange]
  500. public function rewind()// : void
  501. {
  502. $this->getData();
  503. reset($this->data);
  504. }
  505. /**
  506. * @return int
  507. */
  508. #[\ReturnTypeWillChange]
  509. public function count()// : int
  510. {
  511. return $this->getDao()->getTotalCount();
  512. }
  513. }