Completed
Pull Request — master (#6439)
by Simone
10:37
created

getJoinTableRestrictionsWithKey()   C

Complexity

Conditions 10
Paths 60

Size

Total Lines 73
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 48
CRAP Score 10.0203

Importance

Changes 0
Metric Value
dl 0
loc 73
ccs 48
cts 51
cp 0.9412
rs 5.8564
c 0
b 0
f 0
cc 10
eloc 52
nc 60
nop 3
crap 10.0203

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Persisters\Collection;
21
22
use Doctrine\Common\Collections\Criteria;
23
use Doctrine\ORM\Mapping\ClassMetadata;
24
use Doctrine\ORM\Persisters\SqlValueVisitor;
25
use Doctrine\ORM\PersistentCollection;
26
use Doctrine\ORM\Query;
27
use Doctrine\ORM\Utility\PersisterHelper;
28
29
/**
30
 * Persister for many-to-many collections.
31
 *
32
 * @author  Roman Borschel <[email protected]>
33
 * @author  Guilherme Blanco <[email protected]>
34
 * @author  Alexander <[email protected]>
35
 * @since   2.0
36
 */
37
class ManyToManyPersister extends AbstractCollectionPersister
38
{
39
    /**
40
     * {@inheritdoc}
41
     */
42 17
    public function delete(PersistentCollection $collection)
43
    {
44 17
        $mapping = $collection->getMapping();
45
46 17
        if ( ! $mapping['isOwningSide']) {
47
            return; // ignore inverse side
48
        }
49
50 17
        $types = [];
51 17
        $class = $this->em->getClassMetadata($mapping['sourceEntity']);
52
53 17
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
54 17
            $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
0 ignored issues
show
Compatibility introduced by
$class 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...
55
        }
56
57 17
        $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types);
58 17
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 326
    public function update(PersistentCollection $collection)
64
    {
65 326
        $mapping = $collection->getMapping();
66
67 326
        if ( ! $mapping['isOwningSide']) {
68 231
            return; // ignore inverse side
69
        }
70
71 325
        list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection);
72 325
        list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection);
73
74 325
        foreach ($collection->getDeleteDiff() as $element) {
75 11
            $this->conn->executeUpdate(
76
                $deleteSql,
77 11
                $this->getDeleteRowSQLParameters($collection, $element),
78 11
                $deleteTypes
79
            );
80
        }
81
82 325
        foreach ($collection->getInsertDiff() as $element) {
83 325
            $this->conn->executeUpdate(
84
                $insertSql,
85 325
                $this->getInsertRowSQLParameters($collection, $element),
86 325
                $insertTypes
87
            );
88
        }
89 325
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 3
    public function get(PersistentCollection $collection, $index)
95
    {
96 3
        $mapping = $collection->getMapping();
97
98 3
        if ( ! isset($mapping['indexBy'])) {
99
            throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
100
        }
101
102 3
        $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
103 3
        $mappedKey = $mapping['isOwningSide']
104 2
            ? $mapping['inversedBy']
105 3
            : $mapping['mappedBy'];
106
107 3
        return $persister->load([$mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index], null, $mapping, [], 0, 1);
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 18
    public function count(PersistentCollection $collection)
114
    {
115 18
        $conditions     = [];
116 18
        $params         = [];
117 18
        $types          = [];
118 18
        $mapping        = $collection->getMapping();
119 18
        $id             = $this->uow->getEntityIdentifier($collection->getOwner());
120 18
        $sourceClass    = $this->em->getClassMetadata($mapping['sourceEntity']);
121 18
        $targetClass    = $this->em->getClassMetadata($mapping['targetEntity']);
122 18
        $association    = ( ! $mapping['isOwningSide'])
123 4
            ? $targetClass->associationMappings[$mapping['mappedBy']]
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...
124 18
            : $mapping;
125
126 18
        $joinTableName  = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
127 18
        $joinColumns    = ( ! $mapping['isOwningSide'])
128 4
            ? $association['joinTable']['inverseJoinColumns']
129 18
            : $association['joinTable']['joinColumns'];
130
131 18
        foreach ($joinColumns as $joinColumn) {
132 18
            $columnName     = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
133 18
            $referencedName = $joinColumn['referencedColumnName'];
134 18
            $conditions[]   = 't.' . $columnName . ' = ?';
135 18
            $params[]       = $id[$sourceClass->getFieldForColumn($referencedName)];
136 18
            $types[]        = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
137
        }
138
139 18
        list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
140
141 18
        if ($filterSql) {
142 3
            $conditions[] = $filterSql;
143
        }
144
145
        // If there is a provided criteria, make part of conditions
146
        // @todo Fix this. Current SQL returns something like:
147
        //
148
        /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) {
149
            // A join is needed on the target entity
150
            $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
151
            $targetJoinSql   = ' JOIN ' . $targetTableName . ' te'
152
                . ' ON' . implode(' AND ', $this->getOnConditionSQL($association));
153
154
            // And criteria conditions needs to be added
155
            $persister    = $this->uow->getEntityPersister($targetClass->name);
156
            $visitor      = new SqlExpressionVisitor($persister, $targetClass);
157
            $conditions[] = $visitor->dispatch($expression);
158
159
            $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL;
160
        }*/
161
162
        $sql = 'SELECT COUNT(*)'
163 18
            . ' FROM ' . $joinTableName . ' t'
164 18
            . $joinTargetEntitySQL
165 18
            . ' WHERE ' . implode(' AND ', $conditions);
166
167 18
        return $this->conn->fetchColumn($sql, $params, 0, $types);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->conn->fetchColumn...l, $params, 0, $types); of type string|boolean adds the type boolean to the return on line 167 which is incompatible with the return type declared by the interface Doctrine\ORM\Persisters\...lectionPersister::count of type integer.
