承接 strides/laravel-api-module 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

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.

Latest Stable Version PHP Version Laravel Version License Tests

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

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-repository as a standalone command — currently only accessible as a --repository flag on module:make-controller. Will be available as a first-class generator.
  • module:make-migration with alter-table modephp 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 in composer.json autoload.

v1.2 — Inter-module communication

  • module:make-event cross-module — declare that a Listener in module Orders handles an Event from module Product without creating a tight dependency. Resolved through the service container.
  • Module dependency declaration — optional module.json per module to declare "requires": ["Product"], validated on module:enable and module: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 for JsonResource and ResourceCollection (currently accessible only as --resource/--collection flags 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 in Enums/ inside the module.

v1.4 — Tooling

  • module:make-contract + auto-binding — generate an interface and automatically add the bind() call to the module's service provider.
  • module:publish-stubs — allow teams to override individual stub files without publishing the full package, similar to php artisan stub:publish in Laravel core.
  • GitHub Actions CI templatemodule:make-workflow generates a .github/workflows/ci.yml configured 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

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Write tests for new behaviour
  4. Run the test suite: composer test
  5. Ensure code is formatted: composer pint
  6. 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

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 我们能提供哪些服务?
定制开发 / 二次开发

基于 strides/laravel-api-module 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 14
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 20
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-01-06