Skip to content

Commit eee1bfc

Browse files
Add path loader to fluent driver
1 parent 99f1182 commit eee1bfc

File tree

4 files changed

+187
-8
lines changed

4 files changed

+187
-8
lines changed

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
language: php
22

33
php:
4-
- 5.5
54
- 5.6
65
- 7.0
76
- 7.1

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
}
2424
],
2525
"require": {
26-
"php": ">=5.5.0",
26+
"php": ">=5.6.0",
2727
"doctrine/orm": "2.5.*",
2828
"doctrine/inflector": "^1.1"
2929
},
3030
"require-dev": {
31-
"phpunit/phpunit": "~4.0",
31+
"phpunit/phpunit": "~5.7",
3232
"mockery/mockery": "~0.9",
3333
"beberlei/DoctrineExtensions": "~1.0",
3434
"zf1/zend-date": "~1.12",

src/FluentDriver.php

+91-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
77
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
88
use Doctrine\ORM\Mapping\MappingException;
9+
use FilesystemIterator;
910
use InvalidArgumentException;
1011
use LaravelDoctrine\Fluent\Builders\Builder;
1112
use LaravelDoctrine\Fluent\Mappers\MapperSet;
13+
use RecursiveDirectoryIterator;
14+
use RecursiveIteratorIterator;
15+
use RecursiveRegexIterator;
16+
use ReflectionClass;
17+
use RegexIterator;
1218

1319
class FluentDriver implements MappingDriver
1420
{
@@ -22,20 +28,37 @@ class FluentDriver implements MappingDriver
2228
*/
2329
protected $fluentFactory;
2430

31+
/**
32+
* The file extension of mapping documents.
33+
*
34+
* @var string
35+
*/
36+
protected $fileExtension = '.php';
37+
2538
/**
2639
* Initializes a new FileDriver that looks in the given path(s) for mapping
2740
* documents and operates in the specified operating mode.
2841
*
2942
* @param string[] $mappings
43+
* @param array $paths
44+
*
45+
* @throws MappingException
3046
*/
31-
public function __construct(array $mappings = [])
47+
public function __construct(array $mappings = [], array $paths = [])
3248
{
3349
$this->fluentFactory = function (ClassMetadata $metadata) {
3450
return new Builder(new ClassMetadataBuilder($metadata));
3551
};
3652

3753
$this->mappers = new MapperSet();
38-
$this->addMappings($mappings);
54+
55+
if (!empty($paths)) {
56+
$this->loadPaths($paths);
57+
}
58+
59+
if (!empty($mappings)) {
60+
$this->addMappings($mappings);
61+
}
3962
}
4063

4164
/**
@@ -78,8 +101,74 @@ public function isTransient($className)
78101
$this->mappers->getMapperFor($className)->isTransient();
79102
}
80103

104+
/**
105+
* @param array $paths
106+
*
107+
* @throws MappingException
108+
*/
109+
public function loadPaths(array $paths)
110+
{
111+
$includedFiles = [];
112+
113+
foreach ($paths as $path) {
114+
if (!is_dir($path)) {
115+
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
116+
}
117+
118+
$iterator = new RegexIterator(
119+
new RecursiveIteratorIterator(
120+
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
121+
RecursiveIteratorIterator::LEAVES_ONLY
122+
),
123+
'/^.+'.preg_quote($this->fileExtension).'$/i',
124+
RecursiveRegexIterator::GET_MATCH
125+
);
126+
127+
foreach ($iterator as $file) {
128+
$sourceFile = $file[0];
129+
130+
if (!preg_match('(^phar:)i', $sourceFile)) {
131+
$sourceFile = realpath($sourceFile);
132+
}
133+
134+
require_once $sourceFile;
135+
136+
$includedFiles[] = $sourceFile;
137+
}
138+
139+
$declared = get_declared_classes();
140+
141+
foreach ($declared as $className) {
142+
$rc = new ReflectionClass($className);
143+
$sourceFile = $rc->getFileName();
144+
145+
if (!in_array($sourceFile, $includedFiles)) {
146+
continue;
147+
}
148+
149+
if ($rc->isAbstract() || $rc->isInterface()) {
150+
continue;
151+
}
152+
153+
if (!$rc->implementsInterface(Mapping::class)) {
154+
continue;
155+
}
156+
157+
if ($this->isTransient($className)) {
158+
159+
/** @var Mapping $mapping */
160+
$mapping = $rc->newInstanceWithoutConstructor();
161+
162+
$this->addMapping($mapping);
163+
}
164+
}
165+
}
166+
}
167+
81168
/**
82169
* @param string[] $mappings
170+
*
171+
* @throws InvalidArgumentException
83172
*/
84173
public function addMappings(array $mappings = [])
85174
{

tests/FluentDriverTest.php

+94-3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public function test_it_should_load_metadata_for_mappings_passed_as_constructor_
8282
StubEntity::class,
8383
new ClassMetadataInfo(StubEntity::class)
8484
);
85+
8586
$this->assertInstanceOf(
8687
EntityMapper::class,
8788
$driver->getMappers()->getMapperFor(StubEntity::class)
@@ -91,6 +92,7 @@ public function test_it_should_load_metadata_for_mappings_passed_as_constructor_
9192
StubEmbeddable::class,
9293
new ClassMetadataInfo(StubEmbeddable::class)
9394
);
95+
9496
$this->assertInstanceOf(
9597
EmbeddableMapper::class,
9698
$driver->getMappers()->getMapperFor(StubEmbeddable::class)
@@ -100,6 +102,7 @@ public function test_it_should_load_metadata_for_mappings_passed_as_constructor_
100102
StubMappedSuperClass::class,
101103
new ClassMetadataInfo(StubMappedSuperClass::class)
102104
);
105+
103106
$this->assertInstanceOf(
104107
MappedSuperClassMapper::class,
105108
$driver->getMappers()->getMapperFor(StubMappedSuperClass::class)
@@ -126,9 +129,96 @@ public function test_can_add_array_of_new_mappings()
126129
);
127130
}
128131

132+
public function test_can_load_mappings_through_file_path()
133+
{
134+
$driver = new FluentDriver([], [__DIR__ . '/' . 'Stubs/Mappings']);
135+
136+
$this->assertContains(
137+
StubEntity::class,
138+
$driver->getAllClassNames()
139+
);
140+
141+
$this->assertContains(
142+
StubEmbeddable::class,
143+
$driver->getAllClassNames()
144+
);
145+
146+
$this->assertContains(
147+
StubMappedSuperClass::class,
148+
$driver->getAllClassNames()
149+
);
150+
151+
$driver->loadMetadataForClass(
152+
StubEntity::class,
153+
new ClassMetadataInfo(StubEntity::class)
154+
);
155+
156+
$this->assertInstanceOf(
157+
EntityMapper::class,
158+
$driver->getMappers()->getMapperFor(StubEntity::class)
159+
);
160+
161+
$driver->loadMetadataForClass(
162+
StubEmbeddable::class,
163+
new ClassMetadataInfo(StubEmbeddable::class)
164+
);
165+
166+
$this->assertInstanceOf(
167+
EmbeddableMapper::class,
168+
$driver->getMappers()->getMapperFor(StubEmbeddable::class)
169+
);
170+
171+
$driver->loadMetadataForClass(
172+
StubMappedSuperClass::class,
173+
new ClassMetadataInfo(StubMappedSuperClass::class)
174+
);
175+
176+
$this->assertInstanceOf(
177+
MappedSuperClassMapper::class,
178+
$driver->getMappers()->getMapperFor(StubMappedSuperClass::class)
179+
);
180+
}
181+
182+
public function test_can_load_paths()
183+
{
184+
$driver = new FluentDriver();
185+
$driver->loadPaths([__DIR__ . '/' . 'Stubs/Mappings']);
186+
187+
$this->assertContains(
188+
StubEntity::class,
189+
$driver->getAllClassNames()
190+
);
191+
192+
$this->assertContains(
193+
StubEmbeddable::class,
194+
$driver->getAllClassNames()
195+
);
196+
197+
$this->assertContains(
198+
StubMappedSuperClass::class,
199+
$driver->getAllClassNames()
200+
);
201+
}
202+
203+
public function test_loading_by_paths_throws_exception_if_dir_not_exists()
204+
{
205+
$folder = __DIR__ . '/' . 'Stubs/non-existing-folder';
206+
207+
$this->setExpectedException(
208+
MappingException::class,
209+
'File mapping drivers must have a valid directory path, however the given path [' . $folder . '] seems to be incorrect!'
210+
);
211+
212+
$driver = new FluentDriver();
213+
$driver->loadPaths([$folder]);
214+
215+
$driver->getAllClassNames();
216+
}
217+
129218
public function test_the_given_mapping_class_should_exist()
130219
{
131-
$this->setExpectedException(\InvalidArgumentException::class, 'Mapping class [Tests\DoesnExist] does not exist');
220+
$this->setExpectedException(\InvalidArgumentException::class,
221+
'Mapping class [Tests\DoesnExist] does not exist');
132222

133223
$driver = new FluentDriver;
134224

@@ -139,7 +229,8 @@ public function test_the_given_mapping_class_should_exist()
139229

140230
public function test_the_given_mapping_class_should_implement_mapping()
141231
{
142-
$this->setExpectedException(\InvalidArgumentException::class, 'Mapping class [Tests\Stubs\Entities\StubEntity] should implement LaravelDoctrine\Fluent\Mapping');
232+
$this->setExpectedException(\InvalidArgumentException::class,
233+
'Mapping class [Tests\Stubs\Entities\StubEntity] should implement LaravelDoctrine\Fluent\Mapping');
143234

144235
$driver = new FluentDriver;
145236

@@ -223,7 +314,7 @@ public function test_allow_other_fluent_implementations()
223314
return new CustomBuilder(new ClassMetadataBuilder($metadata));
224315
});
225316

226-
$mapping = $this->getMock(EntityMapping::class);
317+
$mapping = $this->createMock(EntityMapping::class);
227318
$mapping->expects($this->once())->method('map')->with($this->isInstanceOf(CustomBuilder::class));
228319

229320
$driver->getMappers()->addMapper('fake', new EntityMapper($mapping));

0 commit comments

Comments
 (0)