Loading history...
168
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173 8
    public function slice(PersistentCollection $collection, $offset, $length = null)
174
    {
175 8
        $mapping   = $collection->getMapping();
176 8
        $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
177
178 8
        return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length);
179
    }
180
    /**
181
     * {@inheritdoc}
182
     */
183 7
    public function containsKey(PersistentCollection $collection, $key)
184
    {
185 7
        $mapping = $collection->getMapping();
186
187 7
        if ( ! isset($mapping['indexBy'])) {
188
            throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
189
        }
190
191 7
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true);
192
193 7
        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
194
195 7
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
196
    }
197
198
    /**
199
     * {@inheritDoc}
200
     */
201 7
    public function contains(PersistentCollection $collection, $element)
202
    {
203 7
        if ( ! $this->isValidEntityState($element)) {
204 2
            return false;
205
        }
206
207 7
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true);
208
209 7
        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
210
211 7
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
212
    }
213
214
    /**
215
     * {@inheritDoc}
216
     */
217 2
    public function removeElement(PersistentCollection $collection, $element)
218
    {
219 2
        if ( ! $this->isValidEntityState($element)) {
220 2
            return false;
221
        }
222
223 2
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false);
224
225 2
        $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
226
227 2
        return (bool) $this->conn->executeUpdate($sql, $params, $types);
228
    }
229
230
    /**
231
     * {@inheritDoc}
232
     */
233 7
    public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
234
    {
235 7
        $mapping       = $collection->getMapping();
236 7
        $owner         = $collection->getOwner();
237 7
        $ownerMetadata = $this->em->getClassMetadata(get_class($owner));
238 7
        $id            = $this->uow->getEntityIdentifier($owner);
239 7
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
240 7
        $onConditions  = $this->getOnConditionSQL($mapping);
241 7
        $whereClauses  = $params = [];
242
243 7
        if ( ! $mapping['isOwningSide']) {
244 1
            $associationSourceClass = $targetClass;
245 1
            $mapping = $targetClass->associationMappings[$mapping['mappedBy']];
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...
246 1
            $sourceRelationMode = 'relationToTargetKeyColumns';
247
        } else {
248 6
            $associationSourceClass = $ownerMetadata;
249 6
            $sourceRelationMode = 'relationToSourceKeyColumns';
250
        }
251
252 7
        foreach ($mapping[$sourceRelationMode] as $key => $value) {
253 7
            $whereClauses[] = sprintf('t.%s = ?', $key);
254 7
            $params[] = $ownerMetadata->containsForeignIdentifier
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier 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...
255
                ? $id[$ownerMetadata->getFieldForColumn($value)]
256 7
                : $id[$ownerMetadata->fieldNames[$value]];
0 ignored issues
show
Bug introduced by
Accessing fieldNames 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...
257
        }
258
259 7
        $parameters = $this->expandCriteriaParameters($criteria);
260
261 7
        foreach ($parameters as $parameter) {
262 2
            list($name, $value) = $parameter;
263 2
            $field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);
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...
264 2
            $whereClauses[]     = sprintf('te.%s = ?', $field);
265 2
            $params[]           = $value;
266
        }
