http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 13 LOC: 316 Statements: 119

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


Report generated at 2008-01-20T12:13:38-05:00