Completed
Pull Request — master (#6378)
by Sam
69:27 queued 63:49
created

SchemaValidator   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 66.41%

Importance

Changes 0
Metric Value
wmc 54
lcom 1
cbo 6
dl 0
loc 237
ccs 87
cts 131
cp 0.6641
rs 7.0642
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A schemaInSyncWithMetadata() 0 8 1
F validateClass() 0 173 49
A validateMapping() 0 14 3

How to fix   Complexity   

Complex Class

Complex classes like SchemaValidator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SchemaValidator, and based on these observations, apply Extract Interface, too.

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\Tools;
21
22
use Doctrine\ORM\EntityManagerInterface;
23
use Doctrine\ORM\Mapping\ClassMetadataInfo;
24
use Doctrine\DBAL\Types\Type;
25
26
/**
27
 * Performs strict validation of the mapping schema
28
 *
29
 * @license     http://www.opensource.org/licenses/mit-license.php MIT
30
 * @link        www.doctrine-project.com
31
 * @since       1.0
32
 * @author      Benjamin Eberlei <[email protected]>
33
 * @author      Guilherme Blanco <[email protected]>
34
 * @author      Jonathan Wage <[email protected]>
35
 * @author      Roman Borschel <[email protected]>
36
 */
37
class SchemaValidator
38
{
39
    /**
40
     * @var EntityManagerInterface
41
     */
42
    private $em;
43
44
    /**
45
     * @param EntityManagerInterface $em
46
     */
47 42
    public function __construct(EntityManagerInterface $em)
48
    {
49 42
        $this->em = $em;
50 42
    }
51
52
    /**
53
     * Checks the internal consistency of all mapping files.
54
     *
55
     * There are several checks that can't be done at runtime or are too expensive, which can be verified
56
     * with this command. For example:
57
     *
58
     * 1. Check if a relation with "mappedBy" is actually connected to that specified field.
59
     * 2. Check if "mappedBy" and "inversedBy" are consistent to each other.
60
     * 3. Check if "referencedColumnName" attributes are really pointing to primary key columns.
61
     *
62
     * @return array
63
     */
64
    public function validateMapping()
65
    {
66
        $errors = [];
67
        $cmf = $this->em->getMetadataFactory();
68
        $classes = $cmf->getAllMetadata();
69
70
        foreach ($classes as $class) {
71
            if ($ce = $this->validateClass($class)) {
72
                $errors[$class->name] = $ce;
73
            }
74
        }
75
76
        return $errors;
77
    }
78
79
    /**
80
     * Validates a single class of the current.
81
     *
82
     * @param ClassMetadataInfo $class
83
     *
84
     * @return array
85
     */
86 42
    public function validateClass(ClassMetadataInfo $class)
87
    {
88 42
        $ce = [];
89 42
        $cmf = $this->em->getMetadataFactory();
90
91 42
        foreach ($class->fieldMappings as $fieldName => $mapping) {
92 41
            if (!Type::hasType($mapping['type'])) {
93 41
                $ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existant type '" . $mapping['type'] . "'.";
94
            }
95
        }
96
97 42
        foreach ($class->associationMappings as $fieldName => $assoc) {
98 39
            if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) {
99
                $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.';
100
101
                return $ce;
102
            }
103
104 39
            if ($assoc['mappedBy'] && $assoc['inversedBy']) {
105
                $ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning.";
106
            }
107
108 39
            $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
109
110 39
            if (isset($assoc['id']) && $targetMetadata->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...
111 1
                $ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " .
112 1
                        "the target entity '". $targetMetadata->name . "' also maps an association as identifier.";
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...
113
            }
114
115 39
            if ($assoc['mappedBy']) {
116 33
                if ($targetMetadata->hasField($assoc['mappedBy'])) {
117
                    $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
118
                            "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association, but as field.";
119
                }
120 33
                if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) {
121
                    $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
122
                            "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist.";
123 33
                } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) {
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 1
                    $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
125 1
                            "bi-directional relationship, but the specified mappedBy association on the target-entity ".
126 1
                            $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
127 1
                            "'inversedBy=\"" . $fieldName . "\"' attribute.";
128 32
                } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) {
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...
129
                    $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
130
                            $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ".
131
                            "inconsistent with each other.";
132
                }
133
            }
134
135 39
            if ($assoc['inversedBy']) {
136 30
                if ($targetMetadata->hasField($assoc['inversedBy'])) {
137
                    $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
138
                            "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association.";
139
                }
140
141 30
                if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) {
142
                    $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
143
                            "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist.";
144 30
                } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) {
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...
145
                    $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ".
146
                            "bi-directional relationship, but the specified mappedBy association on the target-entity ".
147
                            $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
148
                            "'inversedBy' attribute.";
149 30
                } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) {
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...
150
                    $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
151
                            $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ".
152
                            "inconsistent with each other.";
153
                }