267
268 7
        $tableName    = $this->quoteStrategy->getTableName($targetClass, $this->platform);
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...
269 7
        $joinTable    = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$associationSourceClass 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...
270
271 7
        $rsm = new Query\ResultSetMappingBuilder($this->em);
272 7
        $rsm->addRootEntityFromClassMetadata($targetClass->name, 'te');
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...
273
274 7
        $sql = 'SELECT ' . $rsm->generateSelectClause()
275 7
            . ' FROM ' . $tableName . ' te'
276 7
            . ' JOIN ' . $joinTable  . ' t ON'
277 7
            . implode(' AND ', $onConditions)
278 7
            . ' WHERE ' . implode(' AND ', $whereClauses);
279
280 7
        $sql .= $this->getOrderingSql($criteria, $targetClass);
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...
281
282 7
        $sql .= $this->getLimitSql($criteria);
283
284 7
        $stmt = $this->conn->executeQuery($sql, $params);
285
286
        return $this
287 7
            ->em
288 7
            ->newHydrator(Query::HYDRATE_OBJECT)
289 7
            ->hydrateAll($stmt, $rsm);
290
    }
291
292
    /**
293
     * Generates the filter SQL for a given mapping.
294
     *
295
     * This method is not used for actually grabbing the related entities
296
     * but when the extra-lazy collection methods are called on a filtered
297
     * association. This is why besides the many to many table we also
298
     * have to join in the actual entities table leading to additional
299
     * JOIN.
300
     *
301
     * @param array $mapping Array containing mapping information.
302
     *
303
     * @return string[] ordered tuple:
304
     *                   - JOIN condition to add to the SQL
305
     *                   - WHERE condition to add to the SQL
306
     */
307 32
    public function getFilterSql($mapping)
308
    {
309 32
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
310 32
        $rootClass   = $this->em->getClassMetadata($targetClass->rootEntityName);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
311 32
        $filterSql   = $this->generateFilterConditionSQL($rootClass, 'te');
0 ignored issues
show
Compatibility introduced by
$rootClass 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...
312
313 32
        if ('' === $filterSql) {
314 32
            return ['', ''];
315
        }
316
317
        // A join is needed if there is filtering on the target entity
318 6
        $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$rootClass 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...
319 6
        $joinSql   = ' JOIN ' . $tableName . ' te'
320 6
            . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping));
321
322 6
        return [$joinSql, $filterSql];
323
    }
324
325
    /**
326
     * Generates the filter SQL for a given entity and table alias.
327
     *
328
     * @param ClassMetadata $targetEntity     Metadata of the target entity.
329
     * @param string        $targetTableAlias The table alias of the joined/selected table.
330
     *
331
     * @return string The SQL query part to add to a query.
332
     */
333 32
    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
334
    {
335 32
        $filterClauses = [];
336
337 32
        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
338 6
            if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
339 6
                $filterClauses[] = '(' . $filterExpr . ')';
340
            }
341
        }
342
343 32
        return $filterClauses
344 6
            ? '(' . implode(' AND ', $filterClauses) . ')'
345 32
            : '';
346
    }
347
348
    /**
349
     * Generate ON condition
350
     *
351
     * @param  array $mapping
352
     *
353
     * @return array
354
     */
355 13
    protected function getOnConditionSQL($mapping)
356
    {
357 13
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
358 13
        $association = ( ! $mapping['isOwningSide'])
359 3
            ? $targetClass->associationMappings[$mapping['mappedBy']]
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...
360 13
            : $mapping;
361
362 13
        $joinColumns = $mapping['isOwningSide']
363 10
            ? $association['joinTable']['inverseJoinColumns']
364 13
            : $association['joinTable']['joinColumns'];
365
366 13
        $conditions = [];
367
368 13
        foreach ($joinColumns as $joinColumn) {
369 13
            $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
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...
370 13
            $refColumnName  = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
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...
371
372 13
            $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName;
373
        }
374
375 13
        return $conditions;
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     *
381
     * @override
382
     */
383 17
    protected function getDeleteSQL(PersistentCollection $collection)
384
    {
385 17
        $columns    = [];
386 17
        $mapping    = $collection->getMapping();
387 17
        $class      = $this->em->getClassMetadata(get_class($collection->getOwner()));
388 17
        $joinTable  = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);
0 ignored issues
show
Compatibility introduced by
$class 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...
389
390 17
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
391 17
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
Compatibility introduced by
$class 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...
392
        }
