Completed
Push — master ( 757aaf...ac375b )
by Vitaly
03:09
created

Query   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 282
Duplicated Lines 9.57 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 30
Bugs 3 Features 11
Metric Value
wmc 26
c 30
b 3
f 11
lcom 1
cbo 3
dl 27
loc 282
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A whereCondition() 0 15 3
A __construct() 0 6 1
A flush() 0 11 1
A execute() 0 18 1
A exec() 8 8 2
A first() 0 12 3
A fields() 8 8 2
A entity() 11 11 2
A conditionGroup() 0 9 2
B where() 0 24 5
A join() 0 8 1
A groupBy() 0 7 1
A limit() 0 7 1
A orderBy() 0 7 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace samsonframework\orm;
3
use samsonframework\orm\exception\EntityNotFound;
4
5
/**
6
 * Database query builder and executer.
7
 * @author Vitaly Iegorov <[email protected]>
8
 * @version 2.0
9
 */
10
class Query extends QueryHandler implements QueryInterface
11
{
12
    /** @var string Class name for interacting with database */
13
    protected $class_name;
14
15
    /** @var array Collection of entity field names for sorting order */
16
    protected $sorting = array();
17
18
    /** @var array Collection of entity field names for grouping query results */
19
    protected $grouping = array();
20
21
    /** @var array Collection of query results limitations */
22
    protected $limitation = array();
23
24
    /** @var Condition Query base entity condition group */
25
    protected $own_condition;
26
27
    /** @var Condition Query entity condition group */
28
    protected $cConditionGroup;
29
30
    /** @var Database Database instance */
31
    protected $database;
32
33
    /**
34
     * Query constructor.
35
     * @param string|null $entity Entity identifier
36
     * @param Database Database instance
37
     * @throws EntityNotFound
38
     */
39
    public function __construct($entity, Database &$database)
40
    {
41
        $this->database = &$database;
42
        $this->entity($entity);
43
        $this->flush();
44
    }
45
46
    /**
47
     * Reset all query parameters
48
     * @return self Chaining
49
     */
50
    public function flush()
51
    {
52
        $this->grouping = array();
53
        $this->limitation = array();
54
        $this->sorting = array();
55
56
        $this->cConditionGroup = new Condition();
57
        $this->own_condition = new Condition();
58
59
        return $this;
60
    }
61
62
    /**
63
     * Proxy function for performing database request and get collection of database record objects.
64
     * This method encapsulates all logic needed for query to be done before and after actual database
65
     * manager request.
66
     *
67
     * @param string $fetcher Database manager fetching method
68
     * @return mixed Return fetching function result
69
     */
70
    protected function execute($fetcher = 'find')
71
    {
72
        // Call handlers stack
73
        $this->callHandlers();
74
75
        // Remove first argument
76
        $args = func_get_args();
77
        array_shift($args);
78
79
        /** @var RecordInterface[] $return Perform DB request */
80
        $return = call_user_func_array(array($this->database, $fetcher), $args);
81
82
        // Clear this query
83
        $this->flush();
84
85
        // Return bool or collection
86
        return $return;
87
    }
88
89
    /**
90
     * Perform database request and get collection of database record objects.
91
     *
92
     * @param mixed $return External variable to store query results
93
     * @return mixed If no arguments passed returns query results collection, otherwise query success status
94
     */
95 View Code Duplication
    public function exec(&$return = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
    {
97
        /** @var RecordInterface[] $return Perform DB request */
98
        $return = $this->execute('find', $this->class_name, $this);
99
100
        // Return bool or collection
101
        return func_num_args() ? sizeof($return) : $return;
102
    }
103
104
    /**
105
     * Perform database request and get first record from results collection.
106
     *
107
     * @param mixed $return External variable to store query results
108
     * @return mixed If no arguments passed returns query results first database record object,
109
     * otherwise query success status
110
     */
111
    public function first(&$return = null)
112
    {
113
        // Add limitation
114
        $this->limit(1);
115
116
        /** @var RecordInterface[] $return Perform DB request */
117
        $return = $this->execute('find', $this->class_name, $this);
118
        $return = sizeof($return) ? array_shift($return) : null;
119
120
        // Return bool or collection
121
        return func_num_args() ? sizeof($return) : $return;
122
    }
123
124
    /**
125
     * Perform database request and get array of record field values
126
     * @see \samson\activerecord\Query::execute()
127
     * @param string $fieldName Record field name to get value from
128
     * @param string $return External variable to store query results
129
     * @return mixed If no arguments passed returns query results first database record object,
130
     * otherwise query success status
131
     */
132 View Code Duplication
    public function fields($fieldName, &$return = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
133
    {
134
        /** @var RecordInterface[] $return Perform DB request */
135
        $return = $this->execute('fetchColumn', $this->class_name, $this, $fieldName);
136
137
        // Return bool or collection
138
        return func_num_args() > 1 ? sizeof($return) : $return;
139
    }
140
141
    /**
142
     * Set query entity to work with.
143
     *
144
     * @param string $entity Entity identifier
145
     * @return Query Chaining
146
     * @throws EntityNotFound
147
     */
148 View Code Duplication
    public function entity($entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
    {
150
        if (class_exists($entity)) {
151
            $this->flush();
152
            $this->class_name = $entity;
153
        } else {
154
            throw new EntityNotFound('['.$entity.'] not found');
155
        }
156
157
        return $this;
158
    }
159
160
    /**
161
     * Get correct query condition depending on entity field name.
162
     * If base entity has field with this name - use base entity condition
163
     * group, otherwise default condition group.
164
     *
165
     * @param string $fieldName Entity field name
166
     * @return Condition Correct query condition group
167
     */
168
    protected function &conditionGroup($fieldName)
169
    {
170
        if (property_exists($this->class_name, $fieldName)) {
171
            // Add this condition to base entity condition group
172
            return $this->own_condition;
173
        }
174
175
        return $this->cConditionGroup;
176
    }
177
178
    /**
179
     * Add query condition as prepared Condition instance.
180
     *
181
     * @param ConditionInterface $condition Condition to be added
182
     * @return self Chaining
183
     */
184
    public function whereCondition(ConditionInterface $condition)
185
    {
186
        // Iterate condition arguments
187
        foreach ($condition as $argument) {
188
            // If passed condition group has another condition group as argument
189
            if (is_a($argument, __NAMESPACE__ . '\Condition')) {
190
                // Go deeper in recursion
191
                $this->whereCondition($argument);
192
            } else { // Otherwise add condition argument to correct condition group
193
                $this->conditionGroup($argument->field)->addArgument($argument);
194
            }
195
        }
196
197
        return $this;
198
    }
199
200
    /**
201
     * Add condition to current query.
202
     *
203
     * @param string $fieldName Entity field name
204
     * @param string $fieldValue Value
205
     * @param string $relation Relation between field name and its value
206
     * @return self Chaining
207
     */
208
    public function where($fieldName, $fieldValue = null, $relation = '=')
209
    {
210
        // If empty array is passed
211
        if (is_string($fieldName)) {
212
            // TODO: We consider empty array passed as condition value as NULL
213
            // Handle empty field value passing to avoid unexpected behaviour
214
            if (!isset($fieldValue)) {
215
                $relation = ArgumentInterface::ISNULL;
216
                $fieldValue = '';
217
            } elseif (is_array($fieldValue) && !sizeof($fieldValue)) {
218
                // Consider this is illegal condition
219
                $relation = ArgumentInterface::EQUAL;
220
                $fieldName = '1';
221
                $fieldValue = '0';
222
            }
223
224
            // Add condition argument
225
            $this->conditionGroup($fieldName)->add($fieldName, $fieldValue, $relation);
226
        } else {
227
            throw new \InvalidArgumentException('You can only pass string as first argument');
228
        }
229
230
        return $this;
231
    }
232
233
    /**
234
     * Join entity to query.
235
     *
236
     * @param string $entityName Entity identifier
237
     * @return self Chaining
238
     */
239
    public function join($entityName)
240
    {
241
        // TODO: We need to implement this logic
242
        $entityName .= '';
243
244
        // Chaining
245
        return $this;
246
    }
247
248
    /**
249
     * Add query result grouping.
250
     *
251
     * @param string $fieldName Entity field identifier for grouping
252
     * @return self Chaining
253
     */
254
    public function groupBy($fieldName)
255
    {
256
        $this->grouping[] = $fieldName;
257
258
        // Chaining
259
        return $this;
260
    }
261
262
    /**
263
     * Add query result quantity limitation.
264
     *
265
     * @param int $offset Resulting offset
266
     * @param null|int $quantity Amount of RecordInterface object to return
267
     * @return self Chaining
268
     */
269
    public function limit($offset, $quantity = null)
270
    {
271
        $this->limitation = array($offset, $quantity);
272
273
        // Chaining
274
        return $this;
275
    }
276
277
    /**
278
     * Add query result sorting.
279
     *
280
     * @param string $fieldName Entity field identifier for worting
281
     * @param string $order Sorting order
282
     * @return self Chaining
283
     */
284
    public function orderBy($fieldName, $order = 'ASC')
285
    {
286
        $this->sorting[] = array($fieldName, $order);
287
288
        // Chaining
289
        return $this;
290
    }
291
}
292