3 use wcf\system\database\util\PreparedStatementConditionBuilder
;
4 use wcf\system\exception\SystemException
;
6 use wcf\util\StringUtil
;
9 * Abstract class for a list of database objects.
12 * @copyright 2001-2011 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package com.woltlab.wcf
16 * @category Community Framework
18 abstract class DatabaseObjectList
implements \Countable
, ITraversableObject
{
23 public $className = '';
29 public $objectClassName = '';
33 * @var array<wcf\data\DatabaseObject>
35 public $objects = array();
38 * ids of result objects
41 public $objectIDs = null;
47 public $sqlOffset = 0;
53 public $sqlLimit = 20;
56 * sql order by statement
59 public $sqlOrderBy = '';
62 * sql select parameters
65 public $sqlSelects = '';
68 * sql select joins which are necessary for where statements
71 public $sqlConditionJoins = '';
77 public $sqlJoins = '';
80 * enables the automatic usage of the qualified shorthand
83 public $useQualifiedShorthand = true;
87 * @var wcf\system\database\util\PreparedStatementConditionBuilder
89 protected $conditionBuilder = null;
92 * current iterator index
98 * list of index to object relation
101 protected $indexToObject = null;
104 * Creates a new DatabaseObjectList object.
106 public function __construct() {
108 if (empty($this->className
)) {
109 $className = get_called_class();
111 if (StringUtil
::substring($className, -4) == 'List') {
112 $this->className
= StringUtil
::substring($className, 0, -4);
116 $this->conditionBuilder
= new PreparedStatementConditionBuilder();
120 * Counts the number of objects.
124 public function countObjects() {
125 $sql = "SELECT COUNT(*) AS count
126 FROM ".$this->getDatabaseTableName()." ".$this->getDatabaseTableAlias()."
127 ".$this->sqlConditionJoins
."
128 ".$this->getConditionBuilder()->__toString();
129 $statement = WCF
::getDB()->prepareStatement($sql);
130 $statement->execute($this->getConditionBuilder()->getParameters());
131 $row = $statement->fetchArray();
132 return $row['count'];
136 * Reads the object ids from database.
138 public function readObjectIDs() {
139 $this->objectIDs
= array();
140 $sql = "SELECT ".$this->getDatabaseTableAlias().".".$this->getDatabaseTableIndexName()." AS objectID
141 FROM ".$this->getDatabaseTableName()." ".$this->getDatabaseTableAlias()."
142 ".$this->sqlConditionJoins
."
143 ".$this->getConditionBuilder()->__toString()."
144 ".(!empty($this->sqlOrderBy
) ?
"ORDER BY ".$this->sqlOrderBy
: '');
145 $statement = WCF
::getDB()->prepareStatement($sql, $this->sqlLimit
, $this->sqlOffset
);
146 $statement->execute($this->getConditionBuilder()->getParameters());
147 while ($row = $statement->fetchArray()) {
148 $this->objectIDs
[] = $row['objectID'];
153 * Reads the objects from database.
155 public function readObjects() {
156 if ($this->objectIDs
!== null) {
157 if (!count($this->objectIDs
)) {
160 $sql = "SELECT ".(!empty($this->sqlSelects
) ?
$this->sqlSelects
.($this->useQualifiedShorthand ?
',' : '') : '')."
161 ".($this->useQualifiedShorthand ?
$this->getDatabaseTableAlias().'.*' : '')."
162 FROM ".$this->getDatabaseTableName()." ".$this->getDatabaseTableAlias()."
164 WHERE ".$this->getDatabaseTableAlias().".".$this->getDatabaseTableIndexName()." IN (?".str_repeat(',?', count($this->objectIDs
) - 1).")
165 ".(!empty($this->sqlOrderBy
) ?
"ORDER BY ".$this->sqlOrderBy
: '');
166 $statement = WCF
::getDB()->prepareStatement($sql);
167 $statement->execute($this->objectIDs
);
168 $this->objects
= $statement->fetchObjects(($this->objectClassName ?
: $this->className
));
171 //if (!empty($this->sqlSelects)) die("x".$this->sqlSelects);
172 $sql = "SELECT ".(!empty($this->sqlSelects
) ?
$this->sqlSelects
.($this->useQualifiedShorthand ?
',' : '') : '')."
173 ".($this->useQualifiedShorthand ?
$this->getDatabaseTableAlias().'.*' : '')."
174 FROM ".$this->getDatabaseTableName()." ".$this->getDatabaseTableAlias()."
176 ".$this->getConditionBuilder()->__toString()."
177 ".(!empty($this->sqlOrderBy
) ?
"ORDER BY ".$this->sqlOrderBy
: '');
178 $statement = WCF
::getDB()->prepareStatement($sql, $this->sqlLimit
, $this->sqlOffset
);
179 $statement->execute($this->getConditionBuilder()->getParameters());
180 $this->objects
= $statement->fetchObjects(($this->objectClassName ?
: $this->className
));
183 // use table index as array index
185 foreach($this->objects
as $object) {
186 $objectID = $object->{$this->getDatabaseTableIndexName()};
187 $objects[$objectID] = $object;
189 $this->indexToObject
[] = $objectID;
191 $this->objectIDs
= $this->indexToObject
;
192 $this->objects
= $objects;
196 * Returns the object ids of the list.
198 * @return array<integer>
200 public function getObjectIDs() {
201 return $this->objectIDs
;
205 * Sets the object ids.
207 * @param array<integer> $objectIDs
209 public function setObjectIDs(array $objectIDs) {
210 $this->objectIDs
= $objectIDs;
214 * Returns the objects of the list.
216 * @return array<wcf\data\DatabaseObject>
218 public function getObjects() {
219 return $this->objects
;
223 * Returns the condition builder object.
225 * @return wcf\system\database\util\PreparedStatementConditionBuilder
227 public function getConditionBuilder() {
228 return $this->conditionBuilder
;
232 * Returns the name of the database table.
236 public function getDatabaseTableName() {
237 return call_user_func(array($this->className
, 'getDatabaseTableName'));
241 * Returns the name of the database table.
245 public function getDatabaseTableIndexName() {
246 return call_user_func(array($this->className
, 'getDatabaseTableIndexName'));
250 * Returns the name of the database table alias.
254 public function getDatabaseTableAlias() {
255 return call_user_func(array($this->className
, 'getDatabaseTableAlias'));
259 * @see \Countable::count()
261 public function count() {
262 return count($this->objects
);
266 * @see \Iterator::current()
268 public function current() {
269 $objectID = $this->indexToObject
[$this->index
];
270 return $this->objects
[$objectID];
274 * CAUTION: This methods does not return the current iterator index,
275 * rather than the object key which maps to that index.
277 * @see \Iterator::key()
279 public function key() {
280 return $this->indexToObject
[$this->index
];
284 * @see \Iterator::next()
286 public function next() {
291 * @see \Iterator::rewind()
293 public function rewind() {
298 * @see \Iterator::valid()
300 public function valid() {
301 return isset($this->indexToObject
[$this->index
]);
305 * @see \SeekableIterator::seek()
307 public function seek($index) {
308 $this->index
= $index;
310 if (!$this->valid()) {
311 throw new \
OutOfBoundsException();
316 * @see wcf\data\ITraversableObject::seekTo()
318 public function seekTo($objectID) {
319 $this->index
= array_search($objectID, $this->indexToObject
);
321 if ($this->index
=== false) {
322 throw new SystemException("object id '".$objectID."' is invalid");
327 * @see wcf\data\ITraversableObject::search()
329 public function search($objectID) {
331 $this->seekTo($objectID);
332 return $this->current();
334 catch (SystemException
$e) {