393
394 17
        return 'DELETE FROM ' . $joinTable
395 17
            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
396
    }
397
398
    /**
399
     * {@inheritdoc}
400
     *
401
     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql.
402
     * @override
403
     */
404 17
    protected function getDeleteSQLParameters(PersistentCollection $collection)
405
    {
406 17
        $mapping    = $collection->getMapping();
407 17
        $identifier = $this->uow->getEntityIdentifier($collection->getOwner());
408
409
        // Optimization for single column identifier
410 17
        if (count($mapping['relationToSourceKeyColumns']) === 1) {
411 15
            return [reset($identifier)];
412
        }
413
414
        // Composite identifier
415 2
        $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
416 2
        $params      = [];
417
418 2
        foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
419 2
            $params[] = isset($sourceClass->fieldNames[$refColumnName])
0 ignored issues
show
Bug introduced by
Accessing fieldNames 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...
420 1
                ? $identifier[$sourceClass->fieldNames[$refColumnName]]
0 ignored issues
show
Bug introduced by
Accessing fieldNames 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...
421 2
                : $identifier[$sourceClass->getFieldForColumn($columnName)];
422
        }
423
424 2
        return $params;
425
    }
426
427
    /**
428
     * Gets the SQL statement used for deleting a row from the collection.
429
     *
430
     * @param \Doctrine\ORM\PersistentCollection $collection
431
     *
432
     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
433
     *                             of types for bound parameters
434
     */
435 325
    protected function getDeleteRowSQL(PersistentCollection $collection)
436
    {
437 325
        $mapping     = $collection->getMapping();
438 325
        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
439 325
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
440 325
        $columns     = [];
441 325
        $types       = [];
442
443 325
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
444 325
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
Compatibility introduced by
$class 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...
445 325
            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
0 ignored issues
show
Compatibility introduced by
$class 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...
446
        }
447
448 325
        foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
449 325
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
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...
450 325
            $types[]   = 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...
451
        }
452
453
        return [
454 325
            'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
0 ignored issues
show
Compatibility introduced by
$class 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...
455 325
            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?',
456 325
            $types,
457
        ];
458
    }
459
460
    /**
461
     * Gets the SQL parameters for the corresponding SQL statement to delete the given
462
     * element from the given collection.
463
     *
464
     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql.
465
     *
466
     * @param \Doctrine\ORM\PersistentCollection $collection
467
     * @param mixed                              $element
468
     *
469
     * @return array
470
     */
471 11
    protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
472
    {
473 11
        return $this->collectJoinTableColumnParameters($collection, $element);
474
    }
475
476
    /**
477
     * Gets the SQL statement used for inserting a row in the collection.
478
     *
479
     * @param \Doctrine\ORM\PersistentCollection $collection
480
     *
481
     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
482
     *                             of types for bound parameters
483
     */
484 325
    protected function getInsertRowSQL(PersistentCollection $collection)
485
    {
486 325
        $columns     = [];
487 325
        $types       = [];
488 325
        $mapping     = $collection->getMapping();
489 325
        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
490 325
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
491
492 325
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
493 325
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
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...
494 325
            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
0 ignored issues
show
Compatibility introduced by
$class 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...
495
        }
496
497 325
        foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
498 325
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
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...
499 325
            $types[]   = 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...
500
        }
501
502
        return [
503 325
            'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
0 ignored issues
show
Compatibility introduced by
$class 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...
504 325
            . ' (' . implode(', ', $columns) . ')'
505 325
            . ' VALUES'
506 325
            . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')',
507 325
            $types,
508
        ];
509
    }
510
511
    /**
512
     * Gets the SQL parameters for the corresponding SQL statement to insert the given
513
     * element of the given collection into the database.
514
     *
515
     * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql.
516
     *
517
     * @param \Doctrine\ORM\PersistentCollection $collection
518
     * @param mixed                              $element
519
     *
520
     * @return array
521
     */
522 325
    protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)
523
    {
524 325
        return $this->collectJoinTableColumnParameters($collection, $element);
525
    }
526
527
    /**
528
     * Collects the parameters for inserting/deleting on the join table in the order
529
     * of the join table columns as specified in ManyToManyMapping#joinTableColumns.
530
     *
531
     * @param \Doctrine\ORM\PersistentCollection $collection
532
     * @param object                             $element
533
     *
534
     * @return array
535
     */
