Completed
Push — master ( e387d5...1ee501 )
by Vitaly
03:27
created

Query   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 321
Duplicated Lines 12.15 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 20
Bugs 1 Features 11
Metric Value
wmc 31
c 20
b 1
f 11
lcom 1
cbo 4
dl 39
loc 321
rs 9.8

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __invoke() 0 4 1
A __construct() 0 6 1
A flush() 0 11 1
A exec() 14 14 2
A first() 14 14 2
A fields() 11 11 2
A entity() 0 15 3
A getConditionGroup() 0 9 2
A whereCondition() 0 15 3
A where() 0 18 3
A join() 0 8 1
A groupBy() 0 7 1
A limit() 0 7 1
A orderBy() 0 7 1
A id() 0 9 1
B cond() 0 16 6

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
     * Calling this class will result as changing entity.
35
     *
36
     * @param String $entity
37
     * @see self::entity()
38
     * @return self Chaining
39
     * @throws EntityNotFound
40
     */
41
    public function __invoke($entity)
42
    {
43
        return $this->entity($entity);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->entity($entity); of type samsonframework\orm\Query|string adds the type string to the return on line 43 which is incompatible with the return type documented by samsonframework\orm\Query::__invoke of type samsonframework\orm\Query.
Loading history...
44
    }
45
46
    /**
47
     * Query constructor.
48
     * @param string|null $entity Entity identifier
49
     * @param Database Database instance
50
     * @throws EntityNotFound
51
     */
52
    public function __construct($entity, Database &$database)
53
    {
54
        $this->database = &$database;
55
        $this->entity($entity);
56
        $this->flush();
57
    }
58
59
    /**
60
     * Reset all query parameters
61
     * @return self Chaining
62
     */
63
    public function flush()
64
    {
65
        $this->grouping = array();
66
        $this->limitation = array();
67
        $this->sorting = array();
68
69
        $this->cConditionGroup = new Condition();
70
        $this->own_condition = new Condition();
71
72
        return $this;
73
    }
74
75
    /**
76
     * Perform database request and get collection of database record objects
77
     * @see \samson\activerecord\Query::execute()
78
     * @param mixed $return External variable to store query results
79
     * @return mixed If no arguments passed returns query results collection, otherwise query success status
80
     */
81 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...
82
    {
83
        // Call handlers stack
84
        $this->_callHandlers();
85
86
        /** @var RecordInterface[] Perform DB request */
87
        $return = $this->database->find($this->class_name, $this);
0 ignored issues
show
Bug introduced by
The method find() does not seem to exist on object<samsonframework\orm\Database>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
88
89
        // Clear this query
90
        $this->flush();
91
92
        // Return bool or collection
93
        return func_num_args() ? sizeof($return) : $return;
94
    }
95
96
    /**
97
     * Perform database request and get first record from results collection
98
     * @see \samson\activerecord\Query::execute()
99
     * @param mixed $return External variable to store query results
100
     * @return mixed If no arguments passed returns query results first database record object,
101
     * otherwise query success status
102
     */
103 View Code Duplication
    public function first(& $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...
104
    {
105
        // Call handlers stack
106
        $this->_callHandlers();
107
108
        /** @var RecordInterface[] Perform DB request */
109
        $return = array_shift($this->database->find($this->class_name, $this));
0 ignored issues
show
Bug introduced by
$this->database->find($this->class_name, $this) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
Bug introduced by
The method find() does not seem to exist on object<samsonframework\orm\Database>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
110
111
        // Clear this query
112
        $this->flush();
113
114
        // Return bool or collection
115
        return func_num_args() ? sizeof($return) : $return;
116
    }
117
118
    /**
119
     * Perform database request and get array of record field values
120
     * @see \samson\activerecord\Query::execute()
121
     * @param string $fieldName Record field name to get value from
122
     * @param string $return External variable to store query results
123
     * @return Ambigous <boolean, NULL, mixed>
124
     */
125 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...
126
    {
127
        // Call handlers stack
128
        $this->_callHandlers();
129
130
        // Perform DB request
131
        $return = $this->database->fetchColumn($this->class_name, $this, $fieldName);
132
133
        // Return bool or collection
134
        return func_num_args() > 1 ? sizeof($return) : $return;
135
    }
136
137
    /**
138
     * Set query entity to work with.
139
     *
140
     * @param string $entity Entity identifier
141
     * @return Query|string Chaining or current entity identifier if nothing is passed
142
     * @throws EntityNotFound
143
     */
144
    public function entity($entity = null)
145
    {
146
        if (func_num_args() > 0) {
147
            if (class_exists($entity)) {
148
                $this->flush();
149
                $this->class_name = $entity;
150
            } else {
151
                throw new EntityNotFound('['.$entity.'] not found');
152
            }
153
154
            return $this;
155
        }
156
157
        return $this->class_name;
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 &getConditionGroup($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->getConditionGroup($argument->field)->addArgument($argument);
194
            }
195
        }
196
197
        return $this;
198
    }
199
200
    /**
201
     * Add condition to current query.
202
     *
203
     * @param string|Condition|Argument $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
            // Handle empty field value passing to avoid unexpected behaviour
213
            if (!isset($fieldValue)) {
214
                $relation = ArgumentInterface::ISNULL;
215
                $fieldValue = '';
216
            }
217
218
            // Add condition argument
219
            $this->getConditionGroup($fieldName)->add($fieldName, $fieldValue, $relation);
220
        } else {
221
            throw new \InvalidArgumentException('You can only pass string as first argument');
222
        }
223
224
        return $this;
225
    }
226
227
    /**
228
     * Join entity to query.
229
     *
230
     * @param string $entityName Entity identifier
231
     * @return self Chaining
232
     */
233
    public function join($entityName)
234
    {
235
        // TODO: We need to implement this logic
236
        $entityName .= '';
237
238
        // Chaining
239
        return $this;
240
    }
241
242
    /**
243
     * Add query result grouping.
244
     *
245
     * @param string $fieldName Entity field identifier for grouping
246
     * @return self Chaining
247
     */
248
    public function groupBy($fieldName)
249
    {
250
        $this->grouping[] = $fieldName;
251
252
        // Chaining
253
        return $this;
254
    }
255
256
    /**
257
     * Add query result quantity limitation.
258
     *
259
     * @param int $offset Resulting offset
260
     * @param null|int $quantity Amount of RecordInterface object to return
261
     * @return self Chaining
262
     */
263
    public function limit($offset, $quantity = null)
264
    {
265
        $this->limitation = array($offset, $quantity);
266
267
        // Chaining
268
        return $this;
269
    }
270
271
    /**
272
     * Add query result sorting.
273
     *
274
     * @param string $fieldName Entity field identifier for worting
275
     * @param string $order Sorting order
276
     * @return self Chaining
277
     */
278
    public function orderBy($fieldName, $order = 'ASC')
279
    {
280
        $this->sorting[] = array($fieldName, $order);
281
282
        // Chaining
283
        return $this;
284
    }
285
286
    /**
287
     * Add condition by primary field
288
     *
289
     * @param string $value Primary field value
290
     * @return self Chaining
291
     */
292
    public function id($value)
293
    {
294
        // PHP 5.2 get primary field
295
        $_primary = null;
296
        eval('$_primary = ' . $this->class_name . '::$_primary;');
297
298
        // Set primary field value
299
        return $this->where($_primary, $value);
300
    }
301
302
    /**
303
     * Add condition to current query.
304
     * This method supports receives three possible types for $fieldName,
305
     * this is deprecated logic and this should be changed to use separate methods
306
     * for each argument type.
307
     *
308
     * @param string|Condition|Argument $fieldName Entity field name
309
     * @param string $fieldValue Value
310
     * @param string $relation Relation between field name and its value
311
     * @deprecated @see self::where()
312
     * @return self Chaining
313
     */
314
    public function cond($fieldName, $fieldValue = null, $relation = '=')
315
    {
316
        // If empty array is passed
317
        if (is_string($fieldName)) {
318
            return $this->where($fieldName, $fieldValue, $relation);
319
        } elseif (is_array($fieldValue) && !sizeof($fieldValue)) {
320
            $this->empty = true;
0 ignored issues
show
Bug introduced by
The property empty does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
321
            return $this;
322
        } elseif (is_a($fieldName, __NAMESPACE__.'\Condition')) {
323
            $this->whereCondition($fieldName);
0 ignored issues
show
Bug introduced by
It seems like $fieldName defined by parameter $fieldName on line 314 can also be of type object<samsonframework\orm\Argument>; however, samsonframework\orm\Query::whereCondition() does only seem to accept object<samsonframework\orm\ConditionInterface>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
324
        } elseif (is_a($fieldName, __NAMESPACE__.'\Argument')) {
325
            $this->getConditionGroup($fieldName->field)->addArgument($fieldName);
0 ignored issues
show
Bug introduced by
It seems like $fieldName defined by parameter $fieldName on line 314 can also be of type object<samsonframework\orm\Condition>; however, samsonframework\orm\Condition::addArgument() does only seem to accept object<samsonframework\orm\ArgumentInterface>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
326
        }
327
328
        return $this;
329
    }
330
}
331