diff --git a/config/ddd.php b/config/ddd.php index 9b99c85..ffef9d2 100644 --- a/config/ddd.php +++ b/config/ddd.php @@ -63,41 +63,6 @@ 'scope' => 'Scopes', ], - /* - * The folder where the domain cache files will be stored. - */ - 'cache_directory' => 'bootstrap/cache', - - 'autoload' => [ - /* - | Autoload service providers from the domain namespace. - | By default, it loads any non-abstract class inside the domain layer extending Illuminate\Support\ServiceProvider. - | For example: Domain/Invoicing/Providers/InvoicingServiceProvider.php or Domain/Invoicing/InvoicingServiceProvider.php - */ - 'providers' => true, - - /* - | Autoload commands from the domain namespace. - | By default, it loads any non-abstract class inside the domain layer extending Illuminate\Console\Command. - | For example: Domain/Invoicing/Commands/CreateInvoiceCommand.php - */ - 'commands' => true, - - /* - | Autoload policies from the domain namespace. - | By default, it uses the configured `ddd.namespaces.policy` namespace to guess the policy name. - | For example: Domain/Invoicing/Policies/InvoicePolicy.php - */ - 'policies' => true, - - /* - | Autoload factories from the domain namespace. - | By default, it uses the configured `ddd.namespaces.factory` namespace to guess the factory name. - | For example: Domain/Invoicing/Database/Factories/InvoiceFactory.php - */ - 'factories' => true, - ], - /* |-------------------------------------------------------------------------- | Base Model @@ -146,4 +111,31 @@ | */ 'base_action' => null, + + /* + |-------------------------------------------------------------------------- + | Autoloading + |-------------------------------------------------------------------------- + | + | Configure whether domain providers, commands, policies, and factories + | should be auto-discovered and registered. + | + */ + 'autoload' => [ + 'providers' => true, + 'commands' => true, + 'policies' => true, + 'factories' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Caching + |-------------------------------------------------------------------------- + | + | The folder where the domain cache files will be stored. Used for domain + | autoloading. + | + */ + 'cache_directory' => 'bootstrap/cache', ]; diff --git a/src/Commands/CacheClearCommand.php b/src/Commands/CacheClearCommand.php new file mode 100644 index 0000000..5b7f668 --- /dev/null +++ b/src/Commands/CacheClearCommand.php @@ -0,0 +1,20 @@ +info('Domain cache cleared.'); + } +} diff --git a/src/Commands/CacheCommand.php b/src/Commands/CacheCommand.php new file mode 100644 index 0000000..5e137e6 --- /dev/null +++ b/src/Commands/CacheCommand.php @@ -0,0 +1,24 @@ +info('Cached domain providers.'); + + DomainAutoloader::cacheCommands(); + + $this->info('Cached domain commands.'); + } +} diff --git a/src/Commands/DomainFactoryMakeCommand.php b/src/Commands/DomainFactoryMakeCommand.php index d870527..fe06086 100644 --- a/src/Commands/DomainFactoryMakeCommand.php +++ b/src/Commands/DomainFactoryMakeCommand.php @@ -92,8 +92,8 @@ protected function preparePlaceholders(): array // ]); return [ - 'namespacedModel' => $domainModel->fqn, - 'model' => class_basename($domainModel->fqn), + 'namespacedModel' => $domainModel->fullyQualifiedName, + 'model' => class_basename($domainModel->fullyQualifiedName), 'factory' => $this->getFactoryName(), 'namespace' => $domainFactory->namespace, ]; diff --git a/src/LaravelDDDServiceProvider.php b/src/LaravelDDDServiceProvider.php index c7cb0bf..4994536 100644 --- a/src/LaravelDDDServiceProvider.php +++ b/src/LaravelDDDServiceProvider.php @@ -22,6 +22,8 @@ public function configurePackage(Package $package): void ->hasConfigFile() ->hasCommands([ Commands\InstallCommand::class, + Commands\CacheCommand::class, + Commands\CacheClearCommand::class, Commands\DomainListCommand::class, Commands\DomainModelMakeCommand::class, Commands\DomainFactoryMakeCommand::class, @@ -64,6 +66,7 @@ public function packageBooted() public function packageRegistered() { (new DomainAutoloader())->autoload(); + Event::subscribe(CacheClearSubscriber::class); } } diff --git a/src/Support/Domain.php b/src/Support/Domain.php index fac0b26..3f1d7df 100644 --- a/src/Support/Domain.php +++ b/src/Support/Domain.php @@ -109,7 +109,7 @@ public function object(string $type, string $name): DomainObject name: $name, domain: $this->domain, namespace: $namespace, - fqn: $namespace.'\\'.$name, + fullyQualifiedName: $namespace.'\\'.$name, path: $this->path($namespace.'\\'.$name), type: $type ); @@ -128,7 +128,7 @@ public function factory(string $name): DomainObject name: $name, domain: $this->domain, namespace: $this->namespace->factories, - fqn: $this->namespace->factories.'\\'.$name, + fullyQualifiedName: $this->namespace->factories.'\\'.$name, path: str("database/factories/{$this->domainWithSubdomain}/{$name}.php") ->replace(['\\', '/'], DIRECTORY_SEPARATOR) ->toString(), diff --git a/src/Support/DomainAutoloader.php b/src/Support/DomainAutoloader.php index 300c42e..ca75ec8 100644 --- a/src/Support/DomainAutoloader.php +++ b/src/Support/DomainAutoloader.php @@ -9,7 +9,6 @@ use Illuminate\Foundation\Application; use Illuminate\Support\Arr; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; @@ -21,90 +20,58 @@ class DomainAutoloader { - protected string $cacheDirectory; - - protected mixed $config; - public function __construct() { - $this->config = config('ddd'); - $this->cacheDirectory = $this->configValue('cache_directory') ?? 'bootstrap/cache/ddd'; - } - - protected function configValue($path) - { - return data_get($this->config, $path); + // } public function autoload(): void { - if ($value = $this->configValue('autoload.providers')) { - $this->registerProviders($value); + if (! config()->has('ddd.autoload')) { + return; } - if ($value = $this->configValue('autoload.commands')) { - $this->registerCommands($value); + $this->handleProviders(); + + if (app()->runningInConsole()) { + $this->handleCommands(); } - if ($this->configValue('autoload.policies') === true) { - $this->registerPolicies(); + if (config('ddd.autoload.policies') === true) { + $this->handlePolicies(); } - if ($this->configValue('autoload.factories') === true) { - $this->registerFactories(); + if (config('ddd.autoload.factories') === true) { + $this->handleFactories(); } } - protected function normalizePaths($path): array + protected static function normalizePaths($path): array { return collect($path) ->filter(fn ($path) => is_dir($path)) ->toArray(); } - protected function registerProviders(bool|string|array|null $path = null): void + protected function handleProviders(): void { - $paths = $this->normalizePaths($path === true ? app()->basePath(DomainResolver::domainPath()) : $path); - - $serviceProviders = $this->remember('ddd-domain-service-providers', static function () use ($paths) { - if (empty($paths)) { - return []; - } + $providers = DomainCache::has('domain-providers') + ? DomainCache::get('domain-providers') + : static::discoverProviders(); - $finder = Finder::create()->files()->in($paths); - - return Lody::classesFromFinder($finder) - ->isNotAbstract() - ->isInstanceOf(ServiceProvider::class) - ->toArray(); - }); - - $app = app(); - - foreach ($serviceProviders as $serviceProvider) { - $app->register($serviceProvider); + foreach ($providers as $provider) { + app()->register($provider); } } - protected function registerCommands(bool|string|array|null $path = null): void + protected function handleCommands(): void { - $paths = $this->normalizePaths($path === true ? app()->basePath(DomainResolver::domainPath()) : $path); + $commands = DomainCache::has('domain-commands') + ? DomainCache::get('domain-commands') + : static::discoverCommands(); - $commands = $this->remember('ddd-domain-commands', static function () use ($paths) { - if (empty($paths)) { - return []; - } - - $finder = Finder::create()->files()->in($paths); - - return Lody::classesFromFinder($finder) - ->isNotAbstract() - ->isInstanceOf(Command::class) - ->toArray(); - }); - - foreach ($commands as $class) { - $this->registerCommand($class); + foreach ($commands as $command) { + $this->registerCommand($command); } } @@ -115,13 +82,13 @@ protected function registerCommand($class) }); } - protected function registerPolicies(): void + protected function handlePolicies(): void { Gate::guessPolicyNamesUsing(static function (string $class): array|string { if ($model = DomainObject::fromClass($class, 'model')) { return (new Domain($model->domain)) ->object('policy', "{$model->name}Policy") - ->fqn; + ->fullyQualifiedName; } $classDirname = str_replace('/', '\\', dirname(str_replace('\\', '/', $class))); @@ -138,7 +105,7 @@ protected function registerPolicies(): void }); } - protected function registerFactories(): void + protected function handleFactories(): void { Factory::guessFactoryNamesUsing(function (string $modelName) { if (DomainResolver::isDomainClass($modelName)) { @@ -155,30 +122,60 @@ protected function registerFactories(): void }); } - protected function remember($fileName, $callback) + protected static function discoverProviders(): array { - // The cache is not available during booting, so we need to roll our own file based cache - $cacheFilePath = base_path($this->cacheDirectory.'/'.$fileName.'.php'); + $configValue = config('ddd.autoload.providers'); - $data = file_exists($cacheFilePath) ? include $cacheFilePath : null; + if ($configValue === false) { + return []; + } - if (is_null($data)) { - $data = $callback(); + $paths = static::normalizePaths( + $configValue === true ? app()->basePath(DomainResolver::domainPath()) : $configValue + ); - file_put_contents( - $cacheFilePath, - 'files()->in($paths)) + ->isNotAbstract() + ->isInstanceOf(ServiceProvider::class) + ->toArray(); } - public static function clearCache() + protected static function discoverCommands(): array { - $files = glob(base_path(config('ddd.cache_directory').'/ddd-*.php')); + $configValue = config('ddd.autoload.commands'); + + if ($configValue === false) { + return []; + } + + $paths = static::normalizePaths( + $configValue === true ? + app()->basePath(DomainResolver::domainPath()) + : $configValue + ); + + if (empty($paths)) { + return []; + } - File::delete($files); + return Lody::classesFromFinder(Finder::create()->files()->in($paths)) + ->isNotAbstract() + ->isInstanceOf(Command::class) + ->toArray(); + } + + public static function cacheProviders(): void + { + DomainCache::set('domain-providers', static::discoverProviders()); + } + + public static function cacheCommands(): void + { + DomainCache::set('domain-commands', static::discoverCommands()); } protected static function appNamespace() diff --git a/src/Support/DomainCache.php b/src/Support/DomainCache.php new file mode 100644 index 0000000..909d1b1 --- /dev/null +++ b/src/Support/DomainCache.php @@ -0,0 +1,63 @@ +setupTestApplication(); - - DomainAutoloader::clearCache(); }); describe('without autoload', function () { + beforeEach(function () { + Config::set('ddd.autoload.commands', false); + + $this->setupTestApplication(); + + $this->afterApplicationCreated(function () { + (new DomainAutoloader())->autoload(); + }); + }); + it('does not register the command', function () { expect(class_exists('Domain\Invoicing\Commands\InvoiceDeliver'))->toBeTrue(); expect(fn () => Artisan::call('invoice:deliver'))->toThrow(CommandNotFoundException::class); @@ -24,57 +30,73 @@ describe('with autoload', function () { beforeEach(function () { + Config::set('ddd.autoload.commands', true); + + $this->setupTestApplication(); + $this->afterApplicationCreated(function () { (new DomainAutoloader())->autoload(); }); }); - it('registers the command', function () { + it('registers existing commands', function () { + $command = 'invoice:deliver'; + + expect(collect(Artisan::all())) + ->has($command) + ->toBeTrue(); + expect(class_exists('Domain\Invoicing\Commands\InvoiceDeliver'))->toBeTrue(); - Artisan::call('invoice:deliver'); + Artisan::call($command); expect(Artisan::output())->toContain('Invoice delivered!'); }); - it('recognizes new commands created afterwards', function () { - expect(class_exists('Domain\Invoicing\Commands\InvoiceVoid'))->toBeFalse(); + it('registers newly created commands', function () { + $command = 'app:invoice-void'; + + expect(collect(Artisan::all())) + ->has($command) + ->toBeFalse(); Artisan::call('ddd:command', [ 'name' => 'InvoiceVoid', '--domain' => 'Invoicing', ]); - $filepath = base_path('src/Domain/Invoicing/Commands/InvoiceVoid.php'); + expect(collect(Artisan::all())) + ->has($command) + ->toBeTrue(); - expect(file_exists($filepath))->toBeTrue(); + $this->artisan($command)->assertSuccessful(); + })->skip("Can't get this to work, might not be test-able without a real app environment."); +}); + +describe('caching', function () { + beforeEach(function () { + Config::set('ddd.autoload.commands', true); - $class = 'Domain\Invoicing\Commands\InvoiceVoid'; + $this->setupTestApplication(); + }); - // dd( - // [ - // // pre-created files work fine - // 'App\Models\User' => [ - // 'path' => base_path('app/Models/User.php'), - // 'file_exists' => file_exists(base_path('app/Models/User.php')), - // 'class_exists' => class_exists('App\Models\User'), - // ], + it('remembers the last cached state', function () { + DomainCache::set('domain-commands', []); + + $this->afterApplicationCreated(function () { + (new DomainAutoloader())->autoload(); + }); - // 'Domain\Invoicing\Models\Invoice' => [ - // 'path' => base_path('src/Domain/Invoicing/Models/Invoice.php'), - // 'file_exists' => file_exists(base_path('src/Domain/Invoicing/Models/Invoice.php')), - // 'class_exists' => class_exists('Domain\Invoicing\Models\Invoice'), - // ], + // command should not be recognized due to cached empty-state + expect(fn () => Artisan::call('invoice:deliver'))->toThrow(CommandNotFoundException::class); + }); - // // but runtime-created class created but not recognized by class_exists - // $class => [ - // 'path' => $filepath, - // 'file_exists' => file_exists($filepath), - // 'class_exists' => class_exists($class), - // ], - // ], - // ); + it('can bust the cache', function () { + DomainCache::set('domain-commands', []); + DomainCache::clear(); - $instance = new $class(); + $this->afterApplicationCreated(function () { + (new DomainAutoloader())->autoload(); + }); - expect(class_exists($class))->toBeTrue(); - })->markTestIncomplete("Can't get this to work under test environment"); + $this->artisan('invoice:deliver')->assertSuccessful(); + }); }); diff --git a/tests/Autoload/FactoryTest.php b/tests/Autoload/FactoryTest.php index cdae53e..26f366c 100644 --- a/tests/Autoload/FactoryTest.php +++ b/tests/Autoload/FactoryTest.php @@ -57,8 +57,8 @@ }); it('cannot resolve factories that rely on autoloading', function ($modelClass) { - $modelClass::factory(); - })->throws(\Error::class)->with([ + expect(fn () => $modelClass::factory())->toThrow(Error::class); + })->with([ ['Domain\Invoicing\Models\VanillaModel'], ['Domain\Internal\Reporting\Models\Report'], ]); diff --git a/tests/Autoload/PolicyTest.php b/tests/Autoload/PolicyTest.php index a7bee0b..5a4212b 100644 --- a/tests/Autoload/PolicyTest.php +++ b/tests/Autoload/PolicyTest.php @@ -8,6 +8,7 @@ $this->setupTestApplication(); Config::set('ddd.domain_namespace', 'Domain'); + Config::set('ddd.autoload.factories', true); $this->afterApplicationCreated(function () { (new DomainAutoloader())->autoload(); diff --git a/tests/Autoload/ProviderTest.php b/tests/Autoload/ProviderTest.php index e292e4e..7e3e775 100644 --- a/tests/Autoload/ProviderTest.php +++ b/tests/Autoload/ProviderTest.php @@ -2,17 +2,26 @@ use Illuminate\Support\Facades\Config; use Lunarstorm\LaravelDDD\Support\DomainAutoloader; +use Lunarstorm\LaravelDDD\Support\DomainCache; beforeEach(function () { Config::set('ddd.domain_path', 'src/Domain'); Config::set('ddd.domain_namespace', 'Domain'); $this->setupTestApplication(); - - DomainAutoloader::clearCache(); }); describe('without autoload', function () { + beforeEach(function () { + config([ + 'ddd.autoload.providers' => false, + ]); + + $this->afterApplicationCreated(function () { + (new DomainAutoloader())->autoload(); + }); + }); + it('does not register the provider', function () { expect(fn () => app('invoicing'))->toThrow(Exception::class); }); @@ -20,6 +29,10 @@ describe('with autoload', function () { beforeEach(function () { + config([ + 'ddd.autoload.providers' => true, + ]); + $this->afterApplicationCreated(function () { (new DomainAutoloader())->autoload(); }); @@ -30,3 +43,35 @@ $this->artisan('invoice:deliver')->expectsOutputToContain('invoice-secret'); }); }); + +describe('caching', function () { + beforeEach(function () { + config([ + 'ddd.autoload.providers' => true, + ]); + + $this->setupTestApplication(); + }); + + it('remembers the last cached state', function () { + DomainCache::set('domain-providers', []); + + $this->afterApplicationCreated(function () { + (new DomainAutoloader())->autoload(); + }); + + expect(fn () => app('invoicing'))->toThrow(Exception::class); + }); + + it('can bust the cache', function () { + DomainCache::set('domain-providers', []); + DomainCache::clear(); + + $this->afterApplicationCreated(function () { + (new DomainAutoloader())->autoload(); + }); + + expect(app('invoicing'))->toEqual('invoicing-singleton'); + $this->artisan('invoice:deliver')->expectsOutputToContain('invoice-secret'); + }); +}); diff --git a/tests/Command/CacheTest.php b/tests/Command/CacheTest.php new file mode 100644 index 0000000..6522d64 --- /dev/null +++ b/tests/Command/CacheTest.php @@ -0,0 +1,42 @@ +setupTestApplication(); + DomainCache::clear(); +}); + +it('can cache discovered domain providers and commands', function () { + expect(DomainCache::get('domain-providers'))->toBeNull(); + + expect(DomainCache::get('domain-commands'))->toBeNull(); + + $this + ->artisan('ddd:cache') + ->expectsOutput('Cached domain providers.') + ->expectsOutput('Cached domain commands.') + ->execute(); + + expect(DomainCache::get('domain-providers')) + ->toContain('Domain\Invoicing\Providers\InvoiceServiceProvider'); + + expect(DomainCache::get('domain-commands')) + ->toContain('Domain\Invoicing\Commands\InvoiceDeliver'); +}); + +it('can clear the cache', function () { + Artisan::call('ddd:cache'); + + expect(DomainCache::get('domain-providers'))->not->toBeNull(); + expect(DomainCache::get('domain-commands'))->not->toBeNull(); + + $this + ->artisan('ddd:clear') + ->expectsOutput('Domain cache cleared.') + ->execute(); + + expect(DomainCache::get('domain-providers'))->toBeNull(); + expect(DomainCache::get('domain-commands'))->toBeNull(); +}); diff --git a/tests/Generator/MakeFactoryTest.php b/tests/Generator/MakeFactoryTest.php index 75173e8..5d02d90 100644 --- a/tests/Generator/MakeFactoryTest.php +++ b/tests/Generator/MakeFactoryTest.php @@ -48,7 +48,7 @@ expect($contents) ->toContain("namespace {$domainFactory->namespace};") - ->toContain("use {$domainModel->fqn};") + ->toContain("use {$domainModel->fullyQualifiedName};") ->toContain("class {$domainFactory->name} extends Factory") ->toContain("protected \$model = {$modelName}::class;"); })->with('domainPaths')->with('domainSubdomain'); diff --git a/tests/Generator/MakeModelTest.php b/tests/Generator/MakeModelTest.php index 50076a7..b4a5f04 100644 --- a/tests/Generator/MakeModelTest.php +++ b/tests/Generator/MakeModelTest.php @@ -84,7 +84,7 @@ expect(file_exists($expectedFactoryPath))->toBeTrue("Expecting factory file to be generated at {$expectedFactoryPath}"); expect(file_get_contents($expectedFactoryPath)) - ->toContain("use {$domainModel->fqn};") + ->toContain("use {$domainModel->fullyQualifiedName};") ->toContain("protected \$model = {$modelName}::class;"); })->with('domainPaths')->with('domainSubdomain'); diff --git a/tests/InstallTest.php b/tests/InstallTest.php index dcb58e1..5d58ab9 100644 --- a/tests/InstallTest.php +++ b/tests/InstallTest.php @@ -18,8 +18,7 @@ $command->execute(); expect(file_exists($path))->toBeTrue(); - expect(file_get_contents($path)) - ->toEqual(file_get_contents(__DIR__.'/../config/ddd.php')); + expect(file_get_contents($path))->toEqual(file_get_contents(__DIR__.'/../config/ddd.php')); unlink($path); }); diff --git a/tests/Pest.php b/tests/Pest.php index 1363be1..1691c65 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -12,3 +12,8 @@ function skipOnLaravelVersionsBelow($minimumVersion) test()->markTestSkipped("Only relevant from Laravel {$minimumVersion} onwards (Current version: {$version})."); } } + +function setConfigValues(array $values) +{ + TestCase::configValues($values); +} diff --git a/tests/Support/AutoloaderTest.php b/tests/Support/AutoloaderTest.php new file mode 100644 index 0000000..6b7edf9 --- /dev/null +++ b/tests/Support/AutoloaderTest.php @@ -0,0 +1,13 @@ +setupTestApplication(); +}); + +it('can run', function () { + $autoloader = new DomainAutoloader(); + + $autoloader->autoload(); +})->throwsNoExceptions(); diff --git a/tests/Support/CacheTest.php b/tests/Support/CacheTest.php new file mode 100644 index 0000000..e790139 --- /dev/null +++ b/tests/Support/CacheTest.php @@ -0,0 +1,39 @@ +toBeFalse(); + + DomainCache::set($key, $value); + + expect(DomainCache::has($key))->toBeTrue(); + + expect(DomainCache::get($key))->toEqual($value); +})->with([ + ['value', 'ddd'], + ['number', 123], + ['array', [12, 23, 34]], +]); + +it('can clear cache', function () { + DomainCache::set('one', [12, 23, 34]); + DomainCache::set('two', [45, 56, 67]); + DomainCache::set('three', [45, 56, 67]); + + expect(DomainCache::has('one'))->toBeTrue(); + expect(DomainCache::has('two'))->toBeTrue(); + expect(DomainCache::has('three'))->toBeTrue(); + + DomainCache::clear(); + + expect(DomainCache::has('one'))->toBeFalse(); + expect(DomainCache::has('two'))->toBeFalse(); + expect(DomainCache::has('three'))->toBeFalse(); +}); diff --git a/tests/Support/DomainTest.php b/tests/Support/DomainTest.php index 5e3d760..731be28 100644 --- a/tests/Support/DomainTest.php +++ b/tests/Support/DomainTest.php @@ -23,7 +23,7 @@ it('can describe a domain model', function ($domainName, $name, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->model($name)) ->name->toBe($name) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Reporting', 'InvoiceReport', 'Domain\\Reporting\\Models\\InvoiceReport', 'src/Domain/Reporting/Models/InvoiceReport.php'], @@ -33,7 +33,7 @@ it('can describe a domain factory', function ($domainName, $name, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->factory($name)) ->name->toBe($name) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Reporting', 'InvoiceReportFactory', 'Database\\Factories\\Reporting\\InvoiceReportFactory', 'database/factories/Reporting/InvoiceReportFactory.php'], @@ -43,7 +43,7 @@ it('can describe a data transfer object', function ($domainName, $name, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->dataTransferObject($name)) ->name->toBe($name) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Reporting', 'InvoiceData', 'Domain\\Reporting\\Data\\InvoiceData', 'src/Domain/Reporting/Data/InvoiceData.php'], @@ -53,7 +53,7 @@ it('can describe a view model', function ($domainName, $name, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->viewModel($name)) ->name->toBe($name) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Reporting', 'InvoiceReportViewModel', 'Domain\\Reporting\\ViewModels\\InvoiceReportViewModel', 'src/Domain/Reporting/ViewModels/InvoiceReportViewModel.php'], @@ -63,7 +63,7 @@ it('can describe a value object', function ($domainName, $name, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->valueObject($name)) ->name->toBe($name) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Reporting', 'InvoiceTotal', 'Domain\\Reporting\\ValueObjects\\InvoiceTotal', 'src/Domain/Reporting/ValueObjects/InvoiceTotal.php'], @@ -73,7 +73,7 @@ it('can describe an action', function ($domainName, $name, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->action($name)) ->name->toBe($name) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Reporting', 'SendInvoiceReport', 'Domain\\Reporting\\Actions\\SendInvoiceReport', 'src/Domain/Reporting/Actions/SendInvoiceReport.php'], @@ -83,7 +83,7 @@ it('can describe an anonymous domain object', function ($domainName, $objectType, $objectName, $expectedFQN, $expectedPath) { expect((new Domain($domainName))->object($objectType, $objectName)) ->name->toBe($objectName) - ->fqn->toBe($expectedFQN) + ->fullyQualifiedName->toBe($expectedFQN) ->path->toBe(Path::normalize($expectedPath)); })->with([ ['Invoicing', 'rule', 'SomeRule', 'Domain\\Invoicing\\Rules\\SomeRule', 'src/Domain/Invoicing/Rules/SomeRule.php'], diff --git a/tests/TestCase.php b/tests/TestCase.php index fdddabe..ef9b57e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,59 +2,71 @@ namespace Lunarstorm\LaravelDDD\Tests; +use Illuminate\Contracts\Config\Repository; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Arr; use Illuminate\Support\Facades\File; use Lunarstorm\LaravelDDD\LaravelDDDServiceProvider; -use Lunarstorm\LaravelDDD\Support\DomainAutoloader; +use Lunarstorm\LaravelDDD\Support\DomainCache; use Orchestra\Testbench\TestCase as Orchestra; use Symfony\Component\Process\Process; class TestCase extends Orchestra { + public static $configValues = []; + protected function setUp(): void { - parent::setUp(); - - $this->cleanFilesAndFolders(); - - // $composerFile = base_path('composer.json'); - // $data = json_decode(file_get_contents($composerFile), true); - - // // Reset the domain namespace - // Arr::forget($data, ['autoload', 'psr-4', 'Domains\\']); - // Arr::forget($data, ['autoload', 'psr-4', 'Domain\\']); - - // // Set up the essential app namespaces - // data_set($data, ['autoload', 'psr-4', 'App\\'], 'vendor/orchestra/testbench-core/laravel/app'); - // data_set($data, ['autoload', 'psr-4', 'Database\\Factories\\'], 'vendor/orchestra/testbench-core/laravel/database/factories'); - // data_set($data, ['autoload', 'psr-4', 'Database\\Seeders\\'], 'vendor/orchestra/testbench-core/laravel/database/seeders'); - - // file_put_contents($composerFile, json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); + $this->afterApplicationCreated(function () { + $this->cleanSlate(); + + // $this->updateComposer( + // set: [ + // [['autoload', 'psr-4', 'App\\'], 'vendor/orchestra/testbench-core/laravel/app'], + // [['autoload', 'psr-4', 'Database\\Factories\\'], 'vendor/orchestra/testbench-core/laravel/database/factories'], + // [['autoload', 'psr-4', 'Database\\Seeders\\'], 'vendor/orchestra/testbench-core/laravel/database/seeders'], + // [['autoload', 'psr-4', 'Domain\\'], 'vendor/orchestra/testbench-core/laravel/src/Domain'], + // ], + // forget: [ + // ['autoload', 'psr-4', 'Domains\\'], + // ['autoload', 'psr-4', 'Domain\\'], + // ] + // ); + + Factory::guessFactoryNamesUsing( + fn (string $modelName) => 'Lunarstorm\\LaravelDDD\\Database\\Factories\\'.class_basename($modelName).'Factory' + ); + }); - $this->updateComposer( - [ - [['autoload', 'psr-4', 'App\\'], 'vendor/orchestra/testbench-core/laravel/app'], - [['autoload', 'psr-4', 'Database\\Factories\\'], 'vendor/orchestra/testbench-core/laravel/database/factories'], - [['autoload', 'psr-4', 'Database\\Seeders\\'], 'vendor/orchestra/testbench-core/laravel/database/seeders'], - [['autoload', 'psr-4', 'Domain\\'], 'vendor/orchestra/testbench-core/laravel/src/Domain'], - ], - forget: [ - ['autoload', 'psr-4', 'Domains\\'], - ['autoload', 'psr-4', 'Domain\\'], - ] - ); + $this->beforeApplicationDestroyed(function () { + $this->cleanSlate(); + }); - // This may not be needed after all (significantly slows down the tests) - // $this->composerReload(); + parent::setUp(); + } - Factory::guessFactoryNamesUsing( - fn (string $modelName) => 'Lunarstorm\\LaravelDDD\\Database\\Factories\\'.class_basename($modelName).'Factory' - ); + public static function configValues(array $values) + { + static::$configValues = $values; + } - $this->beforeApplicationDestroyed(function () { - $this->cleanFilesAndFolders(); + protected function defineEnvironment($app) + { + tap($app['config'], function (Repository $config) { + foreach (static::$configValues as $key => $value) { + $config->set($key, $value); + } }); + + // $this->updateComposer( + // set: [ + // [['autoload', 'psr-4', 'App\\'], 'vendor/orchestra/testbench-core/laravel/app'], + // ], + // forget: [ + // ['autoload', 'psr-4', 'Domains\\'], + // ['autoload', 'psr-4', 'Domain\\'], + // ] + // ); } protected function getComposerFileContents() @@ -128,8 +140,10 @@ protected function composerReload() }); } - protected function cleanFilesAndFolders() + protected function cleanSlate() { + File::copy(__DIR__.'/.skeleton/composer.json', base_path('composer.json')); + File::delete(base_path('config/ddd.php')); File::cleanDirectory(app_path()); @@ -139,16 +153,16 @@ protected function cleanFilesAndFolders() File::deleteDirectory(base_path('Custom')); File::deleteDirectory(base_path('src/Domain')); File::deleteDirectory(base_path('src/Domains')); + File::deleteDirectory(app_path('Models')); - DomainAutoloader::clearCache(); + DomainCache::clear(); } protected function setupTestApplication() { - File::copyDirectory(__DIR__.'/resources/app', app_path()); - File::copyDirectory(__DIR__.'/resources/database', base_path('database')); - File::copyDirectory(__DIR__.'/resources/Domain', base_path('src/Domain')); - + File::copyDirectory(__DIR__.'/.skeleton/app', app_path()); + File::copyDirectory(__DIR__.'/.skeleton/database', base_path('database')); + File::copyDirectory(__DIR__.'/.skeleton/src/Domain', base_path('src/Domain')); File::ensureDirectoryExists(app_path('Models')); $this->setDomainPathInComposer('Domain', 'src/Domain');