Completed
Pull Request — master (#14)
by Pavel
29:07 queued 23:49
created

Mapping/Driver/YmlMetadataDriver.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Bankiru\Api\Doctrine\Mapping\Driver;
4
5
use Bankiru\Api\Doctrine\Exception\MappingException;
6
use Bankiru\Api\Doctrine\Mapping\ApiMetadata;
7
use Bankiru\Api\Doctrine\Mapping\EntityMetadata;
8
use Bankiru\Api\Doctrine\Rpc\Method\EntityMethodProvider;
9
use Bankiru\Api\Doctrine\Rpc\Method\MethodProvider;
10
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
11
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
12
use Symfony\Component\Yaml\Exception\ParseException;
13
use Symfony\Component\Yaml\Yaml;
14
15
class YmlMetadataDriver extends FileDriver
16
{
17
    /**
18
     * Loads the metadata for the specified class into the provided container.
19
     *
20
     * @param string                       $className
21
     * @param EntityMetadata|ClassMetadata $metadata
22
     *
23
     * @return void
24
     * @throws MappingException
25
     */
26
    public function loadMetadataForClass($className, ClassMetadata $metadata)
27
    {
28
        $element = $this->getElement($className);
29
30
        switch ($element['type']) {
31
            case 'entity':
32
                if (array_key_exists('repositoryClass', $element)) {
33
                    $metadata->setCustomRepositoryClass($element['repositoryClass']);
34
                }
35
                break;
36
            case 'mappedSuperclass':
37
                $metadata->isMappedSuperclass = true;
38
                $metadata->setCustomRepositoryClass(
39
                    array_key_exists('repositoryClass', $element) ? $element['repositoryClass'] : null
40
                );
41
                break;
42
        }
43
44
        // Configure API
45
        if (array_key_exists('api', $element)) {
46
            if (array_key_exists('name', $element['api'])) {
47
                $metadata->apiName = $element['api']['name'];
0 ignored issues
show
Accessing apiName 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...
48
            }
49
        }
50
51
        // Configure Client
52
        if (array_key_exists('client', $element)) {
53
            if (array_key_exists('name', $element['client'])) {
54
                $metadata->clientName = $element['client']['name'];
55
            }
56
57
            $methodProvider = null;
58
            if (array_key_exists('methods', $element['client'])) {
59
                $methodProvider = new MethodProvider($element['client']['methods']);
60
            }
61
            if (array_key_exists('entityPath', $element['client'])) {
62
                $pathSeparator  =
63
                    array_key_exists('entityPathSeparator', $element['client']) ?
64
                        $element['client']['entityPathSeparator'] :
65
                        EntityMethodProvider::DEFAULT_PATH_SEPARATOR;
66
                $methodProvider =
67
                    new EntityMethodProvider($element['client']['entityPath'], $pathSeparator, $methodProvider);
68
            }
69
70
            if (null === $methodProvider && null === $metadata->methodProvider) {
0 ignored issues
show
Accessing methodProvider 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...
71
                throw MappingException::noMethods();
72
            }
73
74
            if (null !== $methodProvider) {
75
                $metadata->methodProvider = $methodProvider;
76
            }
77
        }
78
79
        // Configure fields
80
        if (array_key_exists('fields', $element)) {
81
            foreach ($element['fields'] as $field => $mapping) {
82
                $mapping = $this->fieldToArray($field, $mapping);
83
                $metadata->mapField($mapping);
84
            }
85
        }
86
87
        // Configure identifiers
88
        $associationIds = [];
89
        if (array_key_exists('id', $element)) {
90
            // Evaluate identifier settings
91
            foreach ($element['id'] as $name => $idElement) {
92
                if (isset($idElement['associationKey']) && (bool)$idElement['associationKey'] === true) {
93
                    $associationIds[$name] = true;
94
                    continue;
95
                }
96
97
                $mapping = $this->fieldToArray($name, $idElement);
98
99
                $mapping['id'] = true;
100
                $metadata->mapField($mapping);
101
            }
102
        }
103
104
        foreach (['oneToOne', 'manyToOne', 'oneToMany'] as $type) {
105
            if (array_key_exists($type, $element)) {
106
                $associations = $element[$type];
107
                foreach ($associations as $name => $association) {
108
                    $this->mapAssociation($metadata, $type, $name, $association, $associationIds);
109
                }
110
            }
111
        }
112
    }
113
114
    /**
115
     * @param EntityMetadata $metadata
116
     * @param string         $type
117
     * @param string         $name
118
     * @param array          $association
119
     * @param int[]          $associationIds
120
     */
121
    protected function mapAssociation(EntityMetadata $metadata, $type, $name, $association, $associationIds)
122
    {
123
        $mapping           = $this->fieldToArray($name, $association);
124
        $mapping['target'] = $association['target'];
125
        if (isset($association['fetch'])) {
126
            $mapping['fetch'] = constant(ApiMetadata::class . '::FETCH_' . $association['fetch']);
127
        }
128
        switch ($type) {
129 View Code Duplication
            case 'oneToOne':
130
                $mapping['type'] = EntityMetadata::ONE_TO_ONE;
131
                if (isset($associationIds[$mapping['field']])) {
132
                    $mapping['id'] = true;
133
                }
134
                if (array_key_exists('mappedBy', $association)) {
135
                    $mapping['mappedBy'] = $association['mappedBy'];
136
                }
137
                if (array_key_exists('inversedBy', $association)) {
138
                    $mapping['inversedBy'] = $association['inversedBy'];
139
                }
140
                $metadata->mapOneToOne($mapping);
141
                break;
142
            case 'manyToOne':
143
                $mapping['type'] = EntityMetadata::MANY_TO_ONE;
144
                if (array_key_exists('inversedBy', $association)) {
145
                    $mapping['inversedBy'] = $association['inversedBy'];
146
                }
147
                $metadata->mapManyToOne($mapping);
148
                break;
149 View Code Duplication
            case 'oneToMany':
150
                $mapping['type'] = EntityMetadata::ONE_TO_MANY;
151
                if (array_key_exists('mappedBy', $association)) {
152
                    $mapping['mappedBy'] = $association['mappedBy'];
153
                }
154
                if (array_key_exists('orderBy', $association)) {
155
                    $mapping['orderBy'] = $association['orderBy'];
156
                }
157
                if (array_key_exists('indexBy', $association)) {
158
                    $mapping['indexBy'] = $association['indexBy'];
159
                }
160
                $metadata->mapOneToMany($mapping);
161
                break;
162
        }
163
    }
164
165
    /**
166
     * Loads a mapping file with the given name and returns a map
167
     * from class/entity names to their corresponding file driver elements.
168
     *
169
     * @param string $file The mapping file to load.
170
     *
171
     * @return array
172
     * @throws ParseException
173
     */
174
    protected function loadMappingFile($file)
175
    {
176
        return Yaml::parse(file_get_contents($file));
177
    }
178
179
    private function fieldToArray($field, $source)
180
    {
181
        $mapping = [
182
            'field'    => $field,
183
            'type'     => 'string',
184
            'nullable' => true,
185
        ];
186
187
        if (array_key_exists('type', $source)) {
188
            $mapping['type'] = $source['type'];
189
        }
190
191
        if (array_key_exists('nullable', $source)) {
192
            $mapping['nullable'] = $source['nullable'];
193
        }
194
195
        if (array_key_exists('api_field', $source)) {
196
            $mapping['api_field'] = $source['api_field'];
197
        }
198
199
        return $mapping;
200
    }
201
}
202
203