http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 13 LOC: 326 Statements: 121

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


Report generated at 2008-03-05T18:27:43-05:00