Completed
Pull Request — master (#6500)
by Mathew
17:49 queued 08:41
created

SchemaValidator   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 72.52%

Importance

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

4 Methods

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

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 49
    public function __construct(EntityManagerInterface $em)
48
    {
49 49
        $this->em = $em;
50 49
    }
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 6
    public function validateMapping()
65
    {
66 6
        $errors = [];
67 6
        $cmf = $this->em->getMetadataFactory();
68 6
        $classes = $cmf->getAllMetadata();
69
70 6
        foreach ($classes as $class) {
71 6
            if ($ce = $this->validateClass($class)) {
72 6
                $errors[$class->name] = $ce;
73
            }
74
        }
75
76 6
        return $errors;
77
    }
78
79
    /**
80
     * Validates a single class of the current.
81
     *
82
     * @param ClassMetadataInfo $class
83
     *
84
     * @return array
85
     */
86 49
    public function validateClass(ClassMetadataInfo $class)
87
    {
88 49
        $ce = [];
89 49
        $cmf = $this->em->getMetadataFactory();
90
91 49
        foreach ($class->fieldMappings as $fieldName => $mapping) {
92 48
            if (!Type::hasType($mapping['type'])) {
93 48
                $ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existent type '" . $mapping['type'] . "'.";
94
            }
95
        }
96
97 49
        foreach ($class->associationMappings as $fieldName => $assoc) {
98 46
            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 46
            if ($assoc['mappedBy'] && $assoc['inversedBy']) {
105
                $ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning.";
106
            }
107
108 46
            $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
109
110 46
            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 46
            if ($assoc['mappedBy']) {
116 40
                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 40
                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 40
                } 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 39
                } 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 46
            if ($assoc['inversedBy']) {
136 37
                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 37
                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 37
                } 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 37
                } 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 37
                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 37
                    $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 37
                    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 37
                    } 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 37
                    } 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 46
            if ($assoc['isOwningSide']) {
172 42
                if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) {
173 22
                    $identifierColumns = $class->getIdentifierColumnNames();
174 22
                    foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) {
175 22
                        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 22
                            break;
179
                        }
180
                    }
181
182 22
                    $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 22
                    foreach ($assoc['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
184 22
                        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 22
                            break;
188
                        }
189
                    }
190
191 22
                    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 22
                    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 22
                                "' are missing.";
203
                    }
204
205 38
                } elseif ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
206 38
                    $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 38
                    foreach ($assoc['joinColumns'] as $joinColumn) {
208 38
                        if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
209 2
                            $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
210 38
                                    "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 38
                    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 46
            if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
230 11
                foreach ($assoc['orderBy'] as $orderField => $orientation) {
231 11
                    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 11
                    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 11
                    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 46
                        continue;
245
                    }
246
                }
247
            }
248
        }
249
250 49
        foreach ($class->subClasses as $subClass) {
251 13
            if (!in_array($class->name, class_parents($subClass))) {
252
                $ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ".
253 13
                        "of '" . $class->name . "' but these entities are not related through inheritance.";
254
            }
255
        }
256
257 49
        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