Failed Conditions
Pull Request — master (#6709)
by Sergey
15:18
created

ResultSetMapping::addFieldResult()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 15
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.2
cc 4
eloc 7
nc 4
nop 4
crap 4
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 last argument for new objects in order to initiate object construction
166
     *
167
     * @var array
168
     */
169
    public $nestedNewObjectArguments = [];
170
171
    /**
172
     * Maps metadata parameter names to the metadata attribute.
173
     *
174
     * @var array
175
     */
176
    public $metadataParameterMapping = [];
177
178
    /**
179
     * Contains query parameter names to be resolved as discriminator values
180
     *
181
     * @var array
182
     */
183
    public $discriminatorParameters = [];
184
185
    /**
186
     * Adds an entity result to this ResultSetMapping.
187
     *
188
     * @param string      $class       The class name of the entity.
189
     * @param string      $alias       The alias for the class. The alias must be unique among all entity
190
     *                                 results or joined entity results within this ResultSetMapping.
191
     * @param string|null $resultAlias The result alias with which the entity result should be
192
     *                                 placed in the result structure.
193
     *
194
     * @return ResultSetMapping This ResultSetMapping instance.
195
     *
196
     * @todo Rename: addRootEntity
197
     */
198 1137
    public function addEntityResult($class, $alias, $resultAlias = null)
199
    {
200 1137
        $this->aliasMap[$alias] = $class;
201 1137
        $this->entityMappings[$alias] = $resultAlias;
202
203 1137
        if ($resultAlias !== null) {
204 45
            $this->isMixed = true;
205
        }
206
207 1137
        return $this;
208
    }
209
210
    /**
211
     * Sets a discriminator column for an entity result or joined entity result.
212
     * The discriminator column will be used to determine the concrete class name to
213
     * instantiate.
214
     *
215
     * @param string $alias       The alias of the entity result or joined entity result the discriminator
216
     *                            column should be used for.
217
     * @param string $discrColumn The name of the discriminator column in the SQL result set.
218
     *
219
     * @return ResultSetMapping This ResultSetMapping instance.
220
     *
221
     * @todo Rename: addDiscriminatorColumn
222
     */
223 177
    public function setDiscriminatorColumn($alias, $discrColumn)
224
    {
225 177
        $this->discriminatorColumns[$alias] = $discrColumn;
226 177
        $this->columnOwnerMap[$discrColumn] = $alias;
227
228 177
        return $this;
229
    }
230
231
    /**
232
     * Sets a field to use for indexing an entity result or joined entity result.
233
     *
234
     * @param string $alias     The alias of an entity result or joined entity result.
235
     * @param string $fieldName The name of the field to use for indexing.
236
     *
237
     * @return ResultSetMapping This ResultSetMapping instance.
238
     */
239 43
    public function addIndexBy($alias, $fieldName)
240
    {
241 43
        $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...
242
243 43
        foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) {
244 43
            if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue;
245
246 42
            $this->addIndexByColumn($alias, $columnName);
247 42
            $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...
248
249 42
            break;
250
        }
251
252
        /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals
253
        if ( ! $found) {
254
            $message = sprintf(
255
                'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.',
256
                $alias,
257
                $fieldName
258
            );
259
260
            throw new \LogicException($message);
261
        }
262
        */
263
264 43
        return $this;
265
    }
266
267
    /**
268
     * Sets to index by a scalar result column name.
269
     *
270
     * @param string $resultColumnName
271
     *
272
     * @return ResultSetMapping This ResultSetMapping instance.
273
     */
274 3
    public function addIndexByScalar($resultColumnName)
275
    {
276 3
        $this->indexByMap['scalars'] = $resultColumnName;
277
278 3
        return $this;
279
    }
280
281
    /**
282
     * Sets a column to use for indexing an entity or joined entity result by the given alias name.
283
     *
284
     * @param string $alias
285
     * @param string $resultColumnName
286
     *
287
     * @return ResultSetMapping This ResultSetMapping instance.
288
     */
289 43
    public function addIndexByColumn($alias, $resultColumnName)
290
    {
291 43
        $this->indexByMap[$alias] = $resultColumnName;
292
293 43
        return $this;
294
    }
295
296
    /**
297
     * Checks whether an entity result or joined entity result with a given alias has
298
     * a field set for indexing.
299
     *
300
     * @param string $alias
301
     *
302
     * @return boolean
303
     *
304
     * @todo Rename: isIndexed($alias)
305
     */
306 2
    public function hasIndexBy($alias)
307
    {
308 2
        return isset($this->indexByMap[$alias]);
309
    }
