http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 13 LOC: 321 Statements: 119

Source file Statements Methods Total coverage
Report.php 94.1% 100.0% 94.7%
   
1
<?php
2
/**
3
 * Xyster Framework
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://www.opensource.org/licenses/bsd-license.php
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to xyster@devweblog.org so we can send you a copy immediately.
14
 *
15
 * @category  Xyster
16
 * @package   Xyster_Orm
17
 * @copyright Copyright (c) 2007 Irrational Logic (http://devweblog.org)
18
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
19
 * @version   $Id: Report.php 64 2007-08-12 18:54:56Z doublecompile $
20
 */
21
/**
22
 * @see Xyster_Orm_Query
23
 */
24 1
require_once 'Xyster/Orm/Query.php';
25
/**
26
 * A report query object
27
 *
28
 * @category  Xyster
29
 * @package   Xyster_Orm
30
 * @copyright Copyright (c) 2007 Irrational Logic (http://devweblog.org)
31
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
32
 */
33
class Xyster_Orm_Query_Report extends Xyster_Orm_Query
34
{
35
    const DISTINCT = 'distinct';
36
    const FIELDS = 'fields';
37
    const GROUP = 'group';
38
    const HAVING = 'having';
39
40
    /**
41
     * Turns excluding duplicate records on or off
42
     *
43
     * @param boolean $distinct  True excludes duplicates
44
     */
45
    public function distinct( $distinct = true )
46
    {
47 3
        $this->_parts[self::DISTINCT] = $distinct;
48
49 3
        return $this;
50
    }
51
52
    /**
53
     * Executes the report query
54
     *
55
     * If the entire query cannot be executed in the backend, this method will
56
     * return full entities from the data store, put them in the cache, then do
57
     * any runtime calculations it needs to do.
58
     *
59
     * @return Xyster_Data_Set  The query result set
60
     * @throws Xyster_Orm_Query_Exception if there's a select column not aggregated or grouped
61
     */
62
    public function execute()
63
    {
64
        // make sure all fields are either grouped or aggregates
65 8
        if ( count($this->_parts[self::GROUP]) ) {
66 4
            $groups = array();
67 4
            foreach( $this->_parts[self::GROUP] as $group ) {
68 4
                $groups[] = $group->getName();
69 4
            }
70 4
            foreach( $this->_parts[self::FIELDS] as $field )
71 3
                if ((!$field instanceof Xyster_Data_Field_Aggregate) &&
72 3
                    !in_array($field->getName(), $groups)) {
73 1
                    require_once 'Xyster/Orm/Query/Exception.php';
74 1
                    throw new Xyster_Orm_Query_Exception($field->getName() . ' is not in the group clause');
75 0
                }
76 3
        }
77
78
        // run the query in the backend
79 7
        $collection = $this->_manager->executeQuery($this);
80 7
        if ( $collection instanceof Xyster_Data_Set &&
81 7
            ! $collection instanceof Xyster_Orm_Set ) {
82
            // if it's not an entity set, the whole thing was in the backend;
83
	        // it's safe to just return it
84 3
            return $collection;
85 0
        }
86
87
        // apply any runtime filters to the entity set
88 4
        if ( count($this->_runtime[Xyster_Orm_Query::WHERE]) ) {
89 3
            $collection->filter(Xyster_Data_Criterion::fromArray('AND', $this->_runtime[Xyster_Orm_Query::WHERE]));
90 3
        }
91
92 4
        $fieldsAndGroups = array_merge($this->_parts[self::GROUP], $this->_parts[self::FIELDS]);
93
94
        // setup the Xyster_Data_Set and add the columns to return
95 4
        $rs = new Xyster_Data_Set();
96 4
        foreach( $fieldsAndGroups  as $field ) {
97 4
            $rs->addColumn( $field->getAlias() );
98 4
        }
99
100 4
        if ( $this->_parts[self::GROUP] ) {
101
102 2
            require_once 'Xyster/Data/Tuple.php';
103
            // let Xyster_Data_Tuple do the work for runtime grouping
104 2
            Xyster_Data_Tuple::makeTuples(
105 2
                $rs,
106 2
                $collection,
107 2
                $fieldsAndGroups,
108 2
                $this->_parts[self::HAVING],
109 2
                $this->_parts[Xyster_Orm_Query::LIMIT],
110 2
                $this->_parts[Xyster_Orm_Query::OFFSET]
111 2
                );
112
113 2
        } else {
114
115
            // check to see if the fields contain an aggregate
116 2
            $aggregate = false;
117 2
            foreach( $this->_parts[self::FIELDS] as $field ) {
118 2
                if ( $field instanceof Xyster_Data_Field_Aggregate ) {
119 1
                    $aggregate = $field->getFunction();
120 1
                    break;
121 0
                }
122 1
            }
123 2
            if ( $aggregate ) {
124
                // just one row that contains aggregates
125 1
                require_once 'Xyster/Data/Tuple.php';
126 1
                $tuple = new Xyster_Data_Tuple(array(), $collection);
127 1
                $rs->add($tuple->toRow($this->_parts[self::FIELDS]));
128 1
            } else {
129
                // add the values to the set (enforcing limit & offset)
130 1
                foreach( $collection as $offset=>$entity ) {
131 1
                    if ( $offset >= $this->_parts[Xyster_Orm_Query::OFFSET] ) {
132 1
                        $values = array();
133 1
                        foreach( $this->_parts[self::FIELDS] as $field ) {
134 1
                            $values[$field->getAlias()] = $field->evaluate($entity);
135 1
                        }
136 1
                        $rs->add( $values );
137 1
                        if ( $this->_parts[Xyster_Orm_Query::LIMIT] > 0 &&
138 1
                            count($rs) == $this->_parts[Xyster_Orm_Query::LIMIT] ) {
139 1
                            break;
140 0
                        }
141 1
                    }
142 1
                }
143
            }
144
        }
145
146
        // apply any runtime sort order to the entity set
147 4
        if ( $this->_runtime[Xyster_Orm_Query::ORDER] ) {
148 2
            $sorts = array();
149 2
            foreach( $this->_parts[Xyster_Orm_Query::ORDER] as $sort ) {
150 2
                foreach( $this->_parts[self::FIELDS] as $field ) {
151
                    // make sure the sort field is actually in those returned
152 1
                    if ( $sort->getField()->getName() == $field->getName() ) {
153 1
                        $sorts[] = $sort;
154 1
                        break;
155 0
                    }
156 1
                }
157 2
            }
158 2
            require_once 'Xyster/Data/Comparator.php';
159 2
            $rs->sort(new Xyster_Data_Comparator($sorts));
160 2
        }
161
162 4
        return $rs;
163
    }
164
165
    /**
166
     * Adds a projected field to this query
167
     *
168
     * @param Xyster_Data_Field $field
169
     * @return Xyster_Orm_Query_Report provides a fluent interface
170
     */
171
    public function field( Xyster_Data_Field $field )
172
    {
173 13
        if ( $field instanceof Xyster_Data_Field_Group ) {
174 2
            return $this->group($field);
175 0
        }
176
177 11
        $this->_parser->assertValidFieldForClass($field, $this->_class);
178 11
        $this->_runtime[self::FIELDS] |= $this->_parser->isRuntime($field, $this->_class);
179 11
        $this->_parts[self::FIELDS][] = $field;
180
181 11
        return $this;
182
    }
183
184
    /**
185
     * Gets the non-group {@link Xyster_Data_Field} objects added to the statement
186
     *
187
     * @return array
188
     */
189
    public function getFields()
190
    {
191 7
        return $this->getPart(self::FIELDS);
192
    }
193
194
    /**
195
     * Gets the grouped {@link Xyster_Data_Field} objects added to the statement
196
     *
197
     * @return array
198
     */
199
    public function getGroup()
200
    {
201 6
        return $this->getPart(self::GROUP);
202
    }
203
204
    /**
205
     * Gets the {@link Xyster_Data_Aggregate} {@link Xyster_Data_Criterion} objects added to the statement
206
     *
207
     * @return array
208
     */
209
    public function getHaving()
210
    {
211 4
        return $this->getPart(self::HAVING);
212
    }
213
214
    /**
215
     * Adds grouped field to this report query
216
     *
217
     * @param Xyster_Data_Field_Group $group
218
     * @return Xyster_Orm_Query_Report provides a fluent interface
219
     */
220
    public function group( Xyster_Data_Field_Group $group )
221
    {
222 8
        $this->_parser->assertValidFieldForClass($group, $this->_class);
223 8
        $this->_runtime[self::GROUP] |= $this->_parser->isRuntime($group, $this->_class);
224
225 8
        $this->_parts[self::GROUP][] = $group;
226
227 8
        return $this;
228
    }
229
230
    /**
231
     * Adds a group-criterion to this query
232
     *
233
     * All fields in the criterion must be instances of
234
     * {@link Xyster_Data_Field_Aggregate}, as a "having" clause is applied to
235
     * groups.
236
     *
237
     * @param Xyster_Data_Criterion $having
238
     * @return Xyster_Orm_Query_Report provides a fluent interface
239
     * @throws Xyster_Orm_Query_Exception if not all fields are aggregated
240
     */
241
    public function having( Xyster_Data_Criterion $having )
242
    {
243 8
        $aggs = 0;
244 8
        $fields = Xyster_Data_Criterion::getFields($having);
245 8
        foreach ( $fields as $field ) {
246 8
            $this->_parser->assertValidFieldForClass($field, $this->_class);
247 8
            $aggs += ($field instanceof Xyster_Data_Field_Aggregate) ? 1 : 0;
248 8
        }
249
250 8
        if ( !$aggs || count($fields) != $aggs ) {
251 1
            require_once 'Xyster/Orm/Query/Exception.php';
252 1
            throw new Xyster_Orm_Query_Exception('The criterion provided must contain only aggregated fields');
253 0
        }
254
255 7
        if ( $this->_parser->isRuntime($having, $this->_class) ) {
256 3
            $this->_runtime[self::GROUP] = true;
257 3
        }
258
259 7
        $this->_parts[self::HAVING][] = $having;
260
261 7
        return $this;
262
    }
263
264
    /**
265
     * Gets whether or not this report query has fields that evaluate at runtime
266
     *
267
     * @return boolean
268
     */
269
    public function hasRuntimeFields()
270
    {
271 10
        return (bool) $this->_runtime[self::FIELDS];
272
    }
273
274
    /**
275
     * Gets whether or not this report query has groups that evaluate at runtime
276
     *
277
     * @return boolean
278
     */
279
    public function hasRuntimeGroup()
280
    {
281 12
        return (bool) $this->_runtime[self::GROUP];
282
    }
283
284
    /**
285
     * Gets whether or not to exclude duplicate results
286
     *
287
     * @return boolean
288
     */
289
    public function isDistinct()
290
    {
291 4
        return $this->getPart(self::DISTINCT);
292
    }
293
294
    /**
295
     * Gets whether this query has parts that are evaluated at runtime
296
     *
297
     * @return boolean
298
     */
299
    public function isRuntime()
300
    {
301 18
        return parent::isRuntime() || $this->hasRuntimeGroup() ||
302 18
            $this->hasRuntimeFields();
303
    }
304
305
    /**
306
     * Inits the parts of the query
307
     *
308
     */
309
    protected function _initParts()
310
    {
311 27
        parent::_initParts();
312
313 27
        $this->_parts[self::DISTINCT] = false;
314 27
        $this->_parts[self::FIELDS] = array();
315 27
        $this->_parts[self::GROUP] = array();
316 27
        $this->_parts[self::HAVING] = array();
317
318 27
        $this->_runtime[self::FIELDS] = false;
319 27
        $this->_runtime[self::GROUP] = false;
320
    }
321
}


Report generated at 2007-10-08T19:32:23-05:00