vendor/contao/core-bundle/src/Resources/contao/library/Contao/Model/Collection.php line 139

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Contao.
  4. *
  5. * (c) Leo Feyer
  6. *
  7. * @license LGPL-3.0-or-later
  8. */
  9. namespace Contao\Model;
  10. use Contao\Database\Result;
  11. use Contao\Model;
  12. /**
  13. * The class handles traversing a set of models and lazy loads the database
  14. * result rows upon their first usage.
  15. */
  16. class Collection implements \ArrayAccess, \Countable, \IteratorAggregate
  17. {
  18. /**
  19. * Table name
  20. * @var string
  21. */
  22. protected $strTable;
  23. /**
  24. * Current index
  25. * @var integer
  26. */
  27. protected $intIndex = -1;
  28. /**
  29. * Models
  30. * @var Model[]
  31. */
  32. protected $arrModels = array();
  33. /**
  34. * Create a new collection
  35. *
  36. * @param array $arrModels An array of models
  37. * @param string $strTable The table name
  38. *
  39. * @throws \InvalidArgumentException
  40. */
  41. public function __construct(array $arrModels, $strTable)
  42. {
  43. $arrModels = array_values($arrModels);
  44. foreach ($arrModels as $objModel)
  45. {
  46. if (!$objModel instanceof Model)
  47. {
  48. throw new \InvalidArgumentException('Invalid type: ' . \gettype($objModel));
  49. }
  50. }
  51. $this->arrModels = $arrModels;
  52. $this->strTable = $strTable;
  53. }
  54. /**
  55. * Set an object property
  56. *
  57. * @param string $strKey The property name
  58. * @param mixed $varValue The property value
  59. */
  60. public function __set($strKey, $varValue)
  61. {
  62. if ($this->intIndex < 0)
  63. {
  64. $this->first();
  65. }
  66. $this->arrModels[$this->intIndex]->$strKey = $varValue;
  67. }
  68. /**
  69. * Return an object property
  70. *
  71. * @param string $strKey The property name
  72. *
  73. * @return mixed|null The property value or null
  74. */
  75. public function __get($strKey)
  76. {
  77. if ($this->intIndex < 0)
  78. {
  79. $this->first();
  80. }
  81. return $this->arrModels[$this->intIndex]->$strKey ?? null;
  82. }
  83. /**
  84. * Check whether a property is set
  85. *
  86. * @param string $strKey The property name
  87. *
  88. * @return boolean True if the property is set
  89. */
  90. public function __isset($strKey)
  91. {
  92. if ($this->intIndex < 0)
  93. {
  94. $this->first();
  95. }
  96. return isset($this->arrModels[$this->intIndex]->$strKey);
  97. }
  98. /**
  99. * Create a new collection from a database result
  100. *
  101. * @param Result $objResult The database result object
  102. * @param string $strTable The table name
  103. *
  104. * @return static The model collection
  105. */
  106. public static function createFromDbResult(Result $objResult, $strTable)
  107. {
  108. $arrModels = array();
  109. $strClass = Model::getClassFromTable($strTable);
  110. while ($objResult->next())
  111. {
  112. /** @var Model $strClass */
  113. $objModel = Registry::getInstance()->fetch($strTable, $objResult->{$strClass::getPk()});
  114. if ($objModel !== null)
  115. {
  116. $objModel->mergeRow($objResult->row());
  117. $arrModels[] = $objModel;
  118. }
  119. else
  120. {
  121. $arrModels[] = new $strClass($objResult);
  122. }
  123. }
  124. return new static($arrModels, $strTable);
  125. }
  126. /**
  127. * Return the current row as associative array
  128. *
  129. * @return array The current row as array
  130. */
  131. public function row()
  132. {
  133. if ($this->intIndex < 0)
  134. {
  135. $this->first();
  136. }
  137. return $this->arrModels[$this->intIndex]->row();
  138. }
  139. /**
  140. * Set the current row from an array
  141. *
  142. * @param array $arrData The row data as array
  143. *
  144. * @return static The model collection object
  145. */
  146. public function setRow(array $arrData)
  147. {
  148. if ($this->intIndex < 0)
  149. {
  150. $this->first();
  151. }
  152. $this->arrModels[$this->intIndex]->setRow($arrData);
  153. return $this;
  154. }
  155. /**
  156. * Save the current model
  157. *
  158. * @return static The model collection object
  159. */
  160. public function save()
  161. {
  162. if ($this->intIndex < 0)
  163. {
  164. $this->first();
  165. }
  166. $this->arrModels[$this->intIndex]->save();
  167. return $this;
  168. }
  169. /**
  170. * Delete the current model and return the number of affected rows
  171. *
  172. * @return integer The number of affected rows
  173. */
  174. public function delete()
  175. {
  176. if ($this->intIndex < 0)
  177. {
  178. $this->first();
  179. }
  180. return $this->arrModels[$this->intIndex]->delete();
  181. }
  182. /**
  183. * Return the models as array
  184. *
  185. * @return Model[] An array of models
  186. */
  187. public function getModels()
  188. {
  189. return $this->arrModels;
  190. }
  191. /**
  192. * Lazy load related records
  193. *
  194. * @param string $strKey The property name
  195. *
  196. * @return Collection|Model The model or a model collection if there are multiple rows
  197. */
  198. public function getRelated($strKey)
  199. {
  200. if ($this->intIndex < 0)
  201. {
  202. $this->first();
  203. }
  204. return $this->arrModels[$this->intIndex]->getRelated($strKey);
  205. }
  206. /**
  207. * Return the number of rows in the result set
  208. *
  209. * @return integer The number of rows
  210. */
  211. #[\ReturnTypeWillChange]
  212. public function count()
  213. {
  214. return \count($this->arrModels);
  215. }
  216. /**
  217. * Go to the first row
  218. *
  219. * @return static The model collection object
  220. */
  221. public function first()
  222. {
  223. $this->intIndex = 0;
  224. return $this;
  225. }
  226. /**
  227. * Go to the previous row
  228. *
  229. * @return Collection|false The model collection object or false if there is no previous row
  230. */
  231. public function prev()
  232. {
  233. if ($this->intIndex < 1)
  234. {
  235. return false;
  236. }
  237. --$this->intIndex;
  238. return $this;
  239. }
  240. /**
  241. * Return the current model
  242. *
  243. * @return Model The model object
  244. */
  245. public function current()
  246. {
  247. if ($this->intIndex < 0)
  248. {
  249. $this->first();
  250. }
  251. return $this->arrModels[$this->intIndex];
  252. }
  253. /**
  254. * Go to the next row
  255. *
  256. * @return Collection|false The model collection object or false if there is no next row
  257. */
  258. public function next()
  259. {
  260. if (!isset($this->arrModels[$this->intIndex + 1]))
  261. {
  262. return false;
  263. }
  264. ++$this->intIndex;
  265. return $this;
  266. }
  267. /**
  268. * Go to the last row
  269. *
  270. * @return static The model collection object
  271. */
  272. public function last()
  273. {
  274. $this->intIndex = \count($this->arrModels) - 1;
  275. return $this;
  276. }
  277. /**
  278. * Reset the model
  279. *
  280. * @return static The model collection object
  281. */
  282. public function reset()
  283. {
  284. $this->intIndex = -1;
  285. return $this;
  286. }
  287. /**
  288. * Fetch a column of each row
  289. *
  290. * @param string $strKey The property name
  291. *
  292. * @return array An array with all property values
  293. */
  294. public function fetchEach($strKey)
  295. {
  296. $this->reset();
  297. $return = array();
  298. while ($this->next())
  299. {
  300. $strPk = $this->current()->getPk();
  301. if ($strKey != 'id' && isset($this->$strPk))
  302. {
  303. $return[$this->$strPk] = $this->$strKey;
  304. }
  305. else
  306. {
  307. $return[] = $this->$strKey;
  308. }
  309. }
  310. return $return;
  311. }
  312. /**
  313. * Fetch all columns of every row
  314. *
  315. * @return array An array with all rows and columns
  316. */
  317. public function fetchAll()
  318. {
  319. $this->reset();
  320. $return = array();
  321. while ($this->next())
  322. {
  323. $return[] = $this->row();
  324. }
  325. return $return;
  326. }
  327. /**
  328. * Check whether an offset exists
  329. *
  330. * @param integer $offset The offset
  331. *
  332. * @return boolean True if the offset exists
  333. */
  334. #[\ReturnTypeWillChange]
  335. public function offsetExists($offset)
  336. {
  337. return isset($this->arrModels[$offset]);
  338. }
  339. /**
  340. * Retrieve a particular offset
  341. *
  342. * @param integer $offset The offset
  343. *
  344. * @return Model|null The model or null
  345. */
  346. #[\ReturnTypeWillChange]
  347. public function offsetGet($offset)
  348. {
  349. return $this->arrModels[$offset];
  350. }
  351. /**
  352. * Set a particular offset
  353. *
  354. * @param integer $offset The offset
  355. * @param mixed $value The value to set
  356. *
  357. * @throws \RuntimeException The collection is immutable
  358. */
  359. #[\ReturnTypeWillChange]
  360. public function offsetSet($offset, $value)
  361. {
  362. throw new \RuntimeException('This collection is immutable');
  363. }
  364. /**
  365. * Unset a particular offset
  366. *
  367. * @param integer $offset The offset
  368. *
  369. * @throws \RuntimeException The collection is immutable
  370. */
  371. #[\ReturnTypeWillChange]
  372. public function offsetUnset($offset)
  373. {
  374. throw new \RuntimeException('This collection is immutable');
  375. }
  376. /**
  377. * Retrieve the iterator object
  378. *
  379. * @return \ArrayIterator The iterator object
  380. */
  381. #[\ReturnTypeWillChange]
  382. public function getIterator()
  383. {
  384. return new \ArrayIterator($this->arrModels);
  385. }
  386. }
  387. class_alias(Collection::class, 'Model\Collection');