154
155
                // Verify inverse side/owning side match each other
156 30
                if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) {
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...
157 30
                    $targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']];
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...
158 30
                    if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE) {
159
                        $ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " .
160
                                "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well.";
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...
161 30
                    } elseif ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY) {
162
                        $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " .
163
                                "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many.";
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...
164 30
                    } elseif ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY) {
165
                        $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " .
166
                                "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well.";
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...
167
                    }
168
                }
169
            }
170
171 39
            if ($assoc['isOwningSide']) {
172 35
                if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) {
173 15
                    $identifierColumns = $class->getIdentifierColumnNames();
174 15
                    foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) {
175 15
                        if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
176
                            $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
177
                                "has to be a primary key column on the target entity class '".$class->name."'.";
178 15
                            break;
179
                        }
180
                    }
181
182 15
                    $identifierColumns = $targetMetadata->getIdentifierColumnNames();
0 ignored issues
show
Bug introduced by
The method getIdentifierColumnNames() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifier()?

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...
183 15
                    foreach ($assoc['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
184 15
                        if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) {
185
                            $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
0 ignored issues
show
Bug introduced by
The variable $joinColumn 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...
186
                                "has to be a primary key column on the target entity class '".$targetMetadata->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...
187 15
                            break;
188
                        }
189
                    }
190
191 15
                    if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) {
0 ignored issues
show
Bug introduced by
The method getIdentifierColumnNames() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifier()?

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...
192 1
                        $ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
193 1
                                "have to contain to ALL identifier columns of the target entity '". $targetMetadata->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...
194 1
                                "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) .
0 ignored issues
show
Bug introduced by
The method getIdentifierColumnNames() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifier()?

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...
195 1
                                "' are missing.";
196
                    }
197
198 15
                    if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) {
199 1
                        $ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
200 1
                                "have to contain to ALL identifier columns of the source entity '". $class->name . "', " .
201 1
                                "however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) .
202 15
                                "' are missing.";
203
                    }
204
205 31
                } elseif ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
206 31
                    $identifierColumns = $targetMetadata->getIdentifierColumnNames();
0 ignored issues
show
Bug introduced by
The method getIdentifierColumnNames() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifier()?

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...
207 31
                    foreach ($assoc['joinColumns'] as $joinColumn) {
208 31
                        if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
209 2
                            $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
210 31
                                    "has to be a primary key column on the target entity class '".$targetMetadata->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...
211
                        }
212
                    }
213
214 31
                    if (count($identifierColumns) != count($assoc['joinColumns'])) {
215 1
                        $ids = [];
216
217 1
                        foreach ($assoc['joinColumns'] as $joinColumn) {
218 1
                            $ids[] = $joinColumn['name'];
219
                        }
220
221 1
                        $ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
222 1
                                "have to match to ALL identifier columns of the target entity '". $targetMetadata->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...
223 1
                                "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) .
0 ignored issues
show
Bug introduced by
The method getIdentifierColumnNames() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifier()?

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...
224 1
                                "' are missing.";
225
                    }
226
                }
227
            }
228
229 39
            if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
230 5
                foreach ($assoc['orderBy'] as $orderField => $orientation) {
231 5
                    if (!$targetMetadata->hasField($orderField) && !$targetMetadata->hasAssociation($orderField)) {
232 1
                        $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " .
233 1
                                $orderField . " that is not a field on the target entity " . $targetMetadata->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...
234 1
                        continue;
235
                    }
236 5
                    if ($targetMetadata->isCollectionValuedAssociation($orderField)) {
237 1
                        $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a field " .
238 1
                                $orderField . " on " . $targetMetadata->name . " that is a collection-valued association.";
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...
239 1
                        continue;
240
                    }
241 5
                    if ($targetMetadata->isAssociationInverseSide($orderField)) {
242 1
                        $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a field " .
243 1
                                $orderField . " on " . $targetMetadata->name . " that is the inverse side of an association.";
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...
244 39
                        continue;
245
                    }
246
                }
247
            }
248
        }
249
250 42
        foreach ($class->subClasses as $subClass) {
251 6
            if (!in_array($class->name, class_parents($subClass))) {
252
                $ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ".
253 6
                        "of '" . $class->name . "' but these entities are not related through inheritance.";
254
            }
255
        }
256
257 42
        return $ce;
258
    }
259
260
    /**
261
     * Checks if the Database Schema is in sync with the current metadata state.
262
     *
263
     * @return bool
264
     */
265
    public function schemaInSyncWithMetadata()
266
    {
267
        $schemaTool = new SchemaTool($this->em);
268
269
        $allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
270
271
        return count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0;
272
    }
273
}
274