536 325
    private function collectJoinTableColumnParameters(PersistentCollection $collection, $element)
537
    {
538 325
        $params      = [];
539 325
        $mapping     = $collection->getMapping();
540 325
        $isComposite = count($mapping['joinTableColumns']) > 2;
541
542 325
        $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner());
543 325
        $identifier2 = $this->uow->getEntityIdentifier($element);
544
545 325
        if ($isComposite) {
546 21
            $class1 = $this->em->getClassMetadata(get_class($collection->getOwner()));
547 21
            $class2 = $collection->getTypeClass();
548
        }
549
550 325
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
551 325
            $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
552
553 325
            if ( ! $isComposite) {
554 304
                $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
555
556 304
                continue;
557
            }
558
559 21
            if ($isRelationToSource) {
560 21
                $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
0 ignored issues
show
Bug introduced by
The variable $class1 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
561
562 21
                continue;
563
            }
564
565 21
            $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
0 ignored issues
show
Bug introduced by
The variable $class2 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
566
        }
567
568 325
        return $params;
569
    }
570
571
    /**
572
     * @param \Doctrine\ORM\PersistentCollection $collection
573
     * @param string                             $key
574
     * @param boolean                            $addFilters Whether the filter SQL should be included or not.
575
     *
576
     * @return array ordered vector:
577
     *                - quoted join table name
578
     *                - where clauses to be added for filtering
579
     *                - parameters to be bound for filtering
580
     *                - types of the parameters to be bound for filtering
581
     */
582 7
    private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters)
583
    {
584 7
        $filterMapping = $collection->getMapping();
585 7
        $mapping       = $filterMapping;
586 7
        $indexBy       = $mapping['indexBy'];
587 7
        $id            = $this->uow->getEntityIdentifier($collection->getOwner());
588 7
        $sourceClass   = $this->em->getClassMetadata($mapping['sourceEntity']);
589 7
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
590
591 7
        if (! $mapping['isOwningSide']) {
592 3
            $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
593 3
            $mapping                = $associationSourceClass->associationMappings[$mapping['mappedBy']];
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...
594 3
            $joinColumns            = $mapping['joinTable']['joinColumns'];
595 3
            $sourceRelationMode     = 'relationToTargetKeyColumns';
596 3
            $targetRelationMode     = 'relationToSourceKeyColumns';
597
        } else {
598 4
            $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
599 4
            $joinColumns            = $mapping['joinTable']['inverseJoinColumns'];
600 4
            $sourceRelationMode     = 'relationToSourceKeyColumns';
601 4
            $targetRelationMode     = 'relationToTargetKeyColumns';
602
        }
603
604 7
        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t';
0 ignored issues
show
Compatibility introduced by
$associationSourceClass 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...
605 7
        $whereClauses    = [];
606 7
        $params          = [];
607 7
        $types           = [];
608
609 7
        $joinNeeded = ! in_array($indexBy, $targetClass->identifier);
0 ignored issues
show
Bug introduced by
Accessing identifier 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...
610
611 7
        if ($joinNeeded) { // extra join needed if indexBy is not a @id
612 3
            $joinConditions = [];
613
614 3
            foreach ($joinColumns as $joinTableColumn) {
615 3
                $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName'];
616
            }
617
618 3
            $tableName        = $this->quoteStrategy->getTableName($targetClass, $this->platform);
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...
619 3
            $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);
620 3
            $columnName       = $targetClass->getColumnName($indexBy);
621
622 3
            $whereClauses[] = 'tr.' . $columnName . ' = ?';
623 3
            $params[]       = $key;
624 3
            $types[]        = PersisterHelper::getTypeOfColumn($columnName, $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...
625
        }
626
627 7
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
628 7
            if (isset($mapping[$sourceRelationMode][$joinTableColumn])) {
629 7
                $column         = $mapping[$sourceRelationMode][$joinTableColumn];
630 7
                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
631 7
                $params[]       = $sourceClass->containsForeignIdentifier
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier 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...
632
                    ? $id[$sourceClass->getFieldForColumn($column)]
633 7
                    : $id[$sourceClass->fieldNames[$column]];
0 ignored issues
show
Bug introduced by
Accessing fieldNames 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...
634 7
                $types[]        = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
635 7
            } elseif ( ! $joinNeeded) {
636 4
                $column = $mapping[$targetRelationMode][$joinTableColumn];
637
638 4
                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
639 4
                $params[]       = $key;
640 7
                $types[]        = PersisterHelper::getTypeOfColumn($column, $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...
641
            }
642
        }
643
644 7
        if ($addFilters) {
645 7
            list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
646
647 7
            if ($filterSql) {
648
                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
649
                $whereClauses[] = $filterSql;
650
            }
651
        }
652
653 7
        return [$quotedJoinTable, $whereClauses, $params, $types];
654
    }
655
656
    /**
657
     * @param \Doctrine\ORM\PersistentCollection $collection
658
     * @param object                             $element
659
     * @param boolean                            $addFilters Whether the filter SQL should be included or not.
660
     *
661
     * @return array ordered vector:
662
     *                - quoted join table name
663
     *                - where clauses to be added for filtering
664
     *                - parameters to be bound for filtering
665
     *                - types of the parameters to be bound for filtering
666
     */
667 9
    private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters)
668
    {
669 9
        $filterMapping  = $collection->getMapping();
670 9
        $mapping        = $filterMapping;
671
672 9
        if ( ! $mapping['isOwningSide']) {
673 4
            $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
674 4
            $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']);
675 4
            $sourceId = $this->uow->getEntityIdentifier($element);
676 4
            $targetId = $this->uow->getEntityIdentifier($collection->getOwner());
677
678 4
            $mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
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...
679
        } else {
680 5
            $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
681 5
            $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
682 5
            $sourceId = $this->uow->getEntityIdentifier($collection->getOwner());
683 5
            $targetId = $this->uow->getEntityIdentifier($element);
684
        }
685
686 9
        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
687 9
        $whereClauses    = [];
688 9
        $params          = [];
689 9
        $types           = [];
690
691 9
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
692 9
            $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
693
694 9
            if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
695 9
                $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn];
696 9
                $params[]     = $targetId[$targetClass->getFieldForColumn($targetColumn)];
697 9
                $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $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...
698
699 9
                continue;
700
            }
701
702
            // relationToSourceKeyColumns
703 9
            $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn];
704 9
            $params[]     = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];
705 9
            $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
706
        }
707
708 9
        if ($addFilters) {
709 7
            $quotedJoinTable .= ' t';
710
711 7
            list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
712
713 7
            if ($filterSql) {
714 3
                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
715 3
                $whereClauses[] = $filterSql;
716
            }
717
        }
718
719 9
        return [$quotedJoinTable, $whereClauses, $params, $types];
720
    }
721
722
    /**
723
     * Expands Criteria Parameters by walking the expressions and grabbing all
724
     * parameters and types from it.
725
     *
726
     * @param \Doctrine\Common\Collections\Criteria $criteria
727
     *
728
     * @return array
729
     */
730 7
    private function expandCriteriaParameters(Criteria $criteria)
731
    {
732 7
        $expression = $criteria->getWhereExpression();
733
734 7
        if ($expression === null) {
735 5
            return [];
736
        }
737
738 2
        $valueVisitor = new SqlValueVisitor();
739
740 2
        $valueVisitor->dispatch($expression);
741
742 2
        list(, $types) = $valueVisitor->getParamsAndTypes();
743
744 2
        return $types;
745
    }
746
747
    /**
748
     * @param Criteria $criteria
749
     * @param ClassMetadata $targetClass
750
     * @return string
751
     */
752 7
    private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass)
753
    {
754 7
        $orderings = $criteria->getOrderings();
755 7
        if ($orderings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $orderings of type string[] 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...
756 2
            $orderBy = [];
757 2
            foreach ($orderings as $name => $direction) {
758 2
                $field = $this->quoteStrategy->getColumnName(
759
                    $name,
760
                    $targetClass,
761 2
                    $this->platform
762
                );
763 2
                $orderBy[] = $field . ' ' . $direction;
764
            }
765
766 2
            return ' ORDER BY ' . implode(', ', $orderBy);
767
        }
768 5
        return '';
769
    }
770
771
    /**
772
     * @param Criteria $criteria
773
     * @return string
774
     * @throws \Doctrine\DBAL\DBALException
775
     */
776 7
    private function getLimitSql(Criteria $criteria)
777
    {
778 7
        $limit  = $criteria->getMaxResults();
779 7
        $offset = $criteria->getFirstResult();
780 7
        if ($limit !== null || $offset !== null) {
781 3
            return $this->platform->modifyLimitQuery('', $limit, $offset);
782
        }
783 4
        return '';
784
    }
785
}
786