Completed
Pull Request — 1.1 (#48)
by Patrick
19:04 queued 16:31
created

FluentDriver::loadPaths()   C

Complexity

Conditions 11
Paths 20

Size

Total Lines 58
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 11.0176

Importance

Changes 0
Metric Value
dl 0
loc 58
ccs 36
cts 38
cp 0.9474
rs 6.4179
c 0
b 0
f 0
cc 11
eloc 30
nc 20
nop 1
crap 11.0176

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
namespace LaravelDoctrine\Fluent;
4
5
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
6
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
7
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
8
use Doctrine\ORM\Mapping\MappingException;
9
use FilesystemIterator;
10
use InvalidArgumentException;
11
use LaravelDoctrine\Fluent\Builders\Builder;
12
use LaravelDoctrine\Fluent\Mappers\MapperSet;
13
use RecursiveDirectoryIterator;
14
use RecursiveIteratorIterator;
15
use RecursiveRegexIterator;
16
use ReflectionClass;
17
use RegexIterator;
18
19
class FluentDriver implements MappingDriver
20
{
21
    /**
22
     * @var MapperSet
23
     */
24
    protected $mappers;
25
26
    /**
27
     * @var callable
28
     */
29
    protected $fluentFactory;
30
31
    /**
32
     * The file extension of mapping documents.
33
     *
34
     * @var string
35
     */
36
    protected $fileExtension = '.php';
37
38
    /**
39
     * Initializes a new FileDriver that looks in the given path(s) for mapping
40
     * documents and operates in the specified operating mode.
41
     *
42
     * @param string[] $mappings
43
     * @param array    $paths
44
     *
45
     * @throws MappingException
46
     */
47
    public function __construct(array $mappings = [], array $paths = [])
48
    {
49 20
        $this->fluentFactory = function (ClassMetadata $metadata) {
50 20
            return new Builder(new ClassMetadataBuilder($metadata));
51
        };
52
53 380
        $this->mappers = new MapperSet();
54
55 380
        if (!empty($paths)) {
56 4
            $this->loadPaths($paths);
57 1
        }
58
59 380
        if (!empty($mappings)) {
60 316
            $this->addMappings($mappings);
61 79
        }
62 380
    }
63
64
    /**
65
     * Loads the metadata for the specified class into the provided container.
66
     *
67
     * @param string        $className
68
     * @param ClassMetadata $metadata
69
     */
70 28
    public function loadMetadataForClass($className, ClassMetadata $metadata)
71
    {
72 28
        $this->mappers->getMapperFor($className)->map(
73 24
            $this->getFluent($metadata)
74 6
        );
75 24
    }
76
77
    /**
78
     * Gets the names of all mapped classes known to this driver.
79
     *
80
     * @throws MappingException
81
     *
82
     * @return string[] The names of all mapped classes known to this driver.
83
     */
84 56
    public function getAllClassNames()
85
    {
86 56
        return $this->mappers->getClassNames();
87
    }
88
89
    /**
90
     * Returns whether the class with the specified name should have its metadata loaded.
91
     * This is only the case if it is either mapped as an Entity or a MappedSuperclass.
92
     *
93
     * @param string $className
94
     *
95
     * @return bool
96
     */
97 24
    public function isTransient($className)
98
    {
99
        return
100 24
            !$this->mappers->hasMapperFor($className) ||
101 24
            $this->mappers->getMapperFor($className)->isTransient();
102
    }
103
104
    /**
105
     * @param array $paths
106
     *
107
     * @throws MappingException
108
     */
109 12
    public function loadPaths(array $paths)
110
    {
111 12
        $includedFiles = [];
112
113 12
        foreach ($paths as $path) {
114 12
            if (!is_dir($path)) {
115 4
                throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
116
            }
117
118 8
            $iterator = new RegexIterator(
119 8
                new RecursiveIteratorIterator(
120 8
                    new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
121 6
                    RecursiveIteratorIterator::LEAVES_ONLY
122 2
                ),
123 8
                '/^.+'.preg_quote($this->fileExtension).'$/i',
124 6
                RecursiveRegexIterator::GET_MATCH
125 2
            );
126
127 8
            foreach ($iterator as $file) {
128 8
                $sourceFile = $file[0];
129
130 8
                if (!preg_match('(^phar:)i', $sourceFile)) {
131 8
                    $sourceFile = realpath($sourceFile);
132 2
                }
133
134 8
                require_once $sourceFile;
135
136 8
                $includedFiles[] = $sourceFile;
137 2
            }
138
139 8
            $declared = get_declared_classes();
140
141 8
            foreach ($declared as $className) {
142 8
                $rc = new ReflectionClass($className);
143 8
                $sourceFile = $rc->getFileName();
144
145 8
                if (!in_array($sourceFile, $includedFiles)) {
146 8
                    continue;
147
                }
148
149 8
                if ($rc->isAbstract() || $rc->isInterface()) {
150
                    continue;
151
                }
152
153 8
                if (!$rc->implementsInterface(Mapping::class)) {
154
                    continue;
155
                }
156
157 8
                if ($this->isTransient($className)) {
158
159
                    /** @var Mapping $mapping */
160 8
                    $mapping = $rc->newInstanceWithoutConstructor();
161
162 8
                    $this->addMapping($mapping);
163 2
                }
164 2
            }
165 2
        }
166 8
    }
167
168
    /**
169
     * @param string[] $mappings
170
     *
171
     * @throws InvalidArgumentException
172
     */
173 328
    public function addMappings(array $mappings = [])
174
    {
175 328
        foreach ($mappings as $class) {
176 328
            if (!class_exists($class)) {
177 4
                throw new InvalidArgumentException("Mapping class [{$class}] does not exist");
178
            }
179
180 324
            $mapping = new $class();
181
182 324
            if (!$mapping instanceof Mapping) {
183 4
                throw new InvalidArgumentException("Mapping class [{$class}] should implement ".Mapping::class);
184
            }
185
186 320
            $this->addMapping($mapping);
187 80
        }
188 320
    }
189
190
    /**
191
     * @param Mapping $mapping
192
     *
193
     * @throws MappingException
194
     *
195
     * @return void
196
     */
197 356
    public function addMapping(Mapping $mapping)
198
    {
199 356
        $this->mappers->add($mapping);
200 356
    }
201
202
    /**
203
     * @return MapperSet
204
     */
205 24
    public function getMappers()
206
    {
207 24
        return $this->mappers;
208
    }
209
210
    /**
211
     * Override the default Fluent factory method with a custom one.
212
     * Use this to implement your own Fluent builder.
213
     * The method will receive a ClassMetadata object as its only argument.
214
     *
215
     * @param callable $factory
216
     */
217 4
    public function setFluentFactory(callable $factory)
218
    {
219 4
        $this->fluentFactory = $factory;
220 4
    }
221
222
    /**
223
     * @param ClassMetadata $metadata
224
     *
225
     * @return Fluent
226
     */
227 24
    protected function getFluent(ClassMetadata $metadata)
228
    {
229 24
        return call_user_func($this->fluentFactory, $metadata);
230
    }
231
}
232