strides/laravel-api-module
Composer 安装命令:
composer require strides/laravel-api-module
包简介
A code generation toolkit for building clean, scalable Laravel APIs with modular architecture.
README 文档
README
A code generation toolkit for building clean, scalable Laravel APIs with modular architecture. Designed for teams that work exclusively with APIs and follow the Action → Repository → Transformer pattern.
Why This Package?
When building APIs with Laravel, you end up creating the same set of files for every resource: model, migration, controller, request, repository, transformer, action, and so on. This package automates that with Artisan generators designed specifically for API development — no Blade views, no web routes, no frontend scaffolding.
php artisan module:make-model Product --all
One command. Model, migration, factory, seeder, controller, repository, transformer — all generated with correct namespacing, PSR-12 formatting, and placed in an isolated module directory.
Table of Contents
- Requirements
- Installation
- Quick Start
- Module Management
- Generators Reference
- Architecture Pattern
- Configuration
- Migration Commands
- Best Practices
- Testing
- Roadmap
- Contributing
Requirements
| Dependency | Version |
|---|---|
| PHP | >= 8.1 |
| Laravel | ^9.0 | ^10.0 | ^11.0 | ^12.0 | ^13.0 |
| Composer | Latest |
Installation
1. Install via Composer
composer require strides/laravel-api-module
2. Publish configuration
php artisan vendor:publish --provider="Strides\Module\Providers\ModuleServiceProvider"
3. Register the Modules namespace in composer.json
{
"autoload": {
"psr-4": {
"App\\": "app/",
"Modules\\": "Modules/"
}
}
}
composer dump-autoload
4. Add Modules test suite to phpunit.xml (optional but recommended)
<testsuites> <testsuite name="Modules"> <directory suffix="Test.php">Modules/*/Tests/*</directory> </testsuite> </testsuites>
Quick Start
Create a complete module
php artisan module:make-module Product
This registers the module in modules_name.json and generates the full directory structure:
Modules/
└── Product/
├── Actions/
├── Broadcasting/
├── Casts/
├── Contracts/
├── Database/
│ ├── Factories/
│ ├── Migrations/
│ └── Seeders/
├── Entities/
├── Events/
├── Helpers/
├── Http/
│ ├── Controllers/
│ ├── Middleware/
│ ├── Requests/
│ ├── Resources/
│ └── Transformers/
├── Jobs/
├── Listeners/
├── Providers/
├── Repositories/
├── Services/
└── Tests/
Generate a model with all related files
php artisan module:make-model Product Product --all
Generates: model, migration, factory, seeder, controller, repository.
Generate specific components
php artisan module:make-model Product Product --migration --controller --factory --seeder php artisan module:make-controller Product ProductController --request --resource --repository
Module Management
Every module is registered in modules_name.json at the project root. The package provides four commands to manage module lifecycle.
List all modules
php artisan module:list
+------------+-----------+--------------+----------------------------------+
| Module | Status | Path exists | Path |
+------------+-----------+--------------+----------------------------------+
| Product | Enabled | ✓ | /var/www/Modules/Product |
| Orders | Disabled | ✓ | /var/www/Modules/Orders |
| Legacy | Enabled | ✗ Missing | /var/www/Modules/Legacy |
+------------+-----------+--------------+----------------------------------+
Total: 3 Enabled: 2 Disabled: 1 Missing: 1
Enable / Disable a module
php artisan module:enable Orders php artisan module:disable Orders
Disabling a module prevents its service provider from loading — routes, migrations, and commands become inactive. Files are preserved.
Clean up stale entries
php artisan module:optimize
Scans modules_name.json and removes entries whose directories no longer exist. Asks for confirmation before writing.
The following modules have no directory and will be removed:
- Legacy
Proceed? (yes/no) [yes]:
Done. Removed 1 stale entry from modules_name.json.
Generators Reference
Module
| Command | Description |
|---|---|
module:make-module {name} |
Create a complete module with full directory structure |
Models & Data
| Command | Flags | Description |
|---|---|---|
module:make-model {module} {name} |
-m -c -f -s -a |
Eloquent model with optional related files |
module:make-migration {module} {name} |
Database migration | |
module:make-seeder {module} {name} |
Database seeder | |
module:make-factory {module} {name} |
Model factory | |
module:make-cast {module} {name} |
Custom Eloquent attribute cast (CastsAttributes) |
Model flags:
| Flag | Long | Generates |
|---|---|---|
-m |
--migration |
Migration |
-c |
--controller |
Controller |
-f |
--factory |
Factory |
-s |
--seeder |
Seeder |
-a |
--all |
All of the above |
Controllers & HTTP
| Command | Flags | Description |
|---|---|---|
module:make-controller {module} {name} |
-r -s -c -p -m -a |
API controller |
module:make-request {module} {name} |
FormRequest validation class | |
module:make-transformer {module} {name} |
Data transformer for API responses |
Controller flags:
| Flag | Long | Generates |
|---|---|---|
-r |
--request |
FormRequest class |
-s |
--resource |
API Resource class |
-c |
--collection |
Resource Collection class |
-p |
--repository |
Repository class |
-m |
--model |
Model name for type hints |
-a |
--all |
All of the above |
Business Logic
| Command | Description |
|---|---|
module:make-action {module} {name} |
Single-purpose invokable action class |
module:make-service {module} {name} |
Service class for complex operations |
module:make-repository {module} {name} |
Repository for data access abstraction |
Events & Async
| Command | Flags | Description |
|---|---|---|
module:make-event {module} {name} |
--listener[=Name] |
Event class. Optionally creates a linked Listener |
module:make-listener {module} {name} |
--event=ClassName |
Listener. --event= typehints the handle() parameter |
module:make-job {module} {name} |
Queueable job (ShouldQueue by default) |
Event → Listener examples:
# Create Event only php artisan module:make-event Product ProductCreated # Create Event + auto-generate a linked Listener php artisan module:make-event Product ProductCreated --listener # Create Event + Listener with a specific name php artisan module:make-event Product ProductCreated --listener=SendProductNotification # Create Listener with typed handle(ProductCreated $event) php artisan module:make-listener Product SendProductNotification --event=ProductCreated
Utilities
| Command | Description |
|---|---|
module:make-interface {module} {name} |
PHP interface (placed in Contracts/) |
module:make-helper {module} {name} |
Utility helper class |
module:make-middleware {module} {name} |
HTTP middleware |
module:make-channel {module} {name} |
Broadcasting channel with join() authorization |
module:make-test {module} {name} |
PHPUnit test class |
Architecture Pattern
Action → Repository → Transformer
This package is built around a clean three-layer API architecture:
HTTP Request
│
▼
FormRequest (validation)
│
▼
Controller ──────────────────────────────┐
│ │
▼ │
Action (business logic) │
│ │
▼ │
Repository (data access) │
│ │
▼ │
Transformer (response format) ◄───────────┘
│
▼
JSON Response
Action
Actions are single-purpose invokable classes. One action, one responsibility.
// Modules/Product/Actions/CreateProductAction.php namespace Modules\Product\Actions; use Modules\Product\Entities\Product; use Modules\Product\Repositories\ProductRepository; class CreateProductAction { public function __construct(private ProductRepository $repository) {} public function __invoke(array $data): Product { $product = $this->repository->create($data); event(new ProductCreated($product)); return $product; } }
Repository
Repositories abstract all database queries behind a clean interface.
// Modules/Product/Repositories/ProductRepository.php namespace Modules\Product\Repositories; use Illuminate\Support\Collection; use Modules\Product\Entities\Product; class ProductRepository { public function all(): Collection { return Product::latest()->get(); } public function find(int $id): Product { return Product::findOrFail($id); } public function create(array $data): Product { return Product::create($data); } public function update(Product $product, array $data): Product { $product->update($data); return $product->fresh(); } public function delete(Product $product): bool { return $product->delete(); } }
Transformer
Transformers define exactly what the API returns — nothing more, nothing less.
// Modules/Product/Http/Transformers/ProductTransformer.php namespace Modules\Product\Http\Transformers; use Illuminate\Support\Collection; use Modules\Product\Entities\Product; class ProductTransformer { public function transform(Product $product): array { return [ 'id' => $product->id, 'name' => $product->name, 'slug' => $product->slug, 'price' => (float) $product->price, 'status' => $product->status, 'created_at' => $product->created_at->toIso8601String(), ]; } public function transformCollection(Collection $products): array { return $products->map(fn(Product $p) => $this->transform($p))->all(); } }
Complete controller example
// Modules/Product/Http/Controllers/ProductController.php namespace Modules\Product\Http\Controllers; use Illuminate\Http\JsonResponse; use Illuminate\Routing\Controller; use Modules\Product\Actions\CreateProductAction; use Modules\Product\Actions\UpdateProductAction; use Modules\Product\Actions\DeleteProductAction; use Modules\Product\Http\Requests\StoreProductRequest; use Modules\Product\Http\Transformers\ProductTransformer; use Modules\Product\Repositories\ProductRepository; class ProductController extends Controller { public function __construct( private readonly CreateProductAction $create, private readonly UpdateProductAction $update, private readonly DeleteProductAction $delete, private readonly ProductRepository $repository, private readonly ProductTransformer $transformer, ) {} public function index(): JsonResponse { return response()->json([ 'data' => $this->transformer->transformCollection($this->repository->all()), ]); } public function store(StoreProductRequest $request): JsonResponse { $product = ($this->create)($request->validated()); return response()->json(['data' => $this->transformer->transform($product)], 201); } public function show(int $id): JsonResponse { return response()->json(['data' => $this->transformer->transform($this->repository->find($id))]); } public function update(StoreProductRequest $request, int $id): JsonResponse { $product = ($this->update)($this->repository->find($id), $request->validated()); return response()->json(['data' => $this->transformer->transform($product)]); } public function destroy(int $id): JsonResponse { ($this->delete)($this->repository->find($id)); return response()->json(null, 204); } }
Configuration
Publish and edit config/module.php to customize module structure:
return [ 'namespace' => 'Modules', 'modules' => base_path('Modules'), 'modules_name' => base_path('modules_name.json'), // Format generated .php files with laravel/pint (PSR-12) after creation. // Requires "laravel/pint" in require-dev. Silently skipped if not installed. 'format_with_pint' => true, 'paths' => [ 'modules' => base_path('Modules'), 'generator' => [ 'model' => ['path' => 'Entities', 'generate' => true], 'migration' => ['path' => 'Database/Migrations','generate' => true], 'seeder' => ['path' => 'Database/Seeders', 'generate' => true], 'factory' => ['path' => 'Database/Factories', 'generate' => true], 'controller' => ['path' => 'Http/Controllers', 'generate' => true], 'request' => ['path' => 'Http/Requests', 'generate' => true], 'resource' => ['path' => 'Http/Resources', 'generate' => true], 'collection' => ['path' => 'Http/Resources', 'generate' => true], 'transformer' => ['path' => 'Http/Transformers', 'generate' => true], 'middleware' => ['path' => 'Http/Middleware', 'generate' => true], 'repository' => ['path' => 'Repositories', 'generate' => true], 'service' => ['path' => 'Services', 'generate' => true], 'action' => ['path' => 'Actions', 'generate' => true], 'event' => ['path' => 'Events', 'generate' => true], 'listener' => ['path' => 'Listeners', 'generate' => true], 'job' => ['path' => 'Jobs', 'generate' => true], 'cast' => ['path' => 'Casts', 'generate' => true], 'channel' => ['path' => 'Broadcasting', 'generate' => true], 'contract' => ['path' => 'Contracts', 'generate' => true], 'helper' => ['path' => 'Helpers', 'generate' => true], 'unit_test' => ['path' => 'Tests/Unit', 'generate' => true], 'route' => ['path' => 'Routes', 'generate' => true], 'route_service_provider' => ['path' => 'Providers', 'generate' => true], 'service_provider' => ['path' => 'Providers', 'generate' => true], ], ], ];
To disable a generator (for example if your team does not use Seeders):
'seeder' => ['path' => 'Database/Seeders', 'generate' => false],
Migration Commands
Migrations are scoped per module — you can run, rollback, or seed a single module without touching others.
# Run migrations for one module php artisan module:migrate Product # Run migrations for all modules php artisan module:migrate # Rollback (last 50 steps by default) php artisan module:migrate-rollback Product # Reset all module migrations php artisan module:migrate-reset Product # Refresh (rollback + re-run) php artisan module:migrate-refresh Product # Seed a module php artisan module:migrate-seed Product # Migration status php artisan module:migrate-status Product
Best Practices
Keep actions single-purpose. One action does one thing. CreateProductAction, PublishProductAction, DeleteProductAction — never ProductAction.
Depend on interfaces, not implementations. Bind your repository to an interface in the module's service provider so you can swap implementations in tests.
// In ProductServiceProvider::register() $this->app->bind(ProductRepositoryInterface::class, ProductRepository::class);
Fire events at the action level, not the controller. Actions are the natural boundary for domain events.
public function __invoke(array $data): Product { $product = $this->repository->create($data); event(new ProductCreated($product)); // ✅ here, not in the controller return $product; }
Let transformers control your API contract. Never return $model->toArray() directly from a controller — transformers are your public API contract and protect against accidentally exposing model internals.
Testing
# Run the package's own test suite composer test # Run only a specific test class vendor/bin/phpunit tests/Unit/ModelTest.php # Run with code coverage vendor/bin/phpunit --coverage-html coverage/
The package ships with 72 tests covering all generators, module management commands, and the core builder infrastructure (including state isolation between multiple sequential builds).
When writing tests for your modules, actions and transformers are the highest-value targets:
// Modules/Product/Tests/Unit/Actions/CreateProductActionTest.php namespace Modules\Product\Tests\Unit\Actions; use Modules\Product\Actions\CreateProductAction; use Modules\Product\Repositories\ProductRepository; use Tests\TestCase; class CreateProductActionTest extends TestCase { public function test_creates_product_and_fires_event(): void { Event::fake([ProductCreated::class]); $repository = $this->mock(ProductRepository::class); $repository->shouldReceive('create')->once()->andReturn($this->product()); $product = (new CreateProductAction($repository))(['name' => 'Widget']); $this->assertInstanceOf(Product::class, $product); Event::assertDispatched(ProductCreated::class); } }
Roadmap
Features planned for future releases, in rough priority order.
v1.1 — Developer experience
-
module:make-repositoryas a standalone command — currently only accessible as a--repositoryflag onmodule:make-controller. Will be available as a first-class generator. -
module:make-migrationwith alter-table mode —php artisan module:make-migration Product add_status_to_products. Stub and enum case are already in place; command pending. - PSR-4 verification on
module:enable— warn when enabling a module whose namespace is not incomposer.jsonautoload.
v1.2 — Inter-module communication
-
module:make-eventcross-module — declare that a Listener in moduleOrdershandles an Event from moduleProductwithout creating a tight dependency. Resolved through the service container. - Module dependency declaration — optional
module.jsonper module to declare"requires": ["Product"], validated onmodule:enableandmodule:optimize.
v1.3 — Advanced API patterns
-
module:make-policy— generate a Laravel Policy scoped to a module, with stubs for standard CRUD authorization. -
module:make-resource— standalone generator forJsonResourceandResourceCollection(currently accessible only as--resource/--collectionflags on controller). -
module:make-dto— generate a typed Data Transfer Object class for passing validated data between layers without relying on raw arrays. -
module:make-enum— PHP 8.1 native enum, placed inEnums/inside the module.
v1.4 — Tooling
-
module:make-contract+ auto-binding — generate an interface and automatically add thebind()call to the module's service provider. -
module:publish-stubs— allow teams to override individual stub files without publishing the full package, similar tophp artisan stub:publishin Laravel core. - GitHub Actions CI template —
module:make-workflowgenerates a.github/workflows/ci.ymlconfigured for the project's PHP and Laravel version.
Troubleshooting
Migrations not found
Ensure the module is registered in modules_name.json (it happens automatically when you use module:make-module or any module:make-* command). If you created files manually, run module:optimize to sync the file.
Classes not autoloading
Run composer dump-autoload after adding "Modules\\": "Modules/" to your composer.json.
Service provider not loading
Verify that modules_name.json contains your module name with true. Use php artisan module:list to inspect the state.
Queue jobs not processing
Jobs generated by module:make-job implement ShouldQueue by default. Ensure your queue driver is configured in .env (QUEUE_CONNECTION=redis or similar) and a worker is running.
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Write tests for new behaviour
- Run the test suite:
composer test - Ensure code is formatted:
composer pint - Open a Pull Request
All contributions must include tests and pass PSR-12 formatting. Breaking changes require a discussion issue first.
License
MIT — see LICENSE.
Support
- 🐛 Bug reports: GitHub Issues
- 💬 Discussions: GitHub Discussions
Inspired by nWidart/laravel-modules. Built for teams that live in the API layer.
strides/laravel-api-module 适用场景与选型建议
strides/laravel-api-module 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 14 次下载、GitHub Stars 达 0, 最近一次更新时间为 2024 年 01 月 06 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「module」 「modules」 「laravel」 「strides」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 strides/laravel-api-module 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 strides/laravel-api-module 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 strides/laravel-api-module 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
This is Paymorrow module for OXID eShop.
Analysis module for finding problematical shop data.
yii2 swiper slider
An interactive tour through the TYPO3 backend.
bootstrap select field module implementing http://silviomoreto.github.io/bootstrap-select/
Yii2 module to manage website content. Kind of a CMS...
统计信息
- 总下载量: 14
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 20
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2024-01-06