1: <?php
2: /**
3: * @category Horde
4: * @package Rdo
5: */
6:
7: /**
8: * Iterator for collections of Rdo objects.
9: *
10: * @TODO implement ArrayAccess as well?
11: *
12: * @category Horde
13: * @package Rdo
14: */
15: class Horde_Rdo_List implements Iterator, Countable
16: {
17: /**
18: * Rdo Query
19: * @var mixed
20: */
21: protected $_query;
22:
23: /**
24: * Rdo Mapper
25: * @var Horde_Rdo_Mapper
26: */
27: protected $_mapper;
28:
29: /**
30: * SQL query to run
31: * @var string
32: */
33: protected $_sql;
34:
35: /**
36: * Bind parameters
37: * @var array
38: */
39: protected $_bindParams = array();
40:
41: /**
42: * Result resource
43: * @var Iterator
44: */
45: protected $_result;
46:
47: /**
48: * Current object
49: * @var Horde_Rdo_Base
50: */
51: protected $_current;
52:
53: /**
54: * Current list offset.
55: * @var integer
56: */
57: protected $_index;
58:
59: /**
60: * Are we at the end of the list?
61: * @var boolean
62: */
63: protected $_eof;
64:
65: /**
66: * The number of objects in the list.
67: * @var integer
68: */
69: protected $_count;
70:
71: /**
72: * Constructor.
73: *
74: * @param mixed $query The query to run when results are
75: * requested. Can be a Horde_Rdo_Query object, a literal SQL
76: * query, or a tuple containing an SQL string and an array of bind
77: * parameters to use.
78: * @param Horde_Rdo_Mapper $mapper Mapper to create objects for this list from.
79: */
80: public function __construct($query, $mapper = null)
81: {
82: if ($query instanceof Horde_Rdo_Query) {
83: // Make sure we have a Mapper object, which can be passed
84: // implicitly by being set on the Query.
85: if (!$mapper) {
86: if (!$query->mapper) {
87: throw new Horde_Rdo_Exception('Mapper must be set on the Query object or explicitly passed.');
88: }
89: $mapper = $query->mapper;
90: }
91:
92: // Convert the query into a SQL statement and an array of
93: // bind parameters.
94: list($this->_sql, $this->_bindParams) = $query->getQuery();
95: } elseif (is_string($query)) {
96: // Straight SQL query, empty bind parameters array.
97: $this->_sql = $query;
98: $this->_bindParams = array();
99: } else {
100: // $query is already an array with SQL and bind parameters.
101: list($this->_sql, $this->_bindParams) = $query;
102: }
103:
104: if (!$mapper) {
105: throw new Horde_Rdo_Exception('Mapper must be provided either explicitly or in a Query object');
106: }
107:
108: $this->_query = $query;
109: $this->_mapper = $mapper;
110: }
111:
112: /**
113: * Destructor - release any resources.
114: */
115: public function __destruct()
116: {
117: if ($this->_result) {
118: unset($this->_result);
119: }
120: }
121:
122: /**
123: * Implementation of the rewind() method for iterator.
124: */
125: public function rewind()
126: {
127: if ($this->_result) {
128: unset($this->_result);
129: }
130: $this->_current = null;
131: $this->_index = null;
132: $this->_eof = true;
133: $this->_result = $this->_mapper->adapter->select($this->_sql, $this->_bindParams);
134:
135: $this->next();
136: }
137:
138: /**
139: * Implementation of the current() method for iterator.
140: *
141: * @return mixed The current row, or null if no rows.
142: */
143: public function current()
144: {
145: if (is_null($this->_result)) {
146: $this->rewind();
147: }
148: return $this->_current;
149: }
150:
151: /**
152: * Implementation of the key() method for iterator.
153: *
154: * @return mixed The current row number (starts at 0), or NULL if no rows
155: */
156: public function key()
157: {
158: if (is_null($this->_result)) {
159: $this->rewind();
160: }
161: return $this->_index;
162: }
163:
164: /**
165: * Implementation of the next() method.
166: *
167: * @return Horde_Rdo_Base|null The next Rdo object in the set or
168: * null if no more results.
169: */
170: public function next()
171: {
172: if (is_null($this->_result)) {
173: $this->rewind();
174: }
175:
176: if ($this->_result) {
177: $row = $this->_result->fetch();
178: if (!$row) {
179: $this->_eof = true;
180: } else {
181: $this->_eof = false;
182:
183: if (is_null($this->_index)) {
184: $this->_index = 0;
185: } else {
186: ++$this->_index;
187: }
188:
189: $this->_current = $this->_mapper->map($row);
190: }
191: }
192:
193: return $this->_current;
194: }
195:
196: /**
197: * Implementation of the valid() method for iterator
198: *
199: * @return boolean Whether the iteration is valid
200: */
201: public function valid()
202: {
203: if (is_null($this->_result)) {
204: $this->rewind();
205: }
206: return !$this->_eof;
207: }
208:
209: /**
210: * Implementation of count() for Countable
211: *
212: * @return integer Number of elements in the list
213: */
214: public function count()
215: {
216: if (is_null($this->_count)) {
217: $this->_count = $this->_mapper->count($this->_query);
218: }
219: return $this->_count;
220: }
221:
222: }
223: