Failed Conditions
Pull Request — master (#6392)
by Alessandro
11:37
created

ResultSetMapping   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 551
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Test Coverage

Coverage 86.75%

Importance

Changes 0
Metric Value
wmc 36
lcom 2
cbo 0
dl 0
loc 551
ccs 72
cts 83
cp 0.8675
rs 8.8
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A addEntityResult() 0 11 2
A setDiscriminatorColumn() 0 7 1
B addIndexBy() 0 27 4
A addIndexByScalar() 0 6 1
A addIndexByColumn() 0 6 1
A hasIndexBy() 0 4 1
A isFieldResult() 0 4 1
A addFieldResult() 0 15 4
A addJoinedEntityResult() 0 8 1
A addScalarResult() 0 11 3
A addMetadataParameterMapping() 0 4 1
A isScalarResult() 0 4 1
A getClassName() 0 4 1
A getScalarAlias() 0 4 1
A getDeclaringClass() 0 4 1
A getRelation() 0 4 1
A isRelation() 0 4 1
A getEntityAlias() 0 4 1
A getParentAlias() 0 4 1
A hasParentAlias() 0 4 1
A getFieldName() 0 4 1
A getAliasMap() 0 4 1
A getEntityResultCount() 0 4 1
A isMixedResult() 0 4 1
A addMetaResult() 0 15 3
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Query;
21
22
/**
23
 * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.
24
 *
25
 * IMPORTANT NOTE:
26
 * The properties of this class are only public for fast internal READ access and to (drastically)
27
 * reduce the size of serialized instances for more effective caching due to better (un-)serialization
28
 * performance.
29
 *
30
 * <b>Users should use the public methods.</b>
31
 *
32
 * @author Roman Borschel <[email protected]>
33
 * @since 2.0
34
 * @todo Think about whether the number of lookup maps can be reduced.
35
 */
36
class ResultSetMapping
37
{
38
    /**
39
     * Whether the result is mixed (contains scalar values together with field values).
40
     *
41
     * @ignore
42
     * @var boolean
43
     */
44
    public $isMixed = false;
45
46
    /**
47
     * Whether the result is a select statement.
48
     *
49
     * @ignore
50
     * @var boolean
51
     */
52
    public $isSelect = true;
53
54
    /**
55
     * Maps alias names to class names.
56
     *
57
     * @ignore
58
     * @var array
59
     */
60
    public $aliasMap = [];
61
62
    /**
63
     * Maps alias names to related association field names.
64
     *
65
     * @ignore
66
     * @var array
67
     */
68
    public $relationMap = [];
69
70
    /**
71
     * Maps alias names to parent alias names.
72
     *
73
     * @ignore
74
     * @var array
75
     */
76
    public $parentAliasMap = [];
77
78
    /**
79
     * Maps column names in the result set to field names for each class.
80
     *
81
     * @ignore
82
     * @var array
83
     */
84
    public $fieldMappings = [];
85
86
    /**
87
     * Maps column names in the result set to the alias/field name to use in the mapped result.
88
     *
89
     * @ignore
90
     * @var array
91
     */
92
    public $scalarMappings = [];
93
94
    /**
95
     * Maps column names in the result set to the alias/field type to use in the mapped result.
96
     *
97
     * @ignore
98
     * @var array
99
     */
100
    public $typeMappings = [];
101
102
    /**
103
     * Maps entities in the result set to the alias name to use in the mapped result.
104
     *
105
     * @ignore
106
     * @var array
107
     */
108
    public $entityMappings = [];
109
110
    /**
111
     * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
112
     *
113
     * @ignore
114
     * @var array
115
     */
116
    public $metaMappings = [];
117
118
    /**
119
     * Maps column names in the result set to the alias they belong to.
120
     *
121
     * @ignore
122
     * @var array
123
     */
124
    public $columnOwnerMap = [];
125
126
    /**
127
     * List of columns in the result set that are used as discriminator columns.
128
     *
129
     * @ignore
130
     * @var array
131
     */
132
    public $discriminatorColumns = [];
133
134
    /**
135
     * Maps alias names to field names that should be used for indexing.
136
     *
137
     * @ignore
138
     * @var array
139
     */
140
    public $indexByMap = [];
141
142
    /**
143
     * Map from column names to class names that declare the field the column is mapped to.
144
     *
145
     * @ignore
146
     * @var array
147
     */
148
    public $declaringClasses = [];
149
150
    /**
151
     * This is necessary to hydrate derivate foreign keys correctly.
152
     *
153
     * @var array
154
     */
155
    public $isIdentifierColumn = [];
156
157
    /**
158
     * Maps column names in the result set to field names for each new object expression.
159
     *
160
     * @var array
161
     */
162
    public $newObjectMappings = [];
163
164
    /**
165
     * Maps metadata parameter names to the metadata attribute.
166
     *
167
     * @var array
168
     */
169
    public $metadataParameterMapping = [];
170
171
    /**
172
     * Contains query parameter names to be resolved as discriminator values
173
     *
174
     * @var array
175
     */
176
    public $discriminatorParameters = [];
177
178
    /**
179
     * Adds an entity result to this ResultSetMapping.
180
     *
181
     * @param string      $class       The class name of the entity.
182
     * @param string      $alias       The alias for the class. The alias must be unique among all entity
183
     *                                 results or joined entity results within this ResultSetMapping.
184
     * @param string|null $resultAlias The result alias with which the entity result should be
185
     *                                 placed in the result structure.
186
     *
187
     * @return ResultSetMapping This ResultSetMapping instance.
188
     *
189
     * @todo Rename: addRootEntity
190
     */
191 1131
    public function addEntityResult($class, $alias, $resultAlias = null)
192
    {
193 1131
        $this->aliasMap[$alias] = $class;
194 1131
        $this->entityMappings[$alias] = $resultAlias;
195
196 1131
        if ($resultAlias !== null) {
197 45
            $this->isMixed = true;
198
        }
199
200 1131
        return $this;
201
    }
202
203
    /**
204
     * Sets a discriminator column for an entity result or joined entity result.
205
     * The discriminator column will be used to determine the concrete class name to
206
     * instantiate.
207
     *
208
     * @param string $alias       The alias of the entity result or joined entity result the discriminator
209
     *                            column should be used for.
210
     * @param string $discrColumn The name of the discriminator column in the SQL result set.
211
     *
212
     * @return ResultSetMapping This ResultSetMapping instance.
213
     *
214
     * @todo Rename: addDiscriminatorColumn
215
     */
216 175
    public function setDiscriminatorColumn($alias, $discrColumn)
217
    {
218 175
        $this->discriminatorColumns[$alias] = $discrColumn;
219 175
        $this->columnOwnerMap[$discrColumn] = $alias;
220
221 175
        return $this;
222
    }
223
224
    /**
225
     * Sets a field to use for indexing an entity result or joined entity result.
226
     *
227
     * @param string $alias     The alias of an entity result or joined entity result.
228
     * @param string $fieldName The name of the field to use for indexing.
229
     *
230
     * @return ResultSetMapping This ResultSetMapping instance.
231
     */
232 42
    public function addIndexBy($alias, $fieldName)
233
    {
234 42
        $found = false;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
235
236 42
        foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) {
237 42
            if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue;
238
239 41
            $this->addIndexByColumn($alias, $columnName);
240 41
            $found = true;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
241
242 41
            break;
243
        }
244
245
        /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals
246
        if ( ! $found) {
247
            $message = sprintf(
248
                'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.',
249
                $alias,
250
                $fieldName
251
            );
252
253
            throw new \LogicException($message);
254
        }
255
        */
256
257 42
        return $this;
258
    }
259
260
    /**
261
     * Sets to index by a scalar result column name.
262
     *
263
     * @param string $resultColumnName
264
     *
265
     * @return ResultSetMapping This ResultSetMapping instance.
266
     */
267 3
    public function addIndexByScalar($resultColumnName)
268
    {
269 3
        $this->indexByMap['scalars'] = $resultColumnName;
270
271 3
        return $this;
272
    }
273
274
    /**
275
     * Sets a column to use for indexing an entity or joined entity result by the given alias name.
276
     *
277
     * @param string $alias
278
     * @param string $resultColumnName
279
     *
280
     * @return ResultSetMapping This ResultSetMapping instance.
281
     */
282 42
    public function addIndexByColumn($alias, $resultColumnName)
283
    {
284 42
        $this->indexByMap[$alias] = $resultColumnName;
285
286 42
        return $this;
287
    }
288
289
    /**
290
     * Checks whether an entity result or joined entity result with a given alias has
291
     * a field set for indexing.
292
     *
293
     * @param string $alias
294
     *
295
     * @return boolean
296
     *
297
     * @todo Rename: isIndexed($alias)
298
     */
299 2
    public function hasIndexBy($alias)
300
    {
301 2
        return isset($this->indexByMap[$alias]);
302
    }
303
304
    /**
305
     * Checks whether the column with the given name is mapped as a field result
306
     * as part of an entity result or joined entity result.
307
     *
308
     * @param string $columnName The name of the column in the SQL result set.
309
     *
310
     * @return boolean
311
     *
312
     * @todo Rename: isField
313
     */
314 1
    public function isFieldResult($columnName)
315
    {
316 1
        return isset($this->fieldMappings[$columnName]);
317
    }
318
319
    /**
320
     * Adds a field to the result that belongs to an entity or joined entity.
321
     *
322
     * @param string      $alias          The alias of the root entity or joined entity to which the field belongs.
323
     * @param string      $columnName     The name of the column in the SQL result set.
324
     * @param string      $fieldName      The name of the field on the declaring class.
325
     * @param string|null $declaringClass The name of the class that declares/owns the specified field.
326
     *                                    When $alias refers to a superclass in a mapped hierarchy but
327
     *                                    the field $fieldName is defined on a subclass, specify that here.
328
     *                                    If not specified, the field is assumed to belong to the class
329
     *                                    designated by $alias.
330
     *
331
     * @return ResultSetMapping This ResultSetMapping instance.
332
     *
333
     * @todo Rename: addField
334
     */
335 1116
    public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null)
336
    {
337
        // column name (in result set) => field name
338 1116
        $this->fieldMappings[$columnName] = $fieldName;
339
        // column name => alias of owner
340 1116
        $this->columnOwnerMap[$columnName] = $alias;
341
        // field name => class name of declaring class
342 1116
        $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
343
344 1116
        if ( ! $this->isMixed && $this->scalarMappings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->scalarMappings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
345 10
            $this->isMixed = true;
346
        }
347
348 1116
        return $this;
349
    }
350
351
    /**
352
     * Adds a joined entity result.
353
     *
354
     * @param string $class       The class name of the joined entity.
355
     * @param string $alias       The unique alias to use for the joined entity.
356
     * @param string $parentAlias The alias of the entity result that is the parent of this joined result.
357
     * @param string $relation    The association field that connects the parent entity result
358
     *                            with the joined entity result.
359
     *
360
     * @return ResultSetMapping This ResultSetMapping instance.
361
     *
362
     * @todo Rename: addJoinedEntity
363
     */
364 384
    public function addJoinedEntityResult($class, $alias, $parentAlias, $relation)
365
    {
366 384
        $this->aliasMap[$alias]       = $class;
367 384
        $this->parentAliasMap[$alias] = $parentAlias;
368 384
        $this->relationMap[$alias]    = $relation;
369
370 384
        return $this;
371
    }
372
373
    /**
374
     * Adds a scalar result mapping.
375
     *
376
     * @param string $columnName The name of the column in the SQL result set.
377
     * @param string $alias      The result alias with which the scalar result should be placed in the result structure.
378
     * @param string $type       The column type
379
     *
380
     * @return ResultSetMapping This ResultSetMapping instance.
381
     *
382
     * @todo Rename: addScalar
383
     */
384 330
    public function addScalarResult($columnName, $alias, $type = 'string')
385
    {
386 330
        $this->scalarMappings[$columnName] = $alias;
387 330
        $this->typeMappings[$columnName]   = $type;
388
389 330
        if ( ! $this->isMixed && $this->fieldMappings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->fieldMappings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
390 120
            $this->isMixed = true;
391
        }
392
393 330
        return $this;
394
    }
395
396
    /**
397
     * Adds a metadata parameter mappings.
398
     *
399
     * @param mixed  $parameter The parameter name in the SQL result set.
400
     * @param string $attribute The metadata attribute.
401
     */
402
    public function addMetadataParameterMapping($parameter, $attribute)
403
    {
404
        $this->metadataParameterMapping[$parameter] = $attribute;
405
    }
406
407
    /**
408
     * Checks whether a column with a given name is mapped as a scalar result.
409
     *
410
     * @param string $columnName The name of the column in the SQL result set.
411
     *
412
     * @return boolean
413
     *
414
     * @todo Rename: isScalar
415
     */
416 2
    public function isScalarResult($columnName)
417
    {
418 2
        return isset($this->scalarMappings[$columnName]);
419
    }
420
421
    /**
422
     * Gets the name of the class of an entity result or joined entity result,
423
     * identified by the given unique alias.
424
     *
425
     * @param string $alias
426
     *
427
     * @return string
428
     */
429 5
    public function getClassName($alias)
430
    {
431 5
        return $this->aliasMap[$alias];
432
    }
433
434
    /**
435
     * Gets the field alias for a column that is mapped as a scalar value.
436
     *
437
     * @param string $columnName The name of the column in the SQL result set.
438
     *
439
     * @return string
440
     */
441 2
    public function getScalarAlias($columnName)
442
    {
443 2
        return $this->scalarMappings[$columnName];
444
    }
445
446
    /**
447
     * Gets the name of the class that owns a field mapping for the specified column.
448
     *
449
     * @param string $columnName
450
     *
451
     * @return string
452
     */
453 4
    public function getDeclaringClass($columnName)
454
    {
455 4
        return $this->declaringClasses[$columnName];
456
    }
457
458
    /**
459
     * @param string $alias
460
     *
461
     * @return AssociationMapping
462
     */
463
    public function getRelation($alias)
464
    {
465
        return $this->relationMap[$alias];
466
    }
467
468
    /**
469
     * @param string $alias
470
     *
471
     * @return boolean
472
     */
473 1
    public function isRelation($alias)
474
    {
475 1
        return isset($this->relationMap[$alias]);
476
    }
477
478
    /**
479
     * Gets the alias of the class that owns a field mapping for the specified column.
480
     *
481
     * @param string $columnName
482
     *
483
     * @return string
484
     */
485 4
    public function getEntityAlias($columnName)
486
    {
487 4
        return $this->columnOwnerMap[$columnName];
488
    }
489
490
    /**
491
     * Gets the parent alias of the given alias.
492
     *
493
     * @param string $alias
494
     *
495
     * @return string
496
     */
497
    public function getParentAlias($alias)
498
    {
499
        return $this->parentAliasMap[$alias];
500
    }
501
502
    /**
503
     * Checks whether the given alias has a parent alias.
504
     *
505
     * @param string $alias
506
     *
507
     * @return boolean
508
     */
509 1
    public function hasParentAlias($alias)
510
    {
511 1
        return isset($this->parentAliasMap[$alias]);
512
    }
513
514
    /**
515
     * Gets the field name for a column name.
516
     *
517
     * @param string $columnName
518
     *
519
     * @return string
520
     */
521 1
    public function getFieldName($columnName)
522
    {
523 1
        return $this->fieldMappings[$columnName];
524
    }
525
526
    /**
527
     * @return array
528
     */
529
    public function getAliasMap()
530
    {
531
        return $this->aliasMap;
532
    }
533
534
    /**
535
     * Gets the number of different entities that appear in the mapped result.
536
     *
537
     * @return integer
538
     */
539
    public function getEntityResultCount()
540
    {
541
        return count($this->aliasMap);
542
    }
543
544
    /**
545
     * Checks whether this ResultSetMapping defines a mixed result.
546
     *
547
     * Mixed results can only occur in object and array (graph) hydration. In such a
548
     * case a mixed result means that scalar values are mixed with objects/array in
549
     * the result.
550
     *
551
     * @return boolean
552
     */
553 1
    public function isMixedResult()
554
    {
555 1
        return $this->isMixed;
556
    }
557
558
    /**
559
     * Adds a meta column (foreign key or discriminator column) to the result set.
560
     *
561
     * @param string $alias              The result alias with which the meta result should be placed in the result structure.
562
     * @param string $columnName         The name of the column in the SQL result set.
563
     * @param string $fieldName          The name of the field on the declaring class.
564
     * @param bool   $isIdentifierColumn
565
     * @param string $type               The column type
566
     *
567
     * @return ResultSetMapping This ResultSetMapping instance.
568
     *
569
     * @todo Make all methods of this class require all parameters and not infer anything
570
     */
571 755
    public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null)
572
    {
573 755
        $this->metaMappings[$columnName] = $fieldName;
574 755
        $this->columnOwnerMap[$columnName] = $alias;
575
576 755
        if ($isIdentifierColumn) {
577 67
            $this->isIdentifierColumn[$alias][$columnName] = true;
578
        }
579
580 755
        if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
581 754
            $this->typeMappings[$columnName] = $type;
582
        }
583
584 755
        return $this;
585
    }
586
}
587
588