Failed Conditions
Push — master ( 8b8169...80f782 )
by Marco
15s
created

ResultSetMappingBuilder::generateSelectClause()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 16
cts 16
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 16
nc 9
nop 1
crap 6
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
use Doctrine\ORM\EntityManagerInterface;
23
use Doctrine\ORM\Mapping\ClassMetadataInfo;
24
use Doctrine\ORM\Mapping\MappingException;
25
use Doctrine\ORM\Utility\PersisterHelper;
26
27
/**
28
 * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields.
29
 *
30
 * @author Michael Ridgway <[email protected]>
31
 * @since 2.1
32
 */
33
class ResultSetMappingBuilder extends ResultSetMapping
34
{
35
    /**
36
     * Picking this rename mode will register entity columns as is,
37
     * as they are in the database. This can cause clashes when multiple
38
     * entities are fetched that have columns with the same name.
39
     *
40
     * @var int
41
     */
42
    const COLUMN_RENAMING_NONE = 1;
43
44
    /**
45
     * Picking custom renaming allows the user to define the renaming
46
     * of specific columns with a rename array that contains column names as
47
     * keys and result alias as values.
48
     *
49
     * @var int
50
     */
51
    const COLUMN_RENAMING_CUSTOM = 2;
52
53
    /**
54
     * Incremental renaming uses a result set mapping internal counter to add a
55
     * number to each column result, leading to uniqueness. This only works if
56
     * you use {@see generateSelectClause()} to generate the SELECT clause for
57
     * you.
58
     *
59
     * @var int
60
     */
61
    const COLUMN_RENAMING_INCREMENT = 3;
62
63
    /**
64
     * @var int
65
     */
66
    private $sqlCounter = 0;
67
68
    /**
69
     * @var EntityManagerInterface
70
     */
71
    private $em;
72
73
    /**
74
     * Default column renaming mode.
75
     *
76
     * @var int
77
     */
78
    private $defaultRenameMode;
79
80
    /**
81
     * @param EntityManagerInterface $em
82
     * @param integer                $defaultRenameMode
83
     */
84 58
    public function __construct(EntityManagerInterface $em, $defaultRenameMode = self::COLUMN_RENAMING_NONE)
85
    {
86 58
        $this->em                = $em;
87 58
        $this->defaultRenameMode = $defaultRenameMode;
88 58
    }
89
90
    /**
91
     * Adds a root entity and all of its fields to the result set.
92
     *
93
     * @param string   $class          The class name of the root entity.
94
     * @param string   $alias          The unique alias to use for the root entity.
95
     * @param array    $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
96
     * @param int|null $renameMode     One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
97
     *
98
     * @return void
99
     */
100 39 View Code Duplication
    public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = [], $renameMode = null)
101
    {
102 39
        $renameMode     = $renameMode ?: $this->defaultRenameMode;
103 39
        $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns);
104
105 39
        $this->addEntityResult($class, $alias);
106 39
        $this->addAllClassFields($class, $alias, $columnAliasMap);
107 38
    }
108
109
    /**
110
     * Adds a joined entity and all of its fields to the result set.
111
     *
112
     * @param string   $class          The class name of the joined entity.
113
     * @param string   $alias          The unique alias to use for the joined entity.
114
     * @param string   $parentAlias    The alias of the entity result that is the parent of this joined result.
115
     * @param string   $relation       The association field that connects the parent entity result
116
     *                                 with the joined entity result.
117
     * @param array    $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
118
     * @param int|null $renameMode     One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
119
     *
120
     * @return void
121
     */
122 12 View Code Duplication
    public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = [], $renameMode = null)
123
    {
124 12
        $renameMode     = $renameMode ?: $this->defaultRenameMode;
125 12
        $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns);
126
127 12
        $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
128 12
        $this->addAllClassFields($class, $alias, $columnAliasMap);
129 11
    }
130
131
    /**
132
     * Adds all fields of the given class to the result set mapping (columns and meta fields).
133
     *
134
     * @param string $class
135
     * @param string $alias
136
     * @param array  $columnAliasMap
137
     *
138
     * @return void
139
     *
140
     * @throws \InvalidArgumentException
141
     */
142 39
    protected function addAllClassFields($class, $alias, $columnAliasMap = [])
143
    {
144 39
        $classMetadata = $this->em->getClassMetadata($class);
145 39
        $platform      = $this->em->getConnection()->getDatabasePlatform();
146
147 39
        if ( ! $this->isInheritanceSupported($classMetadata)) {
0 ignored issues
show
Compatibility introduced by
$classMetadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadataInfo>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
148 1
            throw new \InvalidArgumentException('ResultSetMapping builder does not currently support your inheritance scheme.');
149
        }
150
151
152 38
        foreach ($classMetadata->getColumnNames() as $columnName) {
153 38
            $propertyName = $classMetadata->getFieldName($columnName);
0 ignored issues
show
Bug introduced by
The method getFieldName() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getFieldNames()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
154 38
            $columnAlias  = $platform->getSQLResultCasing($columnAliasMap[$columnName]);
155
156 38
            if (isset($this->fieldMappings[$columnAlias])) {
157 1
                throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
158
            }
159
160 38
            $this->addFieldResult($alias, $columnAlias, $propertyName);
161
        }
162
163 38
        foreach ($classMetadata->associationMappings as $associationMapping) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
164 28
            if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
165 22
                $targetClass  = $this->em->getClassMetadata($associationMapping['targetEntity']);
166 22
                $isIdentifier = isset($associationMapping['id']) && $associationMapping['id'] === true;
167
168 22
                foreach ($associationMapping['joinColumns'] as $joinColumn) {
169 22
                    $columnName  = $joinColumn['name'];
170 22
                    $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]);
171 22
                    $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
172
173 22
                    if (isset($this->metaMappings[$columnAlias])) {
174
                        throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper.");
175
                    }
176
177 28
                    $this->addMetaResult($alias, $columnAlias, $columnName, $isIdentifier, $columnType);
178
                }
179
            }
180
        }
181 38
    }
182
183 39
    private function isInheritanceSupported(ClassMetadataInfo $classMetadata)
184
    {
185 39
        if ($classMetadata->isInheritanceTypeSingleTable()
186 39
            && in_array($classMetadata->name, $classMetadata->discriminatorMap, true)) {
187 3
            return true;
188
        }
189
190 37
        return ! ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined());
191
    }
192
193
    /**
194
     * Gets column alias for a given column.
195
     *
196
     * @param string $columnName
197
     * @param int    $mode
198
     * @param array  $customRenameColumns
199
     *
200
     * @return string
201
     */
202 39
    private function getColumnAlias($columnName, $mode, array $customRenameColumns)
203
    {
204
        switch ($mode) {
205 39
            case self::COLUMN_RENAMING_INCREMENT:
206 3
                return $columnName . $this->sqlCounter++;
207
208 36
            case self::COLUMN_RENAMING_CUSTOM:
209 11
                return $customRenameColumns[$columnName] ?? $columnName;
210
211 35
            case self::COLUMN_RENAMING_NONE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
212 35
                return $columnName;
213
214
        }
215
    }
216
217
    /**
218
     * Retrieves a class columns and join columns aliases that are used in the SELECT clause.
219
     *
220
     * This depends on the renaming mode selected by the user.
221
     *
222
     * @param string $className
223
     * @param int    $mode
224
     * @param array  $customRenameColumns
225
     *
226
     * @return array
227
     */
228 39
    private function getColumnAliasMap($className, $mode, array $customRenameColumns)
229
    {
230 39
        if ($customRenameColumns) { // for BC with 2.2-2.3 API
0 ignored issues
show
Bug Best Practice introduced by
The expression $customRenameColumns 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...
231 11
            $mode = self::COLUMN_RENAMING_CUSTOM;
232
        }
233
234 39
        $columnAlias = [];
235 39
        $class       = $this->em->getClassMetadata($className);
236
237 39
        foreach ($class->getColumnNames() as $columnName) {
238 39
            $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns);
239
        }
240
241 39
        foreach ($class->associationMappings as $associationMapping) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
242 29
            if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
243 23
                foreach ($associationMapping['joinColumns'] as $joinColumn) {
244 23
                    $columnName = $joinColumn['name'];
245 29
                    $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns);
246
                }
247
            }
248
        }
249
250 39
        return $columnAlias;
251
    }
252
253
    /**
254
     * Adds the mappings of the results of native SQL queries to the result set.
255
     *
256
     * @param ClassMetadataInfo $class
257
     * @param array             $queryMapping
258
     *
259
     * @return ResultSetMappingBuilder
260
     */
261 10
    public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping)
262
    {
263 10
        if (isset($queryMapping['resultClass'])) {
264 3
            return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']);
265
        }
266
267 8
        return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']);
268
    }
269
270
    /**
271
     * Adds the class mapping of the results of native SQL queries to the result set.
272
     *
273
     * @param ClassMetadataInfo $class
274
     * @param string            $resultClassName
275
     *
276
     * @return  ResultSetMappingBuilder
277
     */
278 3
    public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName)
279
    {
280 3
        $classMetadata  = $this->em->getClassMetadata($resultClassName);
281 3
        $shortName      = $classMetadata->reflClass->getShortName();
0 ignored issues
show
Bug introduced by
Accessing reflClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
282 3
        $alias          = strtolower($shortName[0]).'0';
283
284 3
        $this->addEntityResult($class->name, $alias);
285
286 3
        if ($classMetadata->discriminatorColumn) {
0 ignored issues
show
Bug introduced by
Accessing discriminatorColumn on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
287 1
            $discrColumn = $classMetadata->discriminatorColumn;
0 ignored issues
show
Bug introduced by
Accessing discriminatorColumn on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
288
289 1
            $this->setDiscriminatorColumn($alias, $discrColumn['name']);
290 1
            $this->addMetaResult($alias, $discrColumn['name'], $discrColumn['fieldName'], false, $discrColumn['type']);
291
        }
292
293 3
        foreach ($classMetadata->getColumnNames() as $key => $columnName) {
294 3
            $propertyName = $classMetadata->getFieldName($columnName);
0 ignored issues
show
Bug introduced by
The method getFieldName() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getFieldNames()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
295
296 3
            $this->addFieldResult($alias, $columnName, $propertyName);
297
        }
298
299 3
        foreach ($classMetadata->associationMappings as $associationMapping) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
300 3
            if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
301 3
                $targetClass = $this->em->getClassMetadata($associationMapping['targetEntity']);
302
303 3
                foreach ($associationMapping['joinColumns'] as $joinColumn) {
304 3
                    $columnName  = $joinColumn['name'];
305 3
                    $columnType  = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
306
307 3
                    $this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName), $columnType);
308
                }
309
            }
310
        }
311
312 3
        return $this;
313
    }
314
315
    /**
316
     * Adds the result set mapping of the results of native SQL queries to the result set.
317
     *
318
     * @param ClassMetadataInfo $class
319
     * @param string            $resultSetMappingName
320
     *
321
     * @return ResultSetMappingBuilder
322
     */
323 8
    public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName)
324
    {
325 8
        $counter        = 0;
326 8
        $resultMapping  = $class->getSqlResultSetMapping($resultSetMappingName);
327 8
        $rootShortName  = $class->reflClass->getShortName();
328 8
        $rootAlias      = strtolower($rootShortName[0]) . $counter;
329
330
331 8
        if (isset($resultMapping['entities'])) {
332 8
            foreach ($resultMapping['entities'] as $key => $entityMapping) {
333 8
                $classMetadata  = $this->em->getClassMetadata($entityMapping['entityClass']);
334
335 8
                if ($class->reflClass->name == $classMetadata->reflClass->name) {
0 ignored issues
show
Bug introduced by
Accessing reflClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
336 8
                    $this->addEntityResult($classMetadata->name, $rootAlias);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
337 8
                    $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias);
0 ignored issues
show
Compatibility introduced by
$classMetadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadataInfo>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
338
                } else {
339 2
                    $shortName      = $classMetadata->reflClass->getShortName();
0 ignored issues
show
Bug introduced by
Accessing reflClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
340 2
                    $joinAlias      = strtolower($shortName[0]) . ++ $counter;
341 2
                    $associations   = $class->getAssociationsByTargetClass($classMetadata->name);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
342
343 2
                    $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias);
0 ignored issues
show
Compatibility introduced by
$classMetadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadataInfo>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
344
345 2
                    foreach ($associations as $relation => $mapping) {
346 8
                        $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation);
347
                    }
348
                }
349
350
            }
351
        }
352
353 8
        if (isset($resultMapping['columns'])) {
354 8
            foreach ($resultMapping['columns'] as $entityMapping) {
355 4
                $type = isset($class->fieldNames[$entityMapping['name']])
356
                    ? PersisterHelper::getTypeOfColumn($entityMapping['name'], $class, $this->em)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\ORM\Mapping\ClassMetadataInfo> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a child class of the class Doctrine\ORM\Mapping\ClassMetadataInfo to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
357 4
                    : 'string';
358
359 4
                $this->addScalarResult($entityMapping['name'], $entityMapping['name'], $type);
360
            }
361
        }
362
363 8
        return $this;
364
    }
365
366
    /**
367
     * Adds the entity result mapping of the results of native SQL queries to the result set.
368
     *
369
     * @param ClassMetadataInfo $classMetadata
370
     * @param array             $entityMapping
371
     * @param string            $alias
372
     *
373
     * @return ResultSetMappingBuilder
374
     *
375
     * @throws MappingException
376
     * @throws \InvalidArgumentException
377
     */
378 8
    public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias)
379
    {
380 8
        if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) {
381 1
            $discriminatorColumn = $entityMapping['discriminatorColumn'];
382 1
            $discriminatorType   = $classMetadata->discriminatorColumn['type'];
383
384 1
            $this->setDiscriminatorColumn($alias, $discriminatorColumn);
385 1
            $this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn, false, $discriminatorType);
386
        }
387
388 8
        if (isset($entityMapping['fields']) && !empty($entityMapping['fields'])) {
389 7
            foreach ($entityMapping['fields'] as $field) {
390 7
                $fieldName = $field['name'];
391 7
                $relation  = null;
392
393 7
                if (strpos($fieldName, '.') !== false) {
394 2
                    list($relation, $fieldName) = explode('.', $fieldName);
395
                }
396
397 7
                if (isset($classMetadata->associationMappings[$relation])) {
398 2
                    if ($relation) {
399 2
                        $associationMapping = $classMetadata->associationMappings[$relation];
400 2
                        $joinAlias          = $alias.$relation;
401 2
                        $parentAlias        = $alias;
402
403 2
                        $this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation);
404 2
                        $this->addFieldResult($joinAlias, $field['column'], $fieldName);
405
                    } else {
406 2
                        $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name);
407
                    }
408
                } else {
409 7
                    if ( ! isset($classMetadata->fieldMappings[$fieldName])) {
410
                        throw new \InvalidArgumentException("Entity '".$classMetadata->name."' has no field '".$fieldName."'. ");
411
                    }
412
413 7
                    $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name);
414
                }
415
            }
416
417
        } else {
418 1
            foreach ($classMetadata->getColumnNames() as $columnName) {
419 1
                $propertyName = $classMetadata->getFieldName($columnName);
420
421 1
                $this->addFieldResult($alias, $columnName, $propertyName);
422
            }
423
        }
424
425 8
        return $this;
426
    }
427
428
    /**
429
     * Generates the Select clause from this ResultSetMappingBuilder.
430
     *
431
     * Works only for all the entity results. The select parts for scalar
432
     * expressions have to be written manually.
433
     *
434
     * @param array $tableAliases
435
     *
436
     * @return string
437
     */
438 13
    public function generateSelectClause($tableAliases = [])
439
    {
440 13
        $sql = "";
441
442 13
        foreach ($this->columnOwnerMap as $columnName => $dqlAlias) {
443 13
            $tableAlias = $tableAliases[$dqlAlias] ?? $dqlAlias;
444
445 13
            if ($sql) {
446 13
                $sql .= ", ";
447
            }
448
449 13
            $sql .= $tableAlias . ".";
450
451 13
            if (isset($this->fieldMappings[$columnName])) {
452 13
                $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]);
453 13
                $sql  .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName'];
0 ignored issues
show
Bug introduced by
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
454 7
            } else if (isset($this->metaMappings[$columnName])) {
455 6
                $sql .= $this->metaMappings[$columnName];
456 1
            } else if (isset($this->discriminatorColumns[$dqlAlias])) {
457 1
                $sql .= $this->discriminatorColumns[$dqlAlias];
458
            }
459
460 13
            $sql .= " AS " . $columnName;
461
        }
462
463 13
        return $sql;
464
    }
465
466
    /**
467
     * @return string
468
     */
469 1
    public function __toString()
470
    {
471 1
        return $this->generateSelectClause([]);
472
    }
473
}
474