PHPackages                             muhammedsalama/laravel-base - 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. muhammedsalama/laravel-base

ActiveLibrary[API Development](/categories/api)

muhammedsalama/laravel-base
===========================

Laravel module generator: one command scaffolds Model, Migration, Enum, Filter+Pagination, Interface, Repository, Service, Form Requests, API Resource, Policy, Controller (Swagger/OA), and Tests.

v3.0.4(4w ago)6131MITPHPPHP ^8.1CI passing

Since May 31Pushed 2w agoCompare

[ Source](https://github.com/MuhammedMSalama/LaravelBase)[ Packagist](https://packagist.org/packages/muhammedsalama/laravel-base)[ Docs](https://github.com/Muhammed2024Salama/LaravelBase)[ RSS](/packages/muhammedsalama-laravel-base/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (5)Dependencies (15)Versions (25)Used By (0)

Laravel Base
============

[](#laravel-base)

 One command. A complete, production-ready API module.
 Model · Migration · Enum · Filter · Repository · Service · Requests · Resource · Policy · Controller · Tests

 [ ![Latest Version on Packagist](https://camo.githubusercontent.com/97a4691d61d6e65311c7a421def3b1a5f2132f641882a192b0091c850c706cd2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d7568616d6d656473616c616d612f6c61726176656c2d626173653f7374796c653d666c61742d737175617265) ](https://packagist.org/packages/muhammedsalama/laravel-base) [ ![Total Downloads](https://camo.githubusercontent.com/fa59a527c37fcaef8bccae3fcbb33f5121f121417263904e413e122fe390a812/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d7568616d6d656473616c616d612f6c61726176656c2d626173653f7374796c653d666c61742d737175617265) ](https://packagist.org/packages/muhammedsalama/laravel-base) [ ![Tests](https://camo.githubusercontent.com/dce86633789fc21045a987c12fc9c9c79609b10dccb7785fbab82fbace38e585/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4d7568616d6d65644d53616c616d612f4c61726176656c426173652f63692e796d6c3f6272616e63683d6d61696e267374796c653d666c61742d737175617265266c6162656c3d7465737473) ](https://github.com/MuhammedMSalama/LaravelBase/actions/workflows/ci.yml) [ ![PHPStan Level 5](https://camo.githubusercontent.com/858ba49b4f650d5848b783ce7017c8872144428e62cd948e7b4d7bd815c8ffed/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230352d627269676874677265656e3f7374796c653d666c61742d737175617265) ](https://github.com/MuhammedMSalama/LaravelBase/actions/workflows/ci.yml) [ ![Code Coverage](https://camo.githubusercontent.com/83a2ff3318dcfac2ad448d989182844938f99c58dbd12ce4508a1ecd27c5568e/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f4d7568616d6d65644d53616c616d612f4c61726176656c426173652f6d61696e3f7374796c653d666c61742d737175617265266c6162656c3d636f766572616765) ](https://codecov.io/gh/MuhammedMSalama/LaravelBase) [ ![PHP Version](https://camo.githubusercontent.com/b85fa8c4f61610b8efc6c81b15c7b6efc79b87623697804b828ac7160557a69e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6d7568616d6d656473616c616d612f6c61726176656c2d626173653f7374796c653d666c61742d737175617265) ](https://packagist.org/packages/muhammedsalama/laravel-base) [ ![License: MIT](https://camo.githubusercontent.com/422db9fd40f5831c765cf6530b6750c081b696bd18d904cf89554df98c676277/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e3f7374796c653d666c61742d737175617265) ](LICENSE) [ ![CI](https://camo.githubusercontent.com/a5d91d8e858faa23a80158f9f3f300698a8715ac3242eda5c69aee6cca0d9cd5/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4d7568616d6d65644d53616c616d612f4c61726176656c426173652f63692e796d6c3f6272616e63683d6d61696e267374796c653d666c61742d737175617265266c6162656c3d4349) ](https://github.com/MuhammedMSalama/LaravelBase/actions)

 [![make:module Product demo](docs/demo.gif)](docs/demo.gif)

---

```
php artisan make:module Product
```

One command generates **15 files** across every layer of your REST API:

LayerWhat is generatedDataModel, driver-aware migration, repository interface, repositoryDomainService, filter class (whitelist-safe column filtering + pagination)HTTPController (Swagger-annotated), store &amp; update form requests, API resource, resource collectionAuthPolicy with CRUD gates, wired into every controller actionStatusPHP 8.1 backed string enum (`active / inactive / pending`), auto-cast by the modelTestsFeature test + unit test stubs — pre-skipped so CI is green from commit oneThen add one route:

```
Route::apiResource('products', ProductController::class);
```

Your API is running, documented, authorized, and tested.

---

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

[](#table-of-contents)

- [Why LaravelBase?](#why-laravelbase)
- [Features](#features)
- [Laravel Artisan vs LaravelBase](#laravel-artisan-vs-laravelbase)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Architecture overview](#architecture-overview)
- [make:module — the module generator](#makemodule--the-module-generator)
    - [Component table](#component-table)
    - [--only and --except](#--only-and---except)
    - [Per-component --no-\* flags](#per-component---no--flags)
    - [Other options](#other-options)
- [Filter + Pagination API](#filter--pagination-api)
- [Driver-aware migrations](#driver-aware-migrations)
- [Swagger / OpenAPI docs](#swagger--openapi-docs)
- [Enums](#enums)
- [Policy](#policy)
- [API Resource](#api-resource)
- [Generated tests](#generated-tests)
- [Repository auto-binding](#repository-auto-binding)
- [make:repository — deprecated alias](#makerepository--deprecated-alias)
- [base:create-database](#basecreate-database)
- [Manual setup](#manual-setup)
- [API Reference](#api-reference)
    - [RepositoryInterface](#repositoryinterface)
    - [BaseRepository](#baserepository)
    - [ServiceInterface](#serviceinterface)
    - [BaseService](#baseservice)
    - [BaseRequest](#baserequest)
    - [ApiResponse](#apiresponse)
    - [ApiResponseTrait](#apiresponsetrait)
    - [ImageUploadTrait](#imageuploadtrait)
- [Configuration](#configuration)
- [Publishing helpers and traits](#publishing-helpers-and-traits)
- [Testing and static analysis](#testing-and-static-analysis)
- [Troubleshooting](#troubleshooting)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [Security](#security)
- [License](#license)
- [Author](#author)

---

Why LaravelBase?
----------------

[](#why-laravelbase)

Every Laravel API project ends up writing the same boilerplate: a repository, a service, a form request that returns JSON errors, and a handful of response helpers. You write them once, then copy-paste across every project.

LaravelBase eliminates that tax. The package ships pre-written, production-hardened base classes and a generator that scaffolds a complete vertical slice — not just the model, not just the controller, but every layer wired together and ready to ship:

- **No `ServiceProvider` edits for the common case** — interfaces are auto-bound to implementations by naming convention at boot.
- **No column-injection vulnerabilities** — `AbstractFilter` accepts only columns you explicitly whitelist; unknown request parameters are silently ignored.
- **No documentation debt** — generated controllers carry `@OA` Swagger annotations; install `l5-swagger` and your API is self-documented.
- **No broken CI** — generated test stubs are pre-skipped so your pipeline is green from the first commit.
- **Your code, your rules** — generated files live in `app/`; edit, extend, or delete them freely. They are never re-generated unless you pass `--force`.

The generated code is idiomatic Laravel — no framework-within-a-framework, no magic, no lock-in.

---

Features
--------

[](#features)

FeatureDetails**Module generator**`make:module` scaffolds 15 components in one command: Model, Migration, Enum, Filter, Interface, Repository, Service, Store/Update Requests, API Resource + Collection, Policy, Controller, Feature test, Unit test**Filter + Pagination**`AbstractFilter` base class: whitelist-safe column filters, LIKE search, sort-by whitelist, per-page clamping — ergonomic `$service->filter($request)->paginate()` API**Driver-aware migrations**Detects MySQL / PostgreSQL / SQLite at runtime; uses `json()` on MySQL/PG, `text()` fallback otherwise**Swagger / OpenAPI**Generated controllers carry `@OA\*` PHPDoc annotations (l5-swagger / swagger-php compatible) — optional, gated behind `suggest`**Status Enums**PHP 8.1 backed string enum (`Active`, `Inactive`, `Pending`) with `label()`, `isActive()`, `values()`; model casts `status` automatically**Policy**Full CRUD policy stub with `HandlesAuthorization`; controller calls `$this->authorize()` for every action**API Resource**`JsonResource` + `ResourceCollection` with `@OA\Schema`; controller returns wrapped resources via `ApiResponse`**Generated tests**Feature + Unit test stubs that pass (skipped) from the first commit; inline TODO instructions**Repository pattern**`BaseRepository` with `all`, `paginate`, `find`, `findOrFail`, `findBy`, `create`, `update`, `delete`, `query`**Service layer**`BaseService` wrapping any repository; extend to add domain logic**Contracts-first design**`RepositoryInterface` / `ServiceInterface` with full native type hints**External validation**`BaseRequest` returns a standard 422 JSON envelope on failure — no extra code**Consistent API responses**Static `ApiResponse` helper + `ApiResponseTrait` — identical JSON shape everywhere**Image handling**Secure upload / update / delete via `ImageUploadTrait` (MIME-derived extension)**Auto-binding**`*RepositoryInterface` auto-bound to `*Repository` by naming convention — no manual registration**Laravel 10/11/12/13, PHP 8.1+**No upper-bound constraint on Laravel version---

Laravel Artisan vs LaravelBase
------------------------------

[](#laravel-artisan-vs-laravelbase)

CapabilityLaravel Artisan (built-in)LaravelBase v3Generate Eloquent model`make:model`Included in `make:module` by defaultGenerate migration`make:migration` (separate command)Auto-generated, **driver-aware** — `json()` on MySQL/PG, `text()` with notice on SQLiteRepository patternNot provided`BaseRepository` + `RepositoryInterface` + generated `{Name}Repository` + `{Name}RepositoryInterface`Service layerNot provided`BaseService` + `ServiceInterface` + generated `{Name}Service`Controller`make:controller --api` (empty method bodies)Full CRUD controller with `$this->authorize()`, typed requests, `ApiResponse` calls, and Swagger annotationsForm Requests`make:request` (separate command, manual wiring)`Store{Name}Request` + `Update{Name}Request` generated and wired; `BaseRequest` returns JSON 422 envelope automaticallyJSON API response helperNot provided`ApiResponse` — `success` / `created` / `error` / `notFound` / `paginated` with a consistent `{status, message, data, meta}` envelopeStatus EnumNot providedPHP 8.1 backed string enum with `label()`, `isActive()`, `values()`; model casts `status` automaticallyQuery filtering + paginationNot provided`AbstractFilter`: whitelisted column filters, LIKE search, global `?search=`, sort whitelist, `?per_page=` clamped to \[1, 100\]API Resource + Collection`make:resource` (separate command)`{Name}Resource` + `{Name}ResourceCollection` generated and wired into controller; includes `@OA\Schema` annotationPolicy`make:policy` (separate command, manual registration)`{Name}Policy` with `HandlesAuthorization`, all CRUD gates; controller calls `$this->authorize()` for every actionSwagger / OpenAPI docsNot providedAll 5 CRUD actions annotated (`@OA\Get/Post/Put/Delete`); compatible with `darkaonline/l5-swagger`Repository auto-bindingNot providedConvention-based: `*RepositoryInterface` → `*Repository` resolved at boot; no `ServiceProvider` edit requiredExplicit provider bindingNot provided`--provider` flag creates/updates `RepositoryServiceProvider`; idempotent on re-runsGenerated testsNot providedFeature + Unit test stubs, pre-skipped (CI stays green), with inline TODO instructionsDatabase creation commandNot provided`base:create-database [--connection=]` — MySQL and PostgreSQLImage upload helperNot provided`ImageUploadTrait`: MIME-derived extension (prevents extension-spoofing), upload / update / deleteSubset generationN/A`--only=` / `--except=` — generate any combination of the 15 componentsPer-component skip flagsN/A`--no-model`, `--no-migration`, `--no-enum`, `--no-filter`, `--no-service`, `--no-request`, `--no-resource`, `--no-policy`, `--no-controller`, `--no-test`Laravel version supportCurrent only10, 11, 12, 13PHP version supportCurrent only8.1, 8.2, 8.3, 8.4CI matrixN/APHP 8.1–8.4 × Laravel 10–13 + prefer-lowest + PHPStan level 5 + `make:module` smoke test---

Requirements
------------

[](#requirements)

DependencyVersionPHP`^8.1`Laravel`10.x` and above (10 / 11 / 12 / 13)---

Installation
------------

[](#installation)

```
composer require muhammedsalama/laravel-base
```

The service provider registers automatically via Laravel's package auto-discovery. No manual configuration required.

Install from GitHub (VCS repository)```
"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/MuhammedMSalama/LaravelBase"
    }
]
```

```
composer require muhammedsalama/laravel-base:^3.0
```

Install from a local path (package development)```
"repositories": [
    {
        "type": "path",
        "url": "../laravel-base"
    }
],
"require": {
    "muhammedsalama/laravel-base": "*"
}
```

---

Quick Start
-----------

[](#quick-start)

Generate a complete `Product` module with one command:

```
php artisan make:module Product
```

This creates 15 files — the full vertical slice of a REST API module:

```
app/
  Enums/ProductStatus.php
  Filters/ProductFilters.php
  Interfaces/ProductRepositoryInterface.php
  Repositories/ProductRepository.php
  Services/ProductService.php
  Models/Product.php
  Policies/ProductPolicy.php
  Http/
    Controllers/ProductController.php
    Requests/Product/
      StoreProductRequest.php
      UpdateProductRequest.php
    Resources/
      ProductResource.php
      ProductResourceCollection.php
database/migrations/xxxx_xx_xx_create_products_table.php
tests/
  Feature/ProductTest.php
  Unit/ProductServiceTest.php

```

Register the route, define your `$fillable`, register the policy, and you have a working, documented, authorized CRUD API:

```
// routes/api.php
Route::apiResource('products', ProductController::class);
```

---

Architecture overview
---------------------

[](#architecture-overview)

```
HTTP Request
    │
    ▼
Controller              (thin — delegates, authorizes, transforms)
    │  uses ApiResponse::
    │  type-hints Form Requests (Store/Update)
    │  calls $this->authorize() via Policy
    │  returns ApiResource-wrapped data
    ▼
Filters class           (AbstractFilter — request-driven, whitelisted)
    │  applies WHERE / LIKE / ORDER BY to the query
    │  paginates and returns LengthAwarePaginator
    ▼
Service                 (domain logic)
    │  extends BaseService
    │  exposes filter(Request): {Name}Filters
    │  depends on RepositoryInterface
    ▼
Repository              (data access)
    │  extends BaseRepository
    │  wraps an Eloquent Model
    ▼
Model                   (casts status → {Name}Status enum)
    │
    ▼
Database

```

Validation failures are caught by `BaseRequest::failedValidation()` before the controller runs, and a 422 `ApiResponse` envelope is returned automatically.

---

make:module — the module generator
----------------------------------

[](#makemodule--the-module-generator)

```
php artisan make:module {Name} [options]
```

Generates a complete module. All files are valid, idiomatic, and immediately working.

### Component table

[](#component-table)

ComponentGenerated pathStubInterface`app/Interfaces/{Name}RepositoryInterface.php``interface.stub`Repository`app/Repositories/{Name}Repository.php``repository.stub`Model`app/Models/{Name}.php``module-model.stub` / `model.stub`Migration`database/migrations/{ts}_create_{table}_table.php``migration.stub`Status Enum`app/Enums/{Name}Status.php``enum.stub`Filters`app/Filters/{Name}Filters.php``filter.stub`Service`app/Services/{Name}Service.php``module-service.stub` / `service.stub`StoreRequest`app/Http/Requests/{Name}/Store{Name}Request.php``request.stub`UpdateRequest`app/Http/Requests/{Name}/Update{Name}Request.php``request.stub`API Resource`app/Http/Resources/{Name}Resource.php``resource.stub`ResourceCollection`app/Http/Resources/{Name}ResourceCollection.php``resource-collection.stub`Policy`app/Policies/{Name}Policy.php``policy.stub`Controller`app/Http/Controllers/{Name}Controller.php``module-controller.stub`Feature test`tests/Feature/{Name}Test.php``test-feature.stub`Unit test`tests/Unit/{Name}ServiceTest.php``test-unit.stub`The `module-model.stub` (includes enum cast) is used when `enum` is enabled (the default). The `module-service.stub` (includes `filter()` method) is used when `filter` is enabled. The `module-controller.stub` (full: Resources + Policy + Swagger) is used when `resource`, `request`, `filter`, and `policy` are all enabled. Otherwise the simpler `controller.stub` / `controller.plain.stub` are selected automatically.

### --only and --except

[](#--only-and---except)

Generate a **subset** of components using `--only` (whitelist) or `--except` (blacklist). Both accept comma-separated component names from the table above.

```
# Only the data-access layer
php artisan make:module Invoice --only=model,migration,interface,repository,service

# Everything except tests
php artisan make:module Invoice --except=test

# Just the model and migration — quick prototyping
php artisan make:module Order --only=model,migration

# Skip policy and tests — lean module
php artisan make:module Category --except=policy,test
```

`--only` takes priority over `--except`. When both are omitted, all components are generated.

### Per-component --no-\* flags

[](#per-component---no--flags)

FlagSkipsSide effect`--no-model`ModelModel is **never** overwritten even with `--force``--no-migration`Migration`--no-enum`Status EnumUses plain `model.stub` (no cast)`--no-filter`Filters classUses plain `service.stub` (no `filter()` method) and `controller.stub``--no-service`Service`--no-request`Store + Update RequestsUses `controller.plain.stub``--no-resource`API Resource + CollectionFalls back to `controller.stub``--no-policy`PolicyFalls back to `controller.stub``--no-controller`Controller`--no-test`Feature + Unit test stubs### Other options

[](#other-options)

OptionDescription`--model=Foo`Use `Foo` as the Eloquent model name (default: same as module name)`--controller=FooController`Custom controller class name`--provider`Create / update `RepositoryServiceProvider` with a binding for the interface`--force`Overwrite existing files (model is **never** overwritten regardless)---

Filter + Pagination API
-----------------------

[](#filter--pagination-api)

Every generated module ships with a `{Name}Filters` class extending `MuhammedSalama\Base\Filters\AbstractFilter`, and the generated `{Name}Service` exposes a `filter(Request $request)` method pre-wired to the repository's query builder.

### Usage in the controller

[](#usage-in-the-controller)

```
// {Name}Controller::index() — generated automatically
public function index(Request $request): JsonResponse
{
    $this->authorize('viewAny', Product::class);

    return ApiResponse::paginated(
        $this->service->filter($request)->paginate()
    );
}
```

### Declaring filterable fields

[](#declaring-filterable-fields)

Extend `AbstractFilter` and declare your whitelist:

```
// app/Filters/ProductFilters.php
class ProductFilters extends AbstractFilter
{
    // column => SQL operator ('=', 'like', '>', '=', 'service->filter($request)->paginate());
```

---

Generated tests
---------------

[](#generated-tests)

Both test stubs are **skipped** out of the box so your CI is green from the first commit. Each method has inline `TODO` instructions.

```
php artisan test tests/Feature/ProductTest.php      # all skipped ✓
php artisan test tests/Unit/ProductServiceTest.php  # all skipped ✓
```

Enable the feature tests by:

1. Registering the route: `Route::apiResource('products', ProductController::class);`
2. Creating a factory: `php artisan make:factory ProductFactory --model=Product`
3. Removing the `markTestSkipped()` calls

---

Repository auto-binding
-----------------------

[](#repository-auto-binding)

By default (`auto_bind => true` in `config/base.php`), the package scans `app/Interfaces/*RepositoryInterface.php` at boot and binds each to its matching `app/Repositories/*Repository.php` — no manual registration required.

To manage bindings explicitly, use `--provider` when generating:

```
php artisan make:module Product --provider
# Creates app/Providers/RepositoryServiceProvider.php (once)
# and appends the binding for Product.
# Subsequent --provider runs are idempotent — no duplicate entries.
```

Then register the provider once:

```
// bootstrap/providers.php  (Laravel 11+)
App\Providers\RepositoryServiceProvider::class,

// config/app.php  (Laravel 10)
'providers' => [App\Providers\RepositoryServiceProvider::class],
```

Or disable auto-binding entirely and declare bindings in config:

```
// config/base.php
'auto_bind' => false,
'bindings'  => [
    \App\Interfaces\ProductRepositoryInterface::class
        => \App\Repositories\ProductRepository::class,
],
```

---

make:repository — deprecated alias
----------------------------------

[](#makerepository--deprecated-alias)

> **Deprecated since v3.0.0.** `make:repository` will continue to work indefinitely for backward compatibility, but **new projects should use `make:module`**.

`make:repository` is a thin wrapper that calls `make:module` with all new components suppressed (`--no-resource --no-policy --no-test --no-enum --no-filter`), so the generated output is **identical** to what v2.x produced. All existing options (`--model`, `--controller`, `--no-service`, `--no-controller`, `--no-request`, `--no-migration`, `--provider`, `--force`) are forwarded transparently.

A deprecation notice is printed at runtime:

```
⚠  make:repository is deprecated. Please use `php artisan make:module` instead.

```

```
# These all still work exactly as before:
php artisan make:repository Product
php artisan make:repository BlogPost --model=Post --no-migration
php artisan make:repository Order --controller=OrderApiController
```

---

base:create-database
--------------------

[](#basecreate-database)

Creates the configured database if it does not already exist. Supports MySQL and PostgreSQL.

```
php artisan base:create-database [--connection=]
```

OptionDescription`--connection=`Laravel database connection name (defaults to `database.default`)```
php artisan base:create-database              # default connection
php artisan base:create-database --connection=pgsql
```

- If the database already exists, the command exits successfully without changes.
- MySQL: uses `charset` and `collation` from the connection config.
- PostgreSQL: uses `charset` as the `ENCODING`.
- Requires the matching PDO extension and a user with `CREATE DATABASE` privileges.

---

Manual setup
------------

[](#manual-setup)

The generator covers most cases, but here is how to wire a module by hand if needed.

### Interface

[](#interface)

```
namespace App\Interfaces;

use MuhammedSalama\Base\Interfaces\RepositoryInterface;

interface ProductRepositoryInterface extends RepositoryInterface
{
    // Add product-specific query methods here when needed.
}
```

### Repository

[](#repository)

```
namespace App\Repositories;

use App\Interfaces\ProductRepositoryInterface;
use App\Models\Product;
use MuhammedSalama\Base\Repositories\BaseRepository;

class ProductRepository extends BaseRepository implements ProductRepositoryInterface
{
    public function __construct(Product $model)
    {
        parent::__construct($model);
    }
}
```

### Service

[](#service)

```
namespace App\Services;

use App\Filters\ProductFilters;
use App\Interfaces\ProductRepositoryInterface;
use Illuminate\Http\Request;
use MuhammedSalama\Base\Services\BaseService;

class ProductService extends BaseService
{
    public function __construct(ProductRepositoryInterface $repository)
    {
        parent::__construct($repository);
    }

    public function filter(Request $request): ProductFilters
    {
        return new ProductFilters($request, $this->repository->query());
    }
}
```

### Form Requests

[](#form-requests)

```
namespace App\Http\Requests\Product;

use MuhammedSalama\Base\Requests\BaseRequest;

class StoreProductRequest extends BaseRequest
{
    public function rules(): array
    {
        return [
            'name'   => 'required|string|max:255',
            'price'  => 'required|numeric|min:0',
            'status' => ['required', \Illuminate\Validation\Rule::enum(\App\Enums\ProductStatus::class)],
        ];
    }
}
```

A failed validation response:

```
{
    "status": false,
    "message": "Validation error",
    "errors": {
        "name": ["The name field is required."]
    }
}
```

### Controller

[](#controller)

```
namespace App\Http\Controllers;

use App\Http\Requests\Product\StoreProductRequest;
use App\Http\Requests\Product\UpdateProductRequest;
use App\Http\Resources\ProductResource;
use App\Models\Product;
use App\Services\ProductService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use MuhammedSalama\Base\Helpers\ApiResponse;

class ProductController extends Controller
{
    public function __construct(private ProductService $service) {}

    public function index(Request $request): JsonResponse
    {
        $this->authorize('viewAny', Product::class);
        return ApiResponse::paginated($this->service->filter($request)->paginate());
    }

    public function show(int $id): JsonResponse
    {
        $product = $this->service->find($id);
        $this->authorize('view', $product);
        return ApiResponse::success(new ProductResource($product));
    }

    public function store(StoreProductRequest $request): JsonResponse
    {
        $this->authorize('create', Product::class);
        return ApiResponse::created(new ProductResource($this->service->store($request->validated())));
    }

    public function update(UpdateProductRequest $request, int $id): JsonResponse
    {
        $product = $this->service->find($id);
        $this->authorize('update', $product);
        return ApiResponse::success(new ProductResource($this->service->update($id, $request->validated())));
    }

    public function destroy(int $id): JsonResponse
    {
        $product = $this->service->find($id);
        $this->authorize('delete', $product);
        $this->service->destroy($id);
        return ApiResponse::success(null, 'Product deleted successfully.');
    }
}
```

---

API Reference
-------------

[](#api-reference)

### RepositoryInterface

[](#repositoryinterface)

`MuhammedSalama\Base\Interfaces\RepositoryInterface`

MethodReturnsNotes`all(array $columns, array $relations)``Collection`Eager-loads `$relations``paginate(int $perPage, array $columns, array $relations)``LengthAwarePaginator``find(int|string $id, array $columns, array $relations)``?Model``null` when absent`findOrFail(int|string $id, array $columns, array $relations)``Model`Throws `ModelNotFoundException``findBy(string $column, mixed $value, array $columns)``?Model`First match`create(array $data)``Model`Respects `$fillable`/`$guarded``update(int|string $id, array $data)``Model`Finds, updates, returns model`delete(int|string $id)``bool`Throws if absent`query()``Builder`Fresh query builder for complex queries### BaseRepository

[](#baserepository)

`MuhammedSalama\Base\Repositories\BaseRepository` — implements `RepositoryInterface`.

Extend it and inject the Eloquent model via the constructor:

```
class ProductRepository extends BaseRepository implements ProductRepositoryInterface
{
    public function __construct(Product $model)
    {
        parent::__construct($model);
    }
}
```

Additional method not in the interface:

MethodReturns`getModel(): Model`The raw model instance### ServiceInterface

[](#serviceinterface)

`MuhammedSalama\Base\Interfaces\ServiceInterface`

MethodReturnsNotes`all(array $columns, array $relations)``Collection``paginate(int $perPage, array $columns, array $relations)``LengthAwarePaginator``find(int|string $id, array $columns, array $relations)``Model`Always throws on missing`store(array $data)``Model``update(int|string $id, array $data)``Model``destroy(int|string $id)``bool`### BaseService

[](#baseservice)

`MuhammedSalama\Base\Services\BaseService` — implements `ServiceInterface`.

Extend it and inject the repository interface:

```
class ProductService extends BaseService
{
    public function __construct(ProductRepositoryInterface $repository)
    {
        parent::__construct($repository);
    }
}
```

Additional method not in the interface:

MethodReturns`repository(): RepositoryInterface`The underlying repository (for custom queries)### BaseRequest

[](#baserequest)

`MuhammedSalama\Base\Requests\BaseRequest` — extends Laravel's `FormRequest`.

MemberDefaultNotes`authorize()``true`Override to add gate/policy checks`rules()`—Define validation rules (abstract-like)`failedValidation()`—Throws `HttpResponseException` with 422 envelope### ApiResponse

[](#apiresponse)

`MuhammedSalama\Base\Helpers\ApiResponse` — all methods return `JsonResponse`.

```
ApiResponse::success($data, 'Message');           // 200
ApiResponse::created($data);                       // 201
ApiResponse::noContent();                          // 204
ApiResponse::error('Message', 400, $errors);
ApiResponse::validation($errors);                  // 422
ApiResponse::notFound('Message');                  // 404
ApiResponse::unauthorized();                       // 401
ApiResponse::forbidden();                          // 403
ApiResponse::paginated($paginator, 'Message');     // 200 + meta
```

**Standard success envelope:**

```
{ "status": true, "message": "Success", "data": { ... } }
```

**Standard error envelope:**

```
{ "status": false, "message": "Validation error", "errors": { ... } }
```

**Paginated envelope:**

```
{
    "status": true, "message": "Success",
    "data": [ ... ],
    "meta": { "current_page": 1, "last_page": 5, "per_page": 15, "total": 72 }
}
```

### ApiResponseTrait

[](#apiresponsetrait)

`MuhammedSalama\Base\Traits\ApiResponseTrait` — use inside controllers to call response methods as `$this->success(...)` instead of `ApiResponse::success(...)`. Produces the identical JSON envelope.

MethodStatus`success($data, $message, $code)`200`created($data, $message)`201`error($message, $code, $errors)`400`validationError($errors, $message)`422`notFound($message)`404`unauthorized($message)`401`forbidden($message)`403`paginated($paginator, $message)`200### ImageUploadTrait

[](#imageuploadtrait)

`MuhammedSalama\Base\Traits\ImageUploadTrait` — extensions are derived from MIME type, not the client-supplied filename, preventing extension-spoofing attacks.

MethodReturnsDescription`uploadImage($request, $input, $path)``string|null`Store a single image`uploadMultiImage($request, $input, $path)``array`Store multiple images`updateImage($request, $input, $path, $oldPath)``string|null`Replace an image, deletes old file`deleteImage($path)``void`Delete an image```
// Store
$path = $this->uploadImage($request, 'image', 'uploads/products');

// Replace (old file is deleted automatically)
$path = $this->updateImage($request, 'image', 'uploads/products', $product->image);

// Delete
$this->deleteImage($product->image);
```

Always validate uploads in the Form Request first:

```
'image' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
```

---

Configuration
-------------

[](#configuration)

Publish the config:

```
php artisan vendor:publish --tag=base-config
```

This creates `config/base.php`:

```
return [
    // Auto-bind App\Interfaces\{Name}RepositoryInterface → App\Repositories\{Name}Repository
    'auto_bind' => true,

    // Explicit bindings always registered regardless of auto_bind
    'bindings'  => [
        // \App\Interfaces\ProductRepositoryInterface::class
        //     => \App\Repositories\EloquentProductRepository::class,
    ],
];
```

---

Publishing helpers and traits
-----------------------------

[](#publishing-helpers-and-traits)

By default, `ApiResponse` and the traits are used directly from the package namespace — no copying needed. If you want **editable local copies** under `App\`:

```
php artisan vendor:publish --tag=base-helpers  # → app/Helpers/ApiResponse.php
php artisan vendor:publish --tag=base-traits   # → app/Traits/ApiResponseTrait.php + ImageUploadTrait.php
php artisan vendor:publish --tag=base-config   # → config/base.php
```

After publishing, switch `use` statements to the `App\Helpers` / `App\Traits` namespaces. Published files will not receive automatic updates — treat them as your own code.

---

Testing and static analysis
---------------------------

[](#testing-and-static-analysis)

```
composer install
composer test     # PHPUnit + Orchestra Testbench (SQLite in-memory)
composer analyse  # PHPStan level 5 + Larastan
```

The CI matrix covers PHP 8.1–8.4 × Laravel 10/11/12/13 on every push and pull request.

---

Troubleshooting
---------------

[](#troubleshooting)

**`Deprecation Notice: Function curl_close() is deprecated` during Composer.**These come from Composer running on PHP 8.5+. Run `composer self-update` or use PHP 8.4.

**`Your requirements could not be resolved … nette/schema requires php 8.1 - 8.4`.**A transitive dependency predates PHP 8.5. Update it: `composer update nette/schema --with-all-dependencies`.

**Auto-binding does not seem to work.**Verify that `app/Interfaces/` exists and files match `*RepositoryInterface.php`. Both the interface class and the implementation must be autoloadable (`class_exists()` in `tinker`). Confirm `auto_bind => true` in `config/base.php`.

**`Class … not found` after running `make:module`.**Run `composer dump-autoload` so the PSR-4 autoloader discovers the newly created files.

**Policy gates throw `AuthorizationException` on every request.**The generated policy starts with all gates open (`return true`). If you bound the policy but all requests fail, check that your `Auth::user()` is set (routes are authenticated). If running API tests unauthenticated, call `$this->withoutMiddleware()` or mock the Gate facade.

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for a full list of changes by version.

---

Contributing
------------

[](#contributing)

Contributions are welcome. Please:

1. Fork the repository and create a feature branch.
2. Write tests for any change in behaviour.
3. Ensure `composer test` and `composer analyse` both pass locally.
4. Open a pull request against `main` with a clear description of the change.
5. **CI must be green before merging.** The workflow runs `composer test` across PHP 8.1–8.4 and Laravel 10/11/12/13 (with appropriate PHP-version excludes), plus PHPStan static analysis.

---

Security
--------

[](#security)

If you discover a security vulnerability, please use [GitHub private vulnerability reporting](https://github.com/MuhammedMSalama/LaravelBase/security/advisories/new) or contact the maintainer at . Do **not** disclose vulnerabilities publicly until they have been addressed.

---

License
-------

[](#license)

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

---

Author
------

[](#author)

**Muhammed Salama** — [@MuhammedMSalama](https://github.com/MuhammedMSalama)

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance95

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~2 days

Total

12

Last Release

29d ago

Major Versions

v1.4.0 → v2.0.02026-06-01

v2.2.0 → v3.0.02026-06-05

### Community

Maintainers

![](https://www.gravatar.com/avatar/8358373dfddff9beb9cca1158dd8258f4a9da75f93bbbf2f72d0d51ed02f1d98?d=identicon)[MuhammedMSalama](/maintainers/MuhammedMSalama)

---

Top Contributors

[![MuhammedMSalama](https://avatars.githubusercontent.com/u/158785594?v=4)](https://github.com/MuhammedMSalama "MuhammedMSalama (40 commits)")[![Muhammed2024Salama](https://avatars.githubusercontent.com/u/158785594?v=4)](https://github.com/Muhammed2024Salama "Muhammed2024Salama (31 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (10 commits)")

---

Tags

api-responseform-requestlaravellaravel-packagephprepository-patternservice-layerlaravelenumswaggeropenapicode generatorpaginationartisanfilterREST APIrepository patternservice layerform-requestPolicymodule generatorapi resourceapi-response

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/muhammedsalama-laravel-base/health.svg)

```
[![Health](https://phpackages.com/badges/muhammedsalama-laravel-base/health.svg)](https://phpackages.com/packages/muhammedsalama-laravel-base)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45444.2k1](/packages/pressbooks-pressbooks)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1235.9k21](/packages/fleetbase-core-api)

PHPackages © 2026

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