DEV Community

Cover image for Generate your entire Laravel CRUD stack with one Artisan command
Jean marie Bissari
Jean marie Bissari

Posted on

Generate your entire Laravel CRUD stack with one Artisan command

TL;DRcomposer require bouda/laravel-make-patternphp artisan make:pattern Post → 9 consistent files in seconds. DDD-ready, rollback included, every stub is yours to override.


The problem I kept running into

Every new Laravel project starts the same way. You know the architecture you want: Repository, Service, Controller, some Form Requests, a Resource, a Policy, a test. You've written this stack dozens of times. And every time, you either:

  • Copy-paste from a previous project — and immediately introduce inconsistency between how PostRepository is structured vs CategoryRepository.
  • Write everything from scratch — which is slow and error-prone.
  • Use make:model -a — which gives you the Model, Migration, Factory, Controller, but nothing about repositories, services, or policies wired together.

None of these feel like the right answer when you want a clean, layered architecture.

So I built laravel-make-pattern.


What it does

One command:

php artisan make:pattern Post
Enter fullscreen mode Exit fullscreen mode

Generates 9 files:

app/Models/Post.php
app/Repositories/Contracts/PostRepositoryInterface.php
app/Repositories/PostRepository.php
app/Services/PostService.php
app/Http/Controllers/PostController.php
app/Http/Requests/PostStoreRequest.php
app/Http/Requests/PostUpdateRequest.php
app/Http/Resources/PostResource.php
app/Policies/PostPolicy.php
tests/Feature/PostTest.php
Enter fullscreen mode Exit fullscreen mode

All consistently named, all using the same conventions, all generated from stubs you own and can override.


The generated code

Here's what the repository looks like out of the box:

<?php

namespace App\Repositories;

use App\Models\Post;
use App\Repositories\Contracts\PostRepositoryInterface;

class PostRepository implements PostRepositoryInterface
{
    public function all()
    {
        return Post::all();
    }

    public function find(string $id)
    {
        return Post::findOrFail($id);
    }

    public function create(array $data)
    {
        return Post::create($data);
    }

    public function update(string $id, array $data)
    {
        $model = $this->find($id);
        $model->update($data);
        return $model;
    }

    public function delete(string $id)
    {
        $model = $this->find($id);
        $model->delete();
    }
}
Enter fullscreen mode Exit fullscreen mode

Real, runnable code — not a stub you still have to fill in for 20 minutes.


Features

🏗 DDD / Domain mode

One flag changes everything:

php artisan make:pattern Post --domain=Blog
Enter fullscreen mode Exit fullscreen mode

Generates everything under app/Domain/Blog/ with auto-adapted namespaces:

app/Domain/Blog/Models/Post.php
app/Domain/Blog/Repositories/Contracts/PostRepositoryInterface.php
app/Domain/Blog/Repositories/PostRepository.php
app/Domain/Blog/Services/PostService.php
app/Domain/Blog/Http/Controllers/PostController.php
Enter fullscreen mode Exit fullscreen mode

App\Domain\Blog\Models, App\Domain\Blog\Services — no manual find-and-replace.

🔧 Custom root namespace

php artisan make:pattern Post --namespace=Acme
# => Acme\Models\Post, Acme\Services\PostService, etc.

# Combine with --domain:
php artisan make:pattern Post --domain=Blog --namespace=Acme
# => Acme\Domain\Blog\Models\Post
Enter fullscreen mode Exit fullscreen mode

🎯 Generate only what you need

Already have a Model? Skip it:

php artisan make:pattern Post --only=repository,service,controller
Enter fullscreen mode Exit fullscreen mode

↩️ Rollback support

Every run is recorded in an append-only audit log:

php artisan make:pattern:history
Enter fullscreen mode Exit fullscreen mode
+----+----------+------------------+-------+
| ID | Entity   | Date             | Files |
+----+----------+------------------+-------+
| 3  | Comment  | 2026-08-03 09:15 | 9     |
| 2  | Category | 2026-08-03 09:10 | 9     |
| 1  | Post     | 2026-08-03 09:02 | 9     |
+----+----------+------------------+-------+
Enter fullscreen mode Exit fullscreen mode

Undo the last run:

php artisan make:pattern:undo
Enter fullscreen mode Exit fullscreen mode

Target a specific run by ID — files modified since generation are reported, not silently deleted:

php artisan make:pattern:undo --id=01ARZ3NDEKTSV4RRFFQ69G5FAV
Enter fullscreen mode Exit fullscreen mode

📝 Fully customizable stubs

php artisan vendor:publish --tag=make-pattern-stubs
Enter fullscreen mode Exit fullscreen mode

Edit any stub (repository.stub, service.stub, controller.stub...) and the generator uses your version automatically. Override one layer, leave the rest untouched.

🔒 Repository with error logging (opt-in)

Set this in your config:

// config/make-pattern.php
'wrap_repository_calls' => true,
Enter fullscreen mode Exit fullscreen mode

Every mutating method is then generated with a try/catch that logs before rethrowing:

public function create(array $data)
{
    try {
        return Post::create($data);
    } catch (\Throwable $e) {
        Log::error('PostRepository::create failed', ['exception' => $e]);
        throw $e;
    }
}
Enter fullscreen mode Exit fullscreen mode

🏷 Primary key strategy

'primary_key' => [
    'strategy' => 'ulid', // or 'uuid' or 'increment'
],
Enter fullscreen mode Exit fullscreen mode

Installation

composer require bouda/laravel-make-pattern
Enter fullscreen mode Exit fullscreen mode

Auto-discovered — nothing to register manually.

Publish the config (optional):

php artisan vendor:publish --tag=make-pattern-config
Enter fullscreen mode Exit fullscreen mode

Requirements: PHP 8.2+, Laravel 11, 12, or 13.


Why not make:model -a?

make:model -a is great for an Eloquent-centric approach: Model, migration, factory, seeder, controller. It's the right tool if you stay close to Eloquent.

make:pattern targets a different need — teams that want a layered architecture (Repository pattern, Service layer) but don't want to hand-write the same interfaces, bindings, and boilerplate every single time.

It's not opinionated about what you put inside these files — the stubs are yours. It's opinionated about the structure, which is exactly what keeps a codebase consistent across a team.


Links

If you find it useful, a ⭐ on GitHub goes a long way. Issues and PRs are very welcome.

Top comments (0)