PHPackages                             cyberma/laravel-layer-frame - 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. [Framework](/categories/framework)
4. /
5. cyberma/laravel-layer-frame

ActiveProject[Framework](/categories/framework)

cyberma/laravel-layer-frame
===========================

Layer Frame for Laravel

1.14.1(1mo ago)2490MITPHPPHP &gt;=8.5

Since Nov 27Pushed 1mo agoCompare

[ Source](https://github.com/cyberma-net/laravel-layer-frame)[ Packagist](https://packagist.org/packages/cyberma/laravel-layer-frame)[ RSS](/packages/cyberma-laravel-layer-frame/feed)WikiDiscussions master Synced today

READMEChangelog (10)Dependencies (6)Versions (50)Used By (0)

Layer Frame for Laravel
=======================

[](#layer-frame-for-laravel)

### A scalable, layered architecture for large Laravel projects

[](#a-scalable-layered-architecture-for-large-laravel-projects)

Motivation
----------

[](#motivation)

Laravel's native Active Record (Eloquent) is fantastic for small and medium-sized projects — but it becomes problematic at scale:

- Models grow huge and violate Single Responsibility Principle (SRP)
- Business logic ends up scattered across Controllers, Models, Traits, and global helpers
- Tight coupling between database schema and business logic makes refactors dangerous
- Testing large models becomes painful
- Database structure leaks into the rest of the codebase

Layer Frame solves these issues by introducing a strict, layered, dependency-flow and complete separation between:

- your business logic
- your internal domain models
- your database schema
- your input / API formats

This comes at the cost of more classes, but brings massive benefits:

- refactoring safety
- better testability
- cleaner responsibilities
- fully DB-agnostic data layer
- predictable code structure
- safer long-term evolution of the project

Core Principles
---------------

[](#core-principles)

Layer Frame is built around a few objective architectural rules:

1. **Layered structure (no jumping across layers)**Each layer communicates only with the one directly below or above it. No Controller → DB, no Service → DBStorage, no random SQL inside Services.
2. **Composition over inheritance**Small composable objects instead of monolithic Models or deep inheritance trees.
3. **Configuration over copy-pasted code**Almost everything is controlled via simple configuration arrays:

    - ATTRIBUTES\_MAP
    - PRIMARY\_KEY
    - JSON\_COLUMNS
    - COLUMN\_ALIAS\_MAP
    - MANDATORY\_ATTRIBUTES
4. **90% typical flows must be easy**Simple CRUD is nearly automatic.
5. **The remaining 10% must be fully customizable**Every layer has extension points for special logic.
6. **Strict SRP + SOLID**No class should do more than one job.
7. **Infrastructure-agnostic**DB storage can be swapped, mappings changed, and API formats redesigned without breaking the rest of the code.

Layer Overview
--------------

[](#layer-overview)

### Controllers

[](#controllers)

- Validate incoming data
- Map inputs to InputModels
- Call a Service method
- Map results to API shape (using ApiMapper)
- Return JSON

Controllers do zero business logic.

Helpers included:

- Paginator
- Searcher

For consistent pagination &amp; search interfaces.

### Input Models

[](#input-models)

Represent incoming request structure.

Inside each InputModel:

- Validation rules
- Custom messages (optional)
- doExtraValidations() hook

InputModel is the only place that "knows" the shape of incoming data.

### Input Parsers

[](#input-parsers)

InputParser performs:

- Validation (Laravel Validator)
- Extra validations (custom)
- Filling the model attributes safely

InputParser is generic; you rarely need a custom implementation.

### Services

[](#services)

Services contain all business logic.

A Service:

- receives clean InputModels or Models
- orchestrates Repositories
- applies domain rules
- returns domain Models

Services never talk to the database directly.

### Repositories

[](#repositories)

Repositories form the boundary between business logic and persistence.

A Repository uses:

- ModelMap (attribute → column map)
- DBMapper (model attributes ↔ DB row mapping)
- DBStorage (actual SQL operations)
- ModelFactory (model instantiation)

Purpose:

- Convert IModel → array for insert/update (via DBMapper)
- Convert DB rows → Models (via DBMapper)
- Execute standard DB operations via DBStorage

Unlike Eloquent:

- No "magic queries"
- No Active Record domain pollution
- Fully independent of SQL schema

Repositories support:

- get / first / count
- search
- store
- delete
- patch
- composite keys

All without exposing SQL to the Service.

### Generic Query Execution Architecture

[](#generic-query-execution-architecture)

Layer Frame uses typed query DTOs to extend query capabilities without turning repositories into a method-per-query API.

Current separation:

- `EntityQuery` (conceptual): model retrieval + hydration flows
- `ScalarQuery`: scalar/aggregate values (`count`, `sum`, `avg`, `min`, `max`, `value`, `exists`)
- `CollectionQuery`: `pluck`-style retrieval without hydration
- `MutationQuery`: write operations (`update`, `delete`, `increment`, `decrement`)
- `StreamQuery`: low-memory iteration (`chunk`, `chunkById`, `lazy`, `cursor`)

#### Rationale

[](#rationale)

Without generic query DTOs, repository/storage APIs quickly grow into method explosion:

- `countByX()`, `existsByX()`, `sumByX()`, `avgByX()`, ...
- duplicated across contracts, storage, repositories, and tests
- harder to evolve safely

Typed query DTOs keep the architecture explicit, maintainable, and AI-friendly:

- predictable signatures
- minimal boilerplate
- easier code/test generation
- safer long-term extension

#### Architectural rule: keep repositories semantic

[](#architectural-rule-keep-repositories-semantic)

Query DTOs are infrastructure primitives. Application/domain repositories should remain semantic services built with composition.

Layer Frame repository architecture guidance:

- prefer composition over inheritance
- inject `IRepository` (or a concrete LF repository service) into semantic repositories
- do not build ORM-style repository class trees

Good (composition):

```
use Cyberma\LayerFrame\Contracts\Repositories\IRepository;
use Cyberma\LayerFrame\DBStorage\Aggregates\ScalarQuery;

final class UserRepository
{
    public function __construct(
        private readonly IRepository $repository
    ) {
    }

    public function getActiveUserCount(): int
    {
        return (int)$this->repository->scalar(
            ScalarQuery::count(),
            [['status', '=', 'active']]
        );
    }
}
```

Avoid exposing raw SQL-style repository APIs in services/controllers. Use semantic repository methods for common business use-cases, and generic query DTO execution inside repository services.

#### ScalarQuery examples

[](#scalarquery-examples)

```
// inside a semantic repository service:
$count = $this->repository->scalar(ScalarQuery::count(), [['status', '=', 'active']]);

// count(distinct userId)
$uniqueUsers = $this->repository->scalar(
    ScalarQuery::count('userId', distinct: true),
    [['status', '=', 'completed']]
);

// value(email)
$email = $this->repository->scalar(ScalarQuery::value('email'), [['id', '=', 10]]);
```

#### CollectionQuery examples

[](#collectionquery-examples)

```
// flat array
$ids = $this->repository->collection(
    CollectionQuery::ids(),
    [['status', '=', 'active']]
);

// key/value map: [id => name]
$userMap = $this->repository->collection(
    CollectionQuery::pluck('name', keyColumn: 'id'),
    [['status', '=', 'active']]
);

// distinct values
$statuses = $this->repository->collection(CollectionQuery::distinctColumn('status'));
```

#### MutationQuery examples

[](#mutationquery-examples)

```
// update
$affected = $this->repository->mutate(
    MutationQuery::update(['status' => 'inactive']),
    [['lastLoginAt', '