310
311
    /**
312
     * Checks whether the column with the given name is mapped as a field result
313
     * as part of an entity result or joined entity result.
314
     *
315
     * @param string $columnName The name of the column in the SQL result set.
316
     *
317
     * @return boolean
318
     *
319
     * @todo Rename: isField
320
     */
321 1
    public function isFieldResult($columnName)
322
    {
323 1
        return isset($this->fieldMappings[$columnName]);
324
    }
325
326
    /**
327
     * Adds a field to the result that belongs to an entity or joined entity.
328
     *
329
     * @param string      $alias          The alias of the root entity or joined entity to which the field belongs.
330
     * @param string      $columnName     The name of the column in the SQL result set.
331
     * @param string      $fieldName      The name of the field on the declaring class.
332
     * @param string|null $declaringClass The name of the class that declares/owns the specified field.
333
     *                                    When $alias refers to a superclass in a mapped hierarchy but
334
     *                                    the field $fieldName is defined on a subclass, specify that here.
335
     *                                    If not specified, the field is assumed to belong to the class
336
     *                                    designated by $alias.
337
     *
338
     * @return ResultSetMapping This ResultSetMapping instance.
339
     *
340
     * @todo Rename: addField
341
     */
342 1122
    public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null)
343
    {
344
        // column name (in result set) => field name
345 1122
        $this->fieldMappings[$columnName] = $fieldName;
346
        // column name => alias of owner
347 1122
        $this->columnOwnerMap[$columnName] = $alias;
348
        // field name => class name of declaring class
349 1122
        $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
350
351 1122
        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...
352 10
            $this->isMixed = true;
353
        }
354
355 1122
        return $this;
356
    }
357
358
    /**
359
     * Adds a joined entity result.
360
     *
361
     * @param string $class       The class name of the joined entity.
362
     * @param string $alias       The unique alias to use for the joined entity.
363
     * @param string $parentAlias The alias of the entity result that is the parent of this joined result.
364
     * @param string $relation    The association field that connects the parent entity result
365
     *                            with the joined entity result.
366
     *
367
     * @return ResultSetMapping This ResultSetMapping instance.
368
     *
369
     * @todo Rename: addJoinedEntity
370
     */
371 387
    public function addJoinedEntityResult($class, $alias, $parentAlias, $relation)
372
    {
373 387
        $this->aliasMap[$alias]       = $class;
374 387
        $this->parentAliasMap[$alias] = $parentAlias;
375 387
        $this->relationMap[$alias]    = $relation;
376
377 387
        return $this;
378
    }
379
380
    /**
381
     * Adds a scalar result mapping.
382
     *
383
     * @param string $columnName The name of the column in the SQL result set.
384
     * @param string $alias      The result alias with which the scalar result should be placed in the result structure.
385
     * @param string $type       The column type
386
     *
387
     * @return ResultSetMapping This ResultSetMapping instance.
388
     *
389
     * @todo Rename: addScalar
390
     */
391 331
    public function addScalarResult($columnName, $alias, $type = 'string')
392
    {
393 331
        $this->scalarMappings[$columnName] = $alias;
394 331
        $this->typeMappings[$columnName]   = $type;
395
396 331
        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...
397 120
            $this->isMixed = true;
398
        }
399
400 331
        return $this;
401
    }
402
403
    /**
404
     * Adds a metadata parameter mappings.
405
     *
406
     * @param mixed  $parameter The parameter name in the SQL result set.
407
     * @param string $attribute The metadata attribute.
408
     */
409
    public function addMetadataParameterMapping($parameter, $attribute)
410
    {
411
        $this->metadataParameterMapping[$parameter] = $attribute;
412
    }
413
414
    /**
415
     * Checks whether a column with a given name is mapped as a scalar result.
416
     *
417
     * @param string $columnName The name of the column in the SQL result set.
418
     *
419
     * @return boolean
420
     *
421
     * @todo Rename: isScalar
422
     */
423 2
    public function isScalarResult($columnName)
424
    {
425 2
        return isset($this->scalarMappings[$columnName]);
426
    }
427
428
    /**
429
     * Gets the name of the class of an entity result or joined entity result,
430
     * identified by the given unique alias.
431
     *
432
     * @param string $alias
433
     *
434
     * @return string
435
     */
436 5
    public function getClassName($alias)
437
    {
438 5
        return $this->aliasMap[$alias];
439
    }
440
441
    /**
442
     * Gets the field alias for a column that is mapped as a scalar value.
443
     *
444
     * @param string $columnName The name of the column in the SQL result set.
445
     *
446
     * @return string
447
     */
