osdd:layer
osdd:layer creates a layer, runs the generators you pick and registers the layer in the root composer.json.
Interactive
php artisan osdd:layer
The command asks:
- Where to create the layer, among the keys of
osdd.layers.paths(functionalortechnical). With a single path, it skips the question. The key becomes the vendor of the package. - The layer name, such as
orders. See Naming. - Which generators to run. See the list below.
- Whether to run
composer update functional/orders(default: yes).
┌ Where should the layer be created? ──────────────────────────┐
│ › ● functional │
│ ○ technical │
└──────────────────────────────────────────────────────────────┘
┌ Layer name ──────────────────────────────────────────────────┐
│ orders │
└──────────────────────────────────────────────────────────────┘
┌ Which generators should be run? ─────────────────────────────┐
│ › ◼ migration │
│ ◼ model │
│ ◼ factory │
│ ◻ seeder │
│ ◼ provider │
│ ◼ test │
│ ◻ controller │
│ ◻ policy │
│ ◻ routes │
└──────────────────────────────────────────────────────────────┘
With arguments
php artisan osdd:layer functional/orders --generators=migration,model,factory,provider,test --composer-update
| Argument / Option | Description |
|---|---|
name | Name of the layer, vendor/package. The vendor picks the folder: functional/orders goes to the functional path of osdd.layers.paths. |
--generators | Generators to run, comma-separated (--generators=model,policy) or repeated (--generators=model --generators=policy). |
--requires | Layers the new layer requires, comma-separated or repeated. Full name, or package name when it is unique. |
--target-path | Absolute path of the folder to create the layer in, instead of the path named after the vendor. |
--force | Run the generators again on an existing layer. |
--composer-update / --no-composer-update | Run or skip composer update vendor/package without asking. |
The layer is created in <path>/<package>, and its namespace comes from the name: functional/orders gives Functional\Orders. The command ends with Layer functional/orders created at functional/orders..
Without a terminal
Under -n, in CI, in a script or when a coding agent runs it, the command asks nothing:
- the name argument is required;
- the vendor must match a key of
osdd.layers.paths, or you pass--target-path. Otherwise the command fails:The vendor [acme] matches no key of osdd.layers.paths (functional, technical). Name the layer after one of them, or pass --target-path.; - without
--generators, the default generators run; composer updateis skipped unless you pass--composer-update. The command printsRun composer update functional/orders to install the new layers..
Errors
The command fails with exit code 1 and writes nothing when:
- the name is not a valid layer name;
- a generator is unknown:
Unknown generator [modle]. Valid generators: migration, model, factory, seeder, provider, test, controller, policy, routes.; - a
--requireslayer is unknown, ambiguous, or the new layer itself; - the layer directory exists and is not empty, without
--force:The layer directory [...] already exists. Pass --force to overwrite the files osdd:layer writes..
When composer update runs and fails, the command exits with 1 and keeps the layer: run composer update functional/orders once the error is fixed.
Generators
For a layer named functional/orders:
| Generator | Default | Creates |
|---|---|---|
migration | ✓ | database/migrations/<timestamp>_create_orders_table.php |
model | ✓ | src/Models/Order.php |
factory | ✓ | database/factories/OrderFactory.php |
provider | ✓ | src/Providers/OrdersServiceProvider.php |
test | ✓ | tests/Feature/OrdersTest.php |
seeder | database/seeders/OrdersSeeder.php, registered in the provider | |
controller | src/Http/Controllers/OrdersController.php | |
policy | src/Policies/OrdersPolicy.php, bound to the Order model when model runs too | |
routes | routes/web.php, routes/api.php, routes/console.php, routes/channels.php |
The model takes the singular of the layer name and the table its plural: external-accounts gives the ExternalAccount model and the external_accounts table.
The generators run in a fixed dependency order: provider first, so that migration and seeder can register themselves in it; model before factory and policy. With both model and factory, the model is generated with its factory. The others run in the order you give.
The provider generator also lists the provider under extra.laravel.providers in the layer's composer.json. The generated provider loads the layer's migrations, calls withRouting() with the four route files, and withViews() and withTranslations() with the layer's resources/views and lang folders. Missing files and folders are skipped, so the calls are safe without the routes generator. It registers no seeder: the seeder generator adds the loadSeeders() call.
When another layer already has the same package name (technical/orders next to functional/orders), the provider passes 'functional-orders' as the views and translations namespace, so the two layers do not collide.
Requirements
--requires adds the layers the new one depends on to its composer.json:
php artisan osdd:layer functional/invoices --requires=functional/orders --requires=technical/core
php artisan osdd:layer functional/invoices --requires=orders,core
"require": {
"lomkit/laravel-osdd": "*",
"functional/orders": "*",
"technical/core": "*"
}
Names resolve like the generators' --layer. With --force on an existing layer, the requirements are added to the ones it has. See Boundaries.
What gets registered
Whatever the generators, osdd:layer:
- Writes the layer's
composer.jsonwith"type": "layer"and its PSR-4 namespaces. - Adds a path repository for the layer's folder to the root
composer.json, such as./functional/*, once per folder, or a../URL for a folder outside the project. - Requires
"functional/orders": "*"in the rootcomposer.json, sorted whenconfig.sort-packagesis on.
Empty objects such as "require-dev": {} stay objects, and the indentation of the file is kept. After composer update functional/orders, Laravel discovers the layer's provider. An existing layer cache is refreshed.
Existing layers
osdd:layer refuses a layer directory that already exists and is not empty. To add files to an existing layer, use the generators.
With --force, it runs the generators again:
- the provider and the route files are rewritten;
- the model and the controller are overwritten;
- the other generated files are kept, and no second
create_*_tablemigration is added; - the layer's
composer.jsonis kept: it only gains the keys of a new layer that it lacks, so itsrequire, itsextra.laravel.providersand your own keys stay.
Examples
An API layer
php artisan osdd:layer functional/invoices \
--generators=migration,model,factory,controller,provider,routes \
--requires=functional/orders
An infrastructure layer
php artisan osdd:layer technical/feature-flags --generators=provider