Generating Files
Each file type in your layout has a mod:* command that writes into the layout's folders. It is Laravel's own make:* command underneath, so it takes the same arguments and options and generates the same code.
Running a Generator
In an app that stores documents, a Knowledge module holds everything about them. With the modules layout, generate an event for an uploaded document and a listener for it:
php artisan mod:event Knowledge:DocumentUploaded
# -> app/Modules/Knowledge/Events/DocumentUploaded.php
php artisan mod:listener Knowledge:GenerateEmbeddings --event=DocumentUploaded
# -> app/Modules/Knowledge/Listeners/GenerateEmbeddings.php--event=DocumentUploaded is make:listener's own option. The listener finds the event in the same module:
<?php
namespace App\Modules\Knowledge\Listeners;
use App\Modules\Knowledge\Events\DocumentUploaded;
class GenerateEmbeddings
{
public function handle(DocumentUploaded $event): void
{
//
}
}make:* is untouched and keeps writing to Laravel's default folders.
Listing the Commands
The commands depend on your layout: modules has mod:dto and mod:view-model, slices has mod:handler, and laravel has neither. List the ones your layout has:
php artisan list modEvery hyphenated command also works without the dash: mod:viewmodel, mod:valueobject, mod:jobmiddleware. This applies to your own file types too, unless the name is already taken.
Commands lists every command, its options and the layouts that have it.
MCP File Types
Since 0.5 (unreleased). With laravel/mcp installed, the modules and ddd layouts add mcp-server, mcp-tool, mcp-resource and mcp-prompt. Their mod:* commands use Laravel MCP’s own generators, retaining their options, generated contents and published stubs/mcp-*.stub overrides. make:mcp-* keeps its native placement.
In the modules layout:
php artisan mod:mcp-server Knowledge:DocumentServer
# -> app/Modules/Knowledge/Mcp/Servers/DocumentServer.php
php artisan mod:mcp-tool Knowledge:SearchDocuments
# -> app/Modules/Knowledge/Mcp/Tools/SearchDocuments.php
php artisan mod:mcp-resource Knowledge:DocumentResource
# -> app/Modules/Knowledge/Mcp/Resources/DocumentResource.php
php artisan mod:mcp-prompt Knowledge:SummarizeDocument
# -> app/Modules/Knowledge/Mcp/Prompts/SummarizeDocument.phpThe placements use @module/Mcp/{Servers,Tools,Resources,Prompts}, so they follow a custom module path. In ddd, they belong to the application root at app/Modules/{domain}/Mcp/..., including nested domains:
php artisan mod:mcp-tool Knowledge.Search:SearchDocuments
# -> app/Modules/Knowledge/Search/Mcp/Tools/SearchDocuments.phpOther layouts can declare these file types explicitly with generates(). Existing generator overrides remain in effect. mcp-app-resource is excluded from this integration because its native generator writes a companion view as well as a class. This support covers the four MCP generators; general delegation to arbitrary package generators remains planned in mod#87.
Generating Related Files
make:model's options for related files create them in the same module:
php artisan mod:model Knowledge:Document --allapp/Modules/Knowledge/
├── Database/
│ ├── Factories/
│ │ └── DocumentFactory.php
│ ├── Migrations/
│ │ └── 2026_10_08_120000_create_documents_table.php
│ └── Seeders/
│ └── DocumentSeeder.php
├── Http/
│ ├── Controllers/
│ │ └── DocumentController.php
│ └── Requests/
│ ├── StoreDocumentRequest.php
│ └── UpdateDocumentRequest.php
├── Models/
│ └── Document.php
└── Policies/
└── DocumentPolicy.phpThe same goes for -m, -f, -s, --policy, --controller and --requests. As with make:model, --requests writes form requests only for a resource controller, so pass --controller --resource --requests together.
Each generated class refers to the others where they are: the factory names the model, and the model finds its factory, so Document::factory() works wherever the factory lives.
Choosing the Module
The laravel layout puts files where make:* does. The other layouts group your code, so each command also needs to know which group a file belongs to.
Each way a layout groups code is a dimension. modules has one: the module. slices has two: the feature, and the slice inside it. A layout's folders show each one as a placeholder, such as {module} in app/Modules/{module}/Models, and you give it a value, such as Knowledge.
These three commands do the same thing:
php artisan mod:model Document --module=Knowledge # an option named after the placeholder
php artisan mod:model Document --in=Knowledge # every value at once
php artisan mod:model Knowledge:Document # the short form: value, colon, class nameEach layout's option:
| Layout | Options | Values |
|---|---|---|
laravel | none | |
modules | --module | Knowledge |
features | --feature | Knowledge |
slices | --feature, --slice | Knowledge, IndexDocument |
type-first | --feature (optional) | Knowledge, or none for app/Models/Document.php |
ddd | --domain (one or more folders) | Knowledge, or Knowledge.Search for src/Domain/Knowledge/Search |
Giving Two Values
When a layout has two dimensions, --in and the short form take the values in order, separated by /. A handler's class is always named Handler, so its command needs no name:
php artisan mod:handler --feature=Knowledge --slice=IndexDocument
php artisan mod:handler --in=Knowledge/IndexDocument
php artisan mod:handler Knowledge/IndexDocument:Handler
# -> app/Knowledge/IndexDocument/Handler.phpLeaving the Module Out
A command that needs a module exits with an error naming every way to give one:
php artisan mod:model Document ERROR mod:model needs a module. Pass --module=<module>, --in=<module>, or prefix the name: <module>:Document.In features and slices, mod:command without a feature writes to app/Console/Commands.
New and Misspelled Modules
A command compares the module you give with the modules that exist: folders that hold the layout's files for a module. A new name creates the module's folder and says so, listing the existing modules:
php artisan mod:model Billing:Invoice INFO Created new module Billing (existing: Knowledge).
INFO Model [app/Modules/Billing/Models/Invoice.php] created successfully.A name that differs from an existing module only by case uses that module and says so:
php artisan mod:model knowledge:Note INFO Using existing module Knowledge (you typed knowledge).
INFO Model [app/Modules/Knowledge/Models/Note.php] created successfully.A near miss such as Knowledg asks whether you meant an existing module or a new one, with the closest module selected.
Without a terminal to ask in, such as with --no-interaction or in CI, it starts the new module and says so:
php artisan mod:model Knowledg:Note --no-interaction INFO Created new module Knowledg (existing: Knowledge).
INFO Model [app/Modules/Knowledg/Models/Note.php] created successfully.A near miss is one or two letters away from an existing module, ignoring case, and one letter for names shorter than six, so Agent is close to Agents. On a case-sensitive disk with both Knowledge and KNOWLEDGE, knowledge asks which one you meant, or exits with an error naming both without a terminal.
Each message prints once per command, and other layouts word it with their own dimension: Created new feature Knowledge., Created new slice IndexDocument. In slices, Laravel's own app/Http and app/Models don't count as features.
When a File Already Exists
mod:* checks every file it is about to write before writing any of them. When every file already exists, it prints an error for each and exits with 0, as make:* does:
php artisan mod:model Knowledge:Document -f ERROR app/Modules/Knowledge/Models/Document.php already exists.
ERROR app/Modules/Knowledge/Database/Factories/DocumentFactory.php already exists.When some files exist and others don't, it writes none of them, so the new ones aren't left half done. It prints Nothing was written. and exits with 1. Here the factory exists and the model doesn't:
php artisan mod:model Knowledge:Document -f ERROR app/Modules/Knowledge/Database/Factories/DocumentFactory.php already exists.
ERROR Nothing was written.Pass --force to overwrite, on the commands whose make:* command has it.
For a repeating class shape, Custom generators creates its own command. For several file types together, use a scaffold.