448 2
    public function getScalarAlias($columnName)
449
    {
450 2
        return $this->scalarMappings[$columnName];
451
    }
452
453
    /**
454
     * Gets the name of the class that owns a field mapping for the specified column.
455
     *
456
     * @param string $columnName
457
     *
458
     * @return string
459
     */
460 4
    public function getDeclaringClass($columnName)
461
    {
462 4
        return $this->declaringClasses[$columnName];
463
    }
464
465
    /**
466
     * @param string $alias
467
     *
468
     * @return string
469
     */
470
    public function getRelation($alias)
471
    {
472
        return $this->relationMap[$alias];
473
    }
474
475
    /**
476
     * @param string $alias
477
     *
478
     * @return boolean
479
     */
480 1
    public function isRelation($alias)
481
    {
482 1
        return isset($this->relationMap[$alias]);
483
    }
484
485
    /**
486
     * Gets the alias of the class that owns a field mapping for the specified column.
487
     *
488
     * @param string $columnName
489
     *
490
     * @return string
491
     */
492 4
    public function getEntityAlias($columnName)
493
    {
494 4
        return $this->columnOwnerMap[$columnName];
495
    }
496
497
    /**
498
     * Gets the parent alias of the given alias.
499
     *
500
     * @param string $alias
501
     *
502
     * @return string
503
     */
504
    public function getParentAlias($alias)
505
    {
506
        return $this->parentAliasMap[$alias];
507
    }
508
509
    /**
510
     * Checks whether the given alias has a parent alias.
511
     *
512
     * @param string $alias
513
     *
514
     * @return boolean
515
     */
516 1
    public function hasParentAlias($alias)
517
    {
518 1
        return isset($this->parentAliasMap[$alias]);
519
    }
520
521
    /**
522
     * Gets the field name for a column name.
523
     *
524
     * @param string $columnName
525
     *
526
     * @return string
527
     */
528 1
    public function getFieldName($columnName)
529
    {
530 1
        return $this->fieldMappings[$columnName];
531
    }
532
533
    /**
534
     * @return array
535
     */
536
    public function getAliasMap()
537
    {
538
        return $this->aliasMap;
539
    }
540
541
    /**
542
     * Gets the number of different entities that appear in the mapped result.
543
     *
544
     * @return integer
545
     */
546
    public function getEntityResultCount()
547
    {
548
        return count($this->aliasMap);
549
    }
550
551
    /**
552
     * Checks whether this ResultSetMapping defines a mixed result.
553
     *
554
     * Mixed results can only occur in object and array (graph) hydration. In such a
555
     * case a mixed result means that scalar values are mixed with objects/array in
556
     * the result.
557
     *
558
     * @return boolean
559
     */
560 1
    public function isMixedResult()
561
    {
562 1
        return $this->isMixed;
563
    }
564
565
    /**
566
     * Adds a meta column (foreign key or discriminator column) to the result set.
567
     *
568
     * @param string $alias              The result alias with which the meta result should be placed in the result structure.
569
     * @param string $columnName         The name of the column in the SQL result set.
570
     * @param string $fieldName          The name of the field on the declaring class.
571
     * @param bool   $isIdentifierColumn
572
     * @param string $type               The column type
573
     *
574
     * @return ResultSetMapping This ResultSetMapping instance.
575
     *
576
     * @todo Make all methods of this class require all parameters and not infer anything
577
     */
578 760
    public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null)
579
    {
580 760
        $this->metaMappings[$columnName] = $fieldName;
581 760
        $this->columnOwnerMap[$columnName] = $alias;
582
583 760
        if ($isIdentifierColumn) {
584 68
            $this->isIdentifierColumn[$alias][$columnName] = true;
585
        }
586
587 760
        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...
588 759
            $this->typeMappings[$columnName] = $type;
589
        }
590
591 760
        return $this;
592
    }
593
594 2
    public function addNewObjectAsArgument($alias, $objOwner, $objOwnerIdx)
595
    {
596
        $owner = [
597 2
            'ownerIndex' => $objOwner,
598 2
            'argIndex' => $objOwnerIdx,
599
        ];
600
601 2
        if (!isset($this->nestedNewObjectArguments[$owner['ownerIndex']])) {
602 2
            $this->nestedNewObjectArguments[$alias] = $owner;
603
604 2
            return;
605
        }
606
607 2
        $this->nestedNewObjectArguments = array_merge(
608 2
            [$alias => $owner],
609 2
            $this->nestedNewObjectArguments
610
        );
611 2
    }
612
}
613
614