Commands

osdd:layer

Create a layer, generate its first files and register it in Composer.

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:

  1. Where to create the layer, among the keys of osdd.layers.paths (functional or technical). With a single path, it skips the question. The key becomes the vendor of the package.
  2. The layer name, such as orders. See Naming.
  3. Which generators to run. See the list below.
  4. 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 / OptionDescription
nameName of the layer, vendor/package. The vendor picks the folder: functional/orders goes to the functional path of osdd.layers.paths.
--generatorsGenerators to run, comma-separated (--generators=model,policy) or repeated (--generators=model --generators=policy).
--requiresLayers the new layer requires, comma-separated or repeated. Full name, or package name when it is unique.
--target-pathAbsolute path of the folder to create the layer in, instead of the path named after the vendor.
--forceRun the generators again on an existing layer.
--composer-update / --no-composer-updateRun 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 update is skipped unless you pass --composer-update. The command prints Run 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 --requires layer 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:

GeneratorDefaultCreates
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
seederdatabase/seeders/OrdersSeeder.php, registered in the provider
controllersrc/Http/Controllers/OrdersController.php
policysrc/Policies/OrdersPolicy.php, bound to the Order model when model runs too
routesroutes/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
functional/invoices/composer.json
"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:

  1. Writes the layer's composer.json with "type": "layer" and its PSR-4 namespaces.
  2. 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.
  3. Requires "functional/orders": "*" in the root composer.json, sorted when config.sort-packages is 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_*_table migration is added;
  • the layer's composer.json is kept: it only gains the keys of a new layer that it lacks, so its require, its extra.laravel.providers and 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