From 5bb5de6d760091bf3d41d0f06727ee23b90c040c Mon Sep 17 00:00:00 2001 From: Jasper Tey Date: Wed, 13 Nov 2024 15:07:21 -0500 Subject: [PATCH] Update readme and fix custom resolver logic. --- README.md | 121 ++++++++++++++---- UPGRADING.md | 19 ++- .../Concerns/ResolvesDomainFromInput.php | 5 + src/Commands/DomainControllerMakeCommand.php | 5 - src/Support/GeneratorBlueprint.php | 5 + .../Support/ResolveObjectSchemaUsingTest.php | 5 +- 6 files changed, 129 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 4b97df3..e5d62b8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/lunarstorm/laravel-ddd/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/lunarstorm/laravel-ddd/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) [![Total Downloads](https://img.shields.io/packagist/dt/lunarstorm/laravel-ddd.svg?style=flat-square)](https://packagist.org/packages/lunarstorm/laravel-ddd) -Laravel-DDD is a toolkit to support domain driven design (DDD) in Laravel applications. One of the pain points when adopting DDD is the inability to use Laravel's native `make` commands to generate domain objects since they are typically stored outside the `App\*` namespace. This package aims to fill the gaps by providing equivalent commands such as `ddd:model`, `ddd:dto`, `ddd:view-model` and many more. +Laravel-DDD is a toolkit to support domain driven design (DDD) in Laravel applications. One of the pain points when adopting DDD is the inability to use Laravel's native `make` commands to generate objects outside the `App\*` namespace. This package aims to fill the gaps by providing equivalent commands such as `ddd:model`, `ddd:dto`, `ddd:view-model` and many more. ## Installation You can install the package via composer: @@ -24,7 +24,7 @@ The following additional packages are suggested (but not required) while working - Data Transfer Objects: [spatie/laravel-data](https://github.com/spatie/laravel-data) - Actions: [lorisleiva/laravel-actions](https://github.com/lorisleiva/laravel-actions) -The default DTO and Action stubs of this package reference classes from these packages. If this doesn't apply to your application, you may [customize the stubs](#publishing-stubs-advanced) accordingly. +The default DTO and Action stubs of this package reference classes from these packages. If this doesn't apply to your application, you may [publish and customize the stubs](#customizing-stubs) accordingly. ### Deployment In production, run `ddd:optimize` during the deployment process to [optimize autoloading](#autoloading-in-production). @@ -40,7 +40,7 @@ Since Laravel 11.27.1, `php artisan optimize` automatically invokes `ddd:optimiz 10.25.x | 1.x | 11.x | 1.x | -See **[UPGRADING](UPGRADING.md)** for more details about upgrading from 0.x. +See **[UPGRADING](UPGRADING.md)** for more details about upgrading from an older versions. @@ -97,6 +97,36 @@ The following generators are currently available: Generated objects will be placed in the appropriate domain namespace as specified by `ddd.namespaces.*` in the [config file](#config-file). +### Config Utility (Since 1.2) +A configuration utility was introduced in 1.2 to help manage the package's configuration over time. +```bash +php artisan ddd:config +``` +Output: +``` + ┌ Laravel-DDD Config Utility ──────────────────────────────────┐ + │ › ● Run the configuration wizard │ + │ ○ Update and merge ddd.php with latest package version │ + │ ○ Detect domain namespace from composer.json │ + │ ○ Sync composer.json from ddd.php │ + │ ○ Exit │ + └──────────────────────────────────────────────────────────────┘ +``` +These config tasks are also invokeable directly using arguments: +```bash +# Run the configuration wizard +php artisan ddd:config wizard + +# Update and merge ddd.php with latest package version +php artisan ddd:config update + +# Detect domain namespace from composer.json +php artisan ddd:config detect + +# Sync composer.json from ddd.php +php artisan ddd:config composer +``` + ### Other Commands ```bash # Show a summary of current domains in the domain folder @@ -230,34 +260,83 @@ php artisan ddd:view-model Reporting.Customer:MonthlyInvoicesReportViewModel # (supported by all commands where a domain option is accepted) ``` -## Customization -### Config File -This package ships with opinionated (but sensible) configuration defaults. You may customize by publishing the [config file](#config-file) and generator stubs as needed: +### Custom Object Resolution +If you require advanced customization of generated object naming conventions, you may register a custom resolver using `DDD::resolveObjectSchemaUsing()` in your AppServiceProvider's boot method: +```php +use Lunarstorm\LaravelDDD\Facades\DDD; +use Lunarstorm\LaravelDDD\ValueObjects\CommandContext; +use Lunarstorm\LaravelDDD\ValueObjects\ObjectSchema; + +DDD::resolveObjectSchemaUsing(function (string $domainName, string $nameInput, string $type, CommandContext $command): ?ObjectSchema { + if ($type === 'controller' && $command->option('api')) { + return new ObjectSchema( + name: $name = str($nameInput)->replaceEnd('Controller', '')->finish('ApiController')->toString(), + namespace: "App\\Api\\Controllers\\{$domainName}", + fullyQualifiedName: "App\\Api\\Controllers\\{$domainName}\\{$name}", + path: "src/App/Api/Controllers/{$domainName}/{$name}.php", + ); + } + // Return null to fall back to the default + return null; +}); +``` +The example above will result in the following: ```bash -php artisan ddd:publish --config -php artisan ddd:publish --stubs +php artisan ddd:controller Invoicing:PaymentController --api +# Controller [src/App/Api/Controllers/Invoicing/PaymentApiController.php] created successfully. ``` -### Publishing Stubs (Advanced) -For more granular management of stubs, you may use the `ddd:stub` command: + + +## Customizing Stubs +This package ships with a few ddd-specific stubs, while the rest are pulled from the framework. For a quick reference of available stubs and their source, you may use the `ddd:stub --list` command: ```bash -# Publish one or more stubs interactively via prompts -php artisan ddd:stub +php artisan ddd:stub --list +``` + +### Stub Priority +When generating objects using `ddd:*`, stubs are prioritized as follows: +- Try `stubs/ddd/*.stub` (customized for `ddd:*` only) +- Try `stubs/*.stub` (shared by both `make:*` and `ddd:*`) +- Fallback to the package or framework default +### Publishing Stubs +To publish stubs interactively, you may use the `ddd:stub` command: +```bash +php artisan ddd:stub +``` +``` + ┌ What do you want to do? ─────────────────────────────────────┐ + │ › ● Choose stubs to publish │ + │ ○ Publish all stubs │ + └──────────────────────────────────────────────────────────────┘ + + ┌ Which stub should be published? ─────────────────────────────┐ + │ policy │ + ├──────────────────────────────────────────────────────────────┤ + │ › ◼ policy.plain.stub │ + │ ◻ policy.stub │ + └────────────────────────────────────────────────── 1 selected ┘ + Use the space bar to select options. +``` +You may also use shortcuts to skip the interactive steps: +```bash # Publish all stubs php artisan ddd:stub --all -# Publish and overwrite only the files that have already been published -php artisan ddd:stub --all --existing - -# Overwrite any existing files -php artisan ddd:stub --all --force - -# Publish one or more stubs specified as arguments +# Publish one or more stubs specified as arguments (see ddd:stub --list) php artisan ddd:stub model php artisan ddd:stub model dto action php artisan ddd:stub controller controller.plain controller.api + +# Options: + +# Publish and overwrite only the files that have already been published +php artisan ddd:stub ... --existing + +# Overwrite any existing files +php artisan ddd:stub ... --force ``` To publish multiple related stubs at once, use `*` or `.` as a wildcard ending. ```bash @@ -270,10 +349,6 @@ Publishing /stubs/ddd/listener.queued.stub Publishing /stubs/ddd/listener.typed.stub Publishing /stubs/ddd/listener.stub ``` -For a quick reference of available stubs, use the `--list` option: -```bash -php artisan ddd:stub --list -``` ## Domain Autoloading and Discovery Autoloading behaviour can be configured with the `ddd.autoload` configuration option. By default, domain providers, commands, policies, and factories are auto-discovered and registered. diff --git a/UPGRADING.md b/UPGRADING.md index b9177b4..e9a6e17 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,6 +1,21 @@ # Upgrading - - ## From 0.x to 1.x + +## From 1.1.x to 1.2.x +### Breaking +- Stubs are now published `to base_path('stubs/ddd')` instead of `resource_path('stubs/ddd')`. In other words, they are now co-located alongside the framework's published stubs, within a ddd subfolder. +- Published stubs now use `.stub` extension instead of `.php.stub` (following Laravel's convention). +- If you haven't previously published stubs in your installation, this change will not affect you. + +### Update Config +- Support for Application Layer and Custom Layers was added, introducing changes to the config file. +- Run `php artisan ddd:config update` to rebuild your application's published `ddd.php` config to align with the package's latest copy. +- The update utility will attempt to respect your existing customizations, but you should still review and verify manually. + +### Publishing Stubs +- Old way (removed): `php artisan vendor:publish --tag="ddd-stubs"` +- New way, using stub utility command: `php artisan ddd:stub` (see [Customizing Stubs](README.md#customizing-stubs) in README for more details). + +## From 0.x to 1.x - Minimum required Laravel version is 10.25. - The ddd generator [command syntax](README.md#usage) in 1.x. Generator commands no longer receive a domain argument. For example, instead of `ddd:action Invoicing CreateInvoice`, one of the following would be used: - Using the --domain option: ddd:action CreateInvoice --domain=Invoicing (this takes precedence). diff --git a/src/Commands/Concerns/ResolvesDomainFromInput.php b/src/Commands/Concerns/ResolvesDomainFromInput.php index 5bdb136..103dd77 100644 --- a/src/Commands/Concerns/ResolvesDomainFromInput.php +++ b/src/Commands/Concerns/ResolvesDomainFromInput.php @@ -43,6 +43,11 @@ protected function getPath($name) : parent::getPath($name); } + protected function qualifyClass($name) + { + return $this->blueprint->qualifyClass($name); + } + protected function beforeHandle() { $nameInput = $this->getNameInput(); diff --git a/src/Commands/DomainControllerMakeCommand.php b/src/Commands/DomainControllerMakeCommand.php index 5228211..1cba0c9 100644 --- a/src/Commands/DomainControllerMakeCommand.php +++ b/src/Commands/DomainControllerMakeCommand.php @@ -92,11 +92,6 @@ protected function buildClass($name) $replace = []; - // Todo: these were attempted tweaks to counteract failing CI tests - // on Laravel 10, and should be revisited at some point. - // $replace["use {$this->rootNamespace()}Http\Controllers\Controller;\n"] = ''; - // $replace[' extends Controller'] = ''; - $appRootNamespace = $this->laravel->getNamespace(); $pathToAppBaseController = parent::getPath("Http\Controllers\Controller"); diff --git a/src/Support/GeneratorBlueprint.php b/src/Support/GeneratorBlueprint.php index 6333347..8c1bfd1 100644 --- a/src/Support/GeneratorBlueprint.php +++ b/src/Support/GeneratorBlueprint.php @@ -113,6 +113,11 @@ public function getPath($name) return Path::normalize(app()->basePath($this->schema->path)); } + public function qualifyClass($name) + { + return $this->schema->fullyQualifiedName; + } + public function getFactoryFor(string $name) { return $this->domain->factory($name); diff --git a/tests/Support/ResolveObjectSchemaUsingTest.php b/tests/Support/ResolveObjectSchemaUsingTest.php index 41bafe7..4d7abfe 100644 --- a/tests/Support/ResolveObjectSchemaUsingTest.php +++ b/tests/Support/ResolveObjectSchemaUsingTest.php @@ -46,5 +46,8 @@ $expectedPath = base_path('src/App/Api/Controllers/Invoicing/PaymentApiController.php'); expect(file_get_contents($expectedPath)) - ->toContain("namespace App\Api\Controllers\Invoicing;"); + ->toContain(...[ + "namespace App\Api\Controllers\Invoicing;", + 'class PaymentApiController', + ]); });