From 31bd28b5f3fad5ed85a7cb5a1d37a890e6fc1555 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 00:58:28 +0000 Subject: [PATCH 1/2] Bump aglipanci/laravel-pint-action from 2.3.1 to 2.4 Bumps [aglipanci/laravel-pint-action](https://github.com/aglipanci/laravel-pint-action) from 2.3.1 to 2.4. - [Release notes](https://github.com/aglipanci/laravel-pint-action/releases) - [Commits](https://github.com/aglipanci/laravel-pint-action/compare/2.3.1...2.4) --- updated-dependencies: - dependency-name: aglipanci/laravel-pint-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/fix-php-code-style-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fix-php-code-style-issues.yml b/.github/workflows/fix-php-code-style-issues.yml index 4a2838c..96eeb28 100644 --- a/.github/workflows/fix-php-code-style-issues.yml +++ b/.github/workflows/fix-php-code-style-issues.yml @@ -16,7 +16,7 @@ jobs: ref: ${{ github.head_ref }} - name: Fix PHP code style issues - uses: aglipanci/laravel-pint-action@2.3.1 + uses: aglipanci/laravel-pint-action@2.4 - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v5 From 7f9edea3ff8a5d3c90188519827a060fb58b0ae5 Mon Sep 17 00:00:00 2001 From: Jasper Tey Date: Wed, 17 Apr 2024 08:03:07 -0400 Subject: [PATCH 2/2] 56 autoloading enhancements (#58) * Remove cache:clearing hook. * Fix styling * Autoload ignore & custom filter callback. * Normalize filter paths to forward slashes. * Fix styling * Update config and readme. * Change wording. * Corrected autoload_ignore config key. * Fix styling * Update default migrations ignore folder. * Typo. * Another typo. * Ensure config sample in readme is up-to-date. * Add new autoload_ignore key to upgrade stub. --------- Co-authored-by: JasperTey --- CHANGELOG.md | 7 ++ README.md | 64 +++++++++++++++---- config/ddd.php | 20 ++++++ config/ddd.php.stub | 20 ++++++ src/DomainManager.php | 28 ++++++++ src/Facades/DDD.php | 18 ++++++ src/Facades/LaravelDDD.php | 16 ----- src/LaravelDDD.php | 7 -- src/LaravelDDDServiceProvider.php | 10 +-- src/Support/DomainAutoloader.php | 27 +++++++- tests/Autoload/IgnoreTest.php | 102 ++++++++++++++++++++++++++++++ tests/Command/CacheTest.php | 22 +++++++ 12 files changed, 300 insertions(+), 41 deletions(-) create mode 100755 src/DomainManager.php create mode 100644 src/Facades/DDD.php delete mode 100644 src/Facades/LaravelDDD.php delete mode 100755 src/LaravelDDD.php create mode 100644 tests/Autoload/IgnoreTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aaa0d6..b49a40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to `laravel-ddd` will be documented in this file. +## [Unversioned] +### Added +- Ability to ignore folders during autoloading via config(`ddd.autoload_ignore`), or register a custom filter callback via `DDD::filterAutoloadPathsUsing(callable $filter)`. + +### Changed +- Internal: Domain cache is no longer quietly cleared on laravel's `cache:clearing` event, so that `ddd:cache` yields consistent results no matter which order it runs in production (before or after `cache:clear` or `optimize:clear` commands). + ## [1.1.0] - 2024-04-07 ### Added - Add `ddd:class` generator extending Laravel's `make:class` (Laravel 11 only). diff --git a/README.md b/README.md index c7e19d2..13a2f1b 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,27 @@ When `ddd.autoload.factories` is enabled, the package will register a custom fac If your application implements its own factory discovery using `Factory::guessFactoryNamesUsing()`, you should set `ddd.autoload.factories` to `false` to ensure it is not overridden. +### Ignoring Paths During Autoloading +To specify folders or paths that should be skipped during autoloading discovery, add them to the `ddd.autoload_ignore` configuration option. By default, the `Tests` and `Migrations` folders are ignored. +```php +'autoload_ignore' => [ + 'Tests', + 'Database/Migrations', +], +``` + +Paths specified here are relative to the root of each domain. e.g., `src/Domain/Invoicing/{path-to-ignore}`. If more advanced filtering is needed, a callback can be registered using `DDD::filterAutoloadPathsUsing(callback $filter)` in your AppServiceProvider's boot method: +```php +use Lunarstorm\LaravelDDD\Facades\DDD; +use Symfony\Component\Finder\SplFileInfo; + +DDD::filterAutoloadPathsUsing(function (SplFileInfo $file) { + if (basename($file->getRelativePathname()) === 'functions.php') { + return false; + } +}); +``` + ### Disabling Autoloading You may disable autoloading by setting the respective autoload options to `false` in the configuration file as needed, or by commenting out the autoload configuration entirely. ```php @@ -221,11 +242,14 @@ You may disable autoloading by setting the respective autoload options to `false // 'factories' => true, // ], ``` + + ## Autoloading in Production In production, you should cache the autoload manifests using the `ddd:cache` command as part of your application's deployment process. This will speed up the auto-discovery and registration of domain providers and commands. The `ddd:clear` command may be used to clear the cache if needed. + ## Configuration File This is the content of the published config file (`ddd.php`): @@ -356,34 +380,50 @@ return [ */ 'autoload' => [ /** - * When enabled, any class in the domain layer extending - * `Illuminate\Support\ServiceProvider` will be - * auto-registered as a service provider + * When enabled, any class within the domain layer extending `Illuminate\Support\ServiceProvider` + * will be auto-registered as a service provider */ 'providers' => true, /** - * When enabled, any class in the domain layer extending - * `Illuminate\Console\Command` will be auto-registered - * as a command when running in console. + * When enabled, any class within the domain layer extending `Illuminate\Console\Command` + * will be auto-registered as a command when running in console. */ 'commands' => true, /** - * When enabled, a custom policy discovery callback will be - * registered to resolve policy names for domain models, - * or fallback to Laravel's default otherwise. + * When enabled, the package will register a custom policy discovery callback to resolve policy names + * for domain models, and fallback to Laravel's default for all other cases. */ 'policies' => true, /** - * When enabled, a custom policy discovery callback will be - * registered to resolve factory names for domain models, - * or fallback to Laravel's default otherwise. + * When enabled, the package will register a custom factory discovery callback to resolve factory names + * for domain models, and fallback to Laravel's default for all other cases. */ 'factories' => true, ], + /* + |-------------------------------------------------------------------------- + | Autoload Ignore Folders + |-------------------------------------------------------------------------- + | + | Folders that should be skipped during autoloading discovery, + | relative to the root of each domain. + | + | e.g., src/Domain/Invoicing/ + | + | If more advanced filtering is needed, a callback can be registered + | using the `DDD::filterAutoloadPathsUsing(callback $filter)` in + | the AppServiceProvider's boot method. + | + */ + 'autoload_ignore' => [ + 'Tests', + 'Database/Migrations', + ], + /* |-------------------------------------------------------------------------- | Caching diff --git a/config/ddd.php b/config/ddd.php index ae7acb4..66d8901 100644 --- a/config/ddd.php +++ b/config/ddd.php @@ -150,6 +150,26 @@ 'factories' => true, ], + /* + |-------------------------------------------------------------------------- + | Autoload Ignore Folders + |-------------------------------------------------------------------------- + | + | Folders that should be skipped during autoloading discovery, + | relative to the root of each domain. + | + | e.g., src/Domain/Invoicing/ + | + | If more advanced filtering is needed, a callback can be registered + | using `DDD::filterAutoloadPathsUsing(callback $filter)` in + | the AppServiceProvider's boot method. + | + */ + 'autoload_ignore' => [ + 'Tests', + 'Database/Migrations', + ], + /* |-------------------------------------------------------------------------- | Caching diff --git a/config/ddd.php.stub b/config/ddd.php.stub index cd6a2bb..6c10645 100644 --- a/config/ddd.php.stub +++ b/config/ddd.php.stub @@ -150,6 +150,26 @@ return [ 'factories' => true, ], + /* + |-------------------------------------------------------------------------- + | Autoload Ignore Folders + |-------------------------------------------------------------------------- + | + | Folders that should be skipped during autoloading discovery, + | relative to the root of each domain. + | + | e.g., src/Domain/Invoicing/ + | + | If more advanced filtering is needed, a callback can be registered + | using `DDD::filterAutoloadPathsUsing(callback $filter)` in + | the AppServiceProvider's boot method. + | + */ + 'autoload_ignore' => [ + 'Tests', + 'Database/Migrations', + ], + /* |-------------------------------------------------------------------------- | Caching diff --git a/src/DomainManager.php b/src/DomainManager.php new file mode 100755 index 0000000..2de6b0f --- /dev/null +++ b/src/DomainManager.php @@ -0,0 +1,28 @@ +autoloadFilter = null; + } + + public function filterAutoloadPathsUsing(callable $filter): void + { + $this->autoloadFilter = $filter; + } + + public function getAutoloadFilter(): ?callable + { + return $this->autoloadFilter; + } +} diff --git a/src/Facades/DDD.php b/src/Facades/DDD.php new file mode 100644 index 0000000..5164cda --- /dev/null +++ b/src/Facades/DDD.php @@ -0,0 +1,18 @@ +app->scoped(DomainManager::class, function () { + return new DomainManager(); + }); + + $this->app->bind('ddd', DomainManager::class); + /* * This class is a Package Service Provider * @@ -69,7 +73,5 @@ public function packageBooted() public function packageRegistered() { (new DomainAutoloader())->autoload(); - - Event::subscribe(CacheClearSubscriber::class); } } diff --git a/src/Support/DomainAutoloader.php b/src/Support/DomainAutoloader.php index ca75ec8..f107dc0 100644 --- a/src/Support/DomainAutoloader.php +++ b/src/Support/DomainAutoloader.php @@ -16,6 +16,7 @@ use Lunarstorm\LaravelDDD\Factories\DomainFactory; use Lunarstorm\LaravelDDD\ValueObjects\DomainObject; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; use Throwable; class DomainAutoloader @@ -122,6 +123,28 @@ protected function handleFactories(): void }); } + protected static function finder($paths) + { + $filter = app('ddd')->getAutoloadFilter() ?? function (SplFileInfo $file) { + $pathAfterDomain = str($file->getRelativePath()) + ->replace('\\', '/') + ->after('/') + ->finish('/'); + + $ignoredFolders = collect(config('ddd.autoload_ignore', [])) + ->map(fn ($path) => Str::finish($path, '/')); + + if ($pathAfterDomain->startsWith($ignoredFolders)) { + return false; + } + }; + + return Finder::create() + ->files() + ->in($paths) + ->filter($filter); + } + protected static function discoverProviders(): array { $configValue = config('ddd.autoload.providers'); @@ -138,7 +161,7 @@ protected static function discoverProviders(): array return []; } - return Lody::classesFromFinder(Finder::create()->files()->in($paths)) + return Lody::classesFromFinder(static::finder($paths)) ->isNotAbstract() ->isInstanceOf(ServiceProvider::class) ->toArray(); @@ -162,7 +185,7 @@ protected static function discoverCommands(): array return []; } - return Lody::classesFromFinder(Finder::create()->files()->in($paths)) + return Lody::classesFromFinder(static::finder($paths)) ->isNotAbstract() ->isInstanceOf(Command::class) ->toArray(); diff --git a/tests/Autoload/IgnoreTest.php b/tests/Autoload/IgnoreTest.php new file mode 100644 index 0000000..0e94256 --- /dev/null +++ b/tests/Autoload/IgnoreTest.php @@ -0,0 +1,102 @@ +setupTestApplication(); +}); + +it('can ignore folders when autoloading', function () { + Artisan::call('ddd:cache'); + + $expected = [ + 'Domain\Invoicing\Providers\InvoiceServiceProvider', + 'Domain\Invoicing\Commands\InvoiceDeliver', + ]; + + $cached = [ + ...DomainCache::get('domain-providers'), + ...DomainCache::get('domain-commands'), + ]; + + expect($cached)->toEqual($expected); + + Config::set('ddd.autoload_ignore', ['Commands']); + + Artisan::call('ddd:cache'); + + $expected = [ + 'Domain\Invoicing\Providers\InvoiceServiceProvider', + ]; + + $cached = [ + ...DomainCache::get('domain-providers'), + ...DomainCache::get('domain-commands'), + ]; + + expect($cached)->toEqual($expected); + + Config::set('ddd.autoload_ignore', ['Providers']); + + Artisan::call('ddd:cache'); + + $expected = [ + 'Domain\Invoicing\Commands\InvoiceDeliver', + ]; + + $cached = [ + ...DomainCache::get('domain-providers'), + ...DomainCache::get('domain-commands'), + ]; + + expect($cached)->toEqual($expected); +}); + +it('can register a custom autoload filter', function () { + Artisan::call('ddd:cache'); + + $expected = [ + 'Domain\Invoicing\Providers\InvoiceServiceProvider', + 'Domain\Invoicing\Commands\InvoiceDeliver', + ]; + + $cached = [ + ...DomainCache::get('domain-providers'), + ...DomainCache::get('domain-commands'), + ]; + + expect($cached)->toEqual($expected); + + $secret = null; + + DDD::filterAutoloadPathsUsing(function (SplFileInfo $file) use (&$secret) { + $ignoredFiles = [ + 'InvoiceServiceProvider.php', + 'InvoiceDeliver.php', + ]; + + $secret = 'i-was-invoked'; + + if (Str::endsWith($file->getRelativePathname(), $ignoredFiles)) { + return false; + } + }); + + Artisan::call('ddd:cache'); + + $cached = [ + ...DomainCache::get('domain-providers'), + ...DomainCache::get('domain-commands'), + ]; + + expect($cached)->toEqual([]); + + expect($secret)->toEqual('i-was-invoked'); +}); diff --git a/tests/Command/CacheTest.php b/tests/Command/CacheTest.php index 029e4c2..81a3587 100644 --- a/tests/Command/CacheTest.php +++ b/tests/Command/CacheTest.php @@ -40,3 +40,25 @@ expect(DomainCache::get('domain-providers'))->toBeNull(); expect(DomainCache::get('domain-commands'))->toBeNull(); }); + +it('will not be cleared by laravel cache clearing', function () { + config(['cache.default' => 'file']); + + expect(DomainCache::get('domain-providers'))->toBeNull(); + expect(DomainCache::get('domain-commands'))->toBeNull(); + + $this->artisan('ddd:cache')->execute(); + + expect(DomainCache::get('domain-providers'))->not->toBeNull(); + expect(DomainCache::get('domain-commands'))->not->toBeNull(); + + $this->artisan('cache:clear')->execute(); + + expect(DomainCache::get('domain-providers'))->not->toBeNull(); + expect(DomainCache::get('domain-commands'))->not->toBeNull(); + + $this->artisan('optimize:clear')->execute(); + + expect(DomainCache::get('domain-providers'))->not->toBeNull(); + expect(DomainCache::get('domain-commands'))->not->toBeNull(); +});