PHPackages                             ahmedebead/lara-crud - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. ahmedebead/lara-crud

ActiveLibrary[API Development](/categories/api)

ahmedebead/lara-crud
====================

A comprehensive Laravel package to generate CRUD operations with advanced features for APIs and web applications.

v0.7.0(2mo ago)24.0kMITPHPPHP ^8.2|^8.3

Since Sep 7Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/ahmed3bead/lara-crud)[ Packagist](https://packagist.org/packages/ahmedebead/lara-crud)[ Docs](https://github.com/ahmedebead/lara-crud)[ RSS](/packages/ahmedebead-lara-crud/feed)WikiDiscussions main Synced yesterday

READMEChangelog (10)Dependencies (5)Versions (33)Used By (0)

 [![Lara-CRUD Banner](https://raw.githubusercontent.com/ahmed3bead/lara-crud/main/lara-crud-banner.png)](https://raw.githubusercontent.com/ahmed3bead/lara-crud/main/lara-crud-banner.png)

Lara-CRUD
=========

[](#lara-crud)

A Laravel package that generates complete CRUD scaffolding and provides a layered service architecture out of the box.

**Requires:** PHP 8.2+, Laravel 10–13, `spatie/laravel-query-builder` ^5|^6|^7

```
composer require ahmedebead/lara-crud
```

---

Table of Contents
-----------------

[](#table-of-contents)

1. [How it works](#how-it-works)
2. [Generate a module](#generate-a-module)
3. [What gets generated](#what-gets-generated)
4. [Layer by layer](#layer-by-layer)
    - [Model](#model)
    - [Repository](#repository)
    - [Service — Resource mode (default)](#service--resource-mode-default)
    - [Service — Mapper mode](#service--mapper-mode)
    - [Controller](#controller)
    - [Request](#request)
    - [Response envelope](#response-envelope)
5. [Hook system](#hook-system)
6. [Configuration reference](#configuration-reference)
7. [Artisan commands](#artisan-commands)
8. [Contributing / License](#contributing--license)

---

How it works
------------

[](#how-it-works)

Every generated module follows the same layered flow:

```
HTTP Request → Controller → Service → Repository → Model

```

LayerResponsibilityControllerResolves request class, delegates to service, returns JSONServiceBusiness logic, wraps results in `BaseResponse`RepositoryData access via Spatie QueryBuilderModelEloquent model with allowed filters/includes/sorts---

Generate a module
-----------------

[](#generate-a-module)

```
php artisan lara-crud:go
```

The wizard asks for the table name and generates everything. Publish the config and stubs if you want to customise the defaults:

```
php artisan vendor:publish --provider="Ahmed3bead\LaraCrud\LaraCrudServiceProvider"
```

Config lands at `config/lara_crud.php`. Stubs land at `resources/stubs/vendor/lara-crud/`.

---

What gets generated
-------------------

[](#what-gets-generated)

```
app/{MainContainer}/{ModuleName}/
├── Controllers/       ProductsController.php
├── Models/            Product.php
├── Repositories/      ProductsRepository.php
├── Services/          ProductsService.php
├── Requests/          CreateProductRequest.php  UpdateProductRequest.php  ...
├── Resources/         ProductShowResource.php   ProductListResource.php
├── DTOs/              ProductDTO.php
├── Mappers/           ProductDTOMapper.php
├── Filters/           ProductFilter.php
├── Policies/          ProductPolicy.php
├── Events/            ProductCreated.php  ...
├── Notifications/
├── Scopes/
└── Traits/

```

---

Layer by layer
--------------

[](#layer-by-layer)

### Model

[](#model)

`BaseModel` (integer PK) · `BaseUuidModel` · `BaseUlidModel`

Override these static methods to control filtering and sorting:

```
class Product extends BaseModel
{
    protected $fillable = ['name', 'price', 'category_id'];

    public static function getAllowedFilters(): array
    {
        return ['name', 'category_id', AllowedFilter::scope('active')];
    }

    public static function getAllowedIncludes(): array
    {
        return ['category', 'tags'];
    }

    // Sort direction is passed in by the repository — no request() calls here
    public static function getDefaultSort(bool $sortAsc = false): string
    {
        return $sortAsc ? 'created_at' : '-created_at';
    }
}
```

---

### Repository

[](#repository)

`BaseRepository` provides all common data access methods. Inject it; do not call it directly from controllers.

```
// Available out of the box:
$repo->paginate($requestQuery, $perPage);
$repo->all();
$repo->find($id);                          // findOrFail
$repo->create(array $data);
$repo->update($model, array $data);
$repo->delete($model);
$repo->count(array $filters = []);
$repo->exists(array $filters);
$repo->findMany(array $ids);
$repo->createMany(array $records);         // bulk insert
$repo->findWhere(array $conditions);
$repo->firstWhere(array $conditions);
$repo->minimalListWithFilter(with: [], where: [], limit: 250);
```

Extend it to add module-specific queries:

```
class ProductsRepository extends BaseRepository
{
    public function __construct()
    {
        parent::__construct(new Product, new ProductSelector);
    }

    public function findByCategory(int $categoryId): Collection
    {
        return $this->getModel()->where('category_id', $categoryId)->get();
    }
}
```

---

### Service — Resource mode (default)

[](#service--resource-mode-default)

The generated service uses Laravel API Resources by default. `ShowResource` wraps single items; `ListResource` wraps collections.

```
class ProductsService extends BaseService
{
    protected string $resourceClass     = ProductShowResource::class;
    protected string $listResourceClass = ProductListResource::class;

    public function __construct(ProductsRepository $repository)
    {
        parent::__construct($repository);
    }
}
```

`BaseService` provides these methods automatically:

MethodHTTP verbWraps with`paginate($request)`GET /index`$listResourceClass::collection()``all()`GET ?getAllRecords`$listResourceClass::collection()``show($id)`GET /show`new $resourceClass()``create($data)`POST`new $resourceClass()``update($data, $id)`PUT/PATCH`new $resourceClass()``delete($id)`DELETE204 message---

### Service — Mapper mode

[](#service--mapper-mode)

Replace the resource properties with a DTOMapper if you prefer manual transformation:

```
class ProductsService extends BaseService
{
    public function __construct(
        ProductsRepository $repository,
        ProductDTOMapper   $mapper
    ) {
        parent::__construct($repository, $mapper);
    }
}
```

The mapper must extend `BaseDTOMapper` and implement `fromModel()`. The base class handles `fromCollection()`, `fromArray()`, and `fromPaginator()` automatically.

---

### Service — custom transformation

[](#service--custom-transformation)

Override `getResourceByType()` for full control:

```
public function getResourceByType(string $type, $data = null): mixed
{
    return match($type) {
        'list'  => ProductListResource::collection($data),
        default => new ProductShowResource($data),
    };
}
```

---

### Controller

[](#controller)

`BaseController` resolves the request class from `$requestMap`, validates it, and calls the service. You never write try-catch boilerplate — Laravel's exception handler takes care of it.

```
class ProductsController extends BaseController
{
    protected array $requestMap = [
        'index'  => IndexProductRequest::class,
        'create' => CreateProductRequest::class,
        'update' => UpdateProductRequest::class,
        'delete' => DeleteProductRequest::class,
        'show'   => ShowProductRequest::class,
    ];

    public function __construct(ProductsService $service)
    {
        parent::__construct($service);
    }
}
```

If a key is missing from `$requestMap` a descriptive `RuntimeException` is thrown immediately.

---

### Request

[](#request)

`BaseRequest` defaults to `authorize(): bool { return true; }`. Override it per request class to enforce policies:

```
class CreateProductRequest extends BaseRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('create', Product::class);
    }

    public function rules(): array
    {
        return [
            'name'  => 'required|string|max:255',
            'price' => 'required|numeric|min:0',
        ];
    }
}
```

---

### Response envelope

[](#response-envelope)

Every service method returns a `BaseResponse`. The controller's `tryAndResponse()` converts it to JSON.

```
{
    "status_code": 200,
    "data": { ... },
    "extra_data": {},
    "meta": {
        "currentPage": 1,
        "lastPage": 5,
        "perPage": 20,
        "total": 98
    },
    "errors": {},
    "message": "",
    "source": "OPs"
}
```

The `debug` block (query log) is **never included** unless you opt in via config — it will never leak to production responses:

```
// config/lara_crud.php
'expose_debug_in_response' => env('LARA_CRUD_EXPOSE_DEBUG', false),
```

---

Hook system
-----------

[](#hook-system)

Hooks let you attach code to any service method lifecycle without touching the method itself.

### Execution flow

[](#execution-flow)

```
Before hooks (sync)   → validation, authorization, rate-limit checks
       ↓
Core service method   → the actual create/update/delete/show/paginate
       ↓
After hooks           → notifications, audit log, cache invalidation
       ↓
Error hooks           → alerting, rollback side-effects

```

### Creating a hook

[](#creating-a-hook)

```
php artisan lara-crud:hook ProductAuditHook
```

```
class ProductAuditHook extends BaseHookJob
{
    public function handle(HookContext $context): void
    {
        $model = $context->getModelFromResult();

        ActivityLog::record(
            user:    $context->user,
            action:  $context->method,
            subject: $model,
        );
    }

    public function shouldExecute(HookContext $context): bool
    {
        return $context->isAfter() && $context->isSuccessful();
    }
}
```

### Registering hooks in a service

[](#registering-hooks-in-a-service)

Override `registerHooks()` in your service:

```
class ProductsService extends BaseService
{
    protected string $resourceClass     = ProductShowResource::class;
    protected string $listResourceClass = ProductListResource::class;

    public function __construct(ProductsRepository $repository)
    {
        parent::__construct($repository);
    }

    protected function registerHooks(): void
    {
        parent::registerHooks();

        // Runs synchronously before every create
        $this->addServiceSyncHook('before', 'create', ValidateSkuHook::class);

        // Queued after create/update/delete — does not slow down the response
        $this->addServiceQueuedHook('after', 'create', SendProductCreatedEmailHook::class);
        $this->addServiceQueuedHook('after', 'update', InvalidateProductCacheHook::class);
        $this->addServiceQueuedHook('after', 'delete', InvalidateProductCacheHook::class);
    }
}
```

### Hook execution strategies

[](#hook-execution-strategies)

MethodBehavior`addServiceSyncHook`Immediate, blocks the request`addServiceQueuedHook`Dispatched to a queue`addServiceDelayedHook`Queued with a delay (seconds)`addServiceBatchedHook`Accumulated into a batch job### Extension points in BaseService

[](#extension-points-in-baseservice)

Override these empty methods instead of registering hooks manually when you want category-level grouping:

```
// Runs when hooks.default_service_hooks.global = true
protected function registerGlobalServiceHooks(): void
{
    $this->addServiceSyncHook('before', 'create', AuthorizationHook::class);
}

// Runs when hooks.default_service_hooks.crud = true
protected function registerCrudHooks(): void
{
    $this->addServiceQueuedHook('after', 'create', NotifyAdminHook::class);
}

// Runs when hooks.default_service_hooks.performance = true
protected function registerPerformanceHooks(): void {}

// Runs when hooks.default_service_hooks.caching = true
protected function registerCachingHooks(): void {}
```

### HookContext reference

[](#hookcontext-reference)

`HookContext` is passed to every hook's `handle()` method:

```
$context->method            // 'create', 'update', 'delete', 'show', 'paginate', ...
$context->phase             // 'before' | 'after' | 'error'
$context->data              // raw input passed to the service method
$context->parameters        // extra parameters array
$context->result            // the BaseResponse (after phase only)
$context->service           // the service instance
$context->user              // Auth::user()

// Helpers
$context->isBefore()
$context->isAfter()
$context->isSuccessful()    // status 2xx
$context->getModelFromResult()      // extracts Eloquent model from result
$context->getDataFromResult()       // extracts data payload from result
$context->getStatusCode()
$context->getMessage()
$context->getModelAttributes()
$context->getModelChanges()
$context->wasModelRecentlyCreated()
```

### Hook management commands

[](#hook-management-commands)

```
php artisan lara-crud:hooks list          # list all registered hooks
php artisan lara-crud:hooks stats         # execution statistics
php artisan lara-crud:hooks debug         # debug a specific service
php artisan lara-crud:hooks enable        # enable hooks globally
php artisan lara-crud:hooks disable       # disable hooks globally
php artisan lara-crud:hooks clear         # remove all hooks
php artisan lara-crud:hooks test          # test-fire a hook
php artisan lara-crud:hooks export        # export hook config to JSON
```

---

Configuration reference
-----------------------

[](#configuration-reference)

`config/lara_crud.php`

```
'api_version'              => 'V1',
'dto_enabled'              => false,
'api_resource_enabled'     => true,
'primary_key_fields_type'  => 'id',   // 'id' | 'uuid' | 'ulid'
'ui_mode'                  => 'bootstrap', // 'bootstrap' | 'adminlte'
'policies_enabled'         => false,

// Set to true only in local/staging — query logs will appear in every API response
'expose_debug_in_response' => env('LARA_CRUD_EXPOSE_DEBUG', false),

'hooks' => [
    'enabled'          => env('LARA_CRUD_HOOKS_ENABLED', true),
    'debug'            => env('LARA_CRUD_HOOKS_DEBUG', false),
    'queue_connection' => env('LARA_CRUD_QUEUE_CONNECTION', 'default'),
    'batch_queue'      => env('LARA_CRUD_BATCH_QUEUE', 'batch'),

    'default_service_hooks' => [
        'global'      => true,   // registerGlobalServiceHooks()
        'crud'        => true,   // registerCrudHooks()
        'performance' => false,  // registerPerformanceHooks()
        'caching'     => false,  // registerCachingHooks()
    ],
],

'dirs' => [
    'main-container-dir-name' => 'YourApp',
    // ...
],
```

---

Artisan commands
----------------

[](#artisan-commands)

CommandPurpose`lara-crud:go`Interactive CRUD generator wizard`lara-crud:hook {name}`Generate a hook class`lara-crud:hooks`Manage hooks (list/stats/debug/clear/enable/disable/test/export)`lara-crud:api-controller`Generate API controller only`lara-crud:model`Generate model only`lara-crud:test`Generate unit test`lara-crud:export-table`Export table schema to JSON---

Contributing / License
----------------------

[](#contributing--license)

Contributions are welcome. Please open an issue or pull request on [GitHub](https://github.com/ahmedebead/lara-crud).

This package is open-sourced software licensed under the [MIT license](LICENSE).

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance87

Actively maintained with recent releases

Popularity24

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 96.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~20 days

Recently: every ~7 days

Total

31

Last Release

65d ago

PHP version history (4 changes)0.0.1PHP ^8.0|^8.1|8.2

v0.3.0PHP ^8.0|^8.1|^8.2|^8.3

v0.4.5PHP ^8.1|^8.2|^8.3

v0.5.0PHP ^8.2|^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/98d9e0c899adb8f509d8d30bfaaaff6393df92ed0daadc384c16c201d4b8e52c?d=identicon)[ahmedm3bead](/maintainers/ahmedm3bead)

---

Top Contributors

[![AhmedMEbead](https://avatars.githubusercontent.com/u/157358238?v=4)](https://github.com/AhmedMEbead "AhmedMEbead (51 commits)")[![ahmed3bead](https://avatars.githubusercontent.com/u/60138292?v=4)](https://github.com/ahmed3bead "ahmed3bead (2 commits)")

---

Tags

apilaravelgeneratorscaffoldservicecrudrepositorydto

### Embed Badge

![Health badge](/badges/ahmedebead-lara-crud/health.svg)

```
[![Health](https://phpackages.com/badges/ahmedebead-lara-crud/health.svg)](https://phpackages.com/packages/ahmedebead-lara-crud)
```

###  Alternatives

[darkaonline/l5-swagger

OpenApi or Swagger integration to Laravel

3.0k37.6M134](/packages/darkaonline-l5-swagger)[knuckleswtf/scribe

Generate API documentation for humans from your Laravel codebase.✍

2.3k14.2M63](/packages/knuckleswtf-scribe)[typicms/base

A modular multilingual CMS built with Laravel, enabling developers to manage structured content like pages, news, events, and more.

1.6k20.4k](/packages/typicms-base)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
