PHPackages                             alvarez/concrete-php - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. alvarez/concrete-php

AbandonedArchivedLibrary[Utility &amp; Helpers](/categories/utility)

alvarez/concrete-php
====================

Generator of DTOs and Services for Laravel using stubs

v1.1.1(4mo ago)119MITPHP

Since Jan 5Pushed 4mo agoCompare

[ Source](https://github.com/DevDanielAlvarez/ConcretePHP)[ Packagist](https://packagist.org/packages/alvarez/concrete-php)[ RSS](/packages/alvarez-concrete-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (4)Versions (7)Used By (0)

 [![ConcretePHP Logo](assets/img/logo.svg)](assets/img/logo.svg)

ConcretePHP — Service Layer &amp; DTO Abstractions
==================================================

[](#concretephp--service-layer--dto-abstractions)

 [ ![Packagist Version](https://camo.githubusercontent.com/af0c87b6c29f6d41ea208241dff9dcee67b3c64eb3d07428e6340277de6d0452/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616c766172657a2f636f6e63726574652d706870) ](https://packagist.org/packages/alvarez/concrete-php) [![PHP Version](https://camo.githubusercontent.com/27a980098454c4cd9d29b85beb083f8d3a837b94e1d6178db26e108e057cfdeb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e302d627269676874677265656e)](https://camo.githubusercontent.com/27a980098454c4cd9d29b85beb083f8d3a837b94e1d6178db26e108e057cfdeb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e302d627269676874677265656e) [![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)

 A lightweight, pragmatic set of abstractions to organize business logic in Laravel: a minimal **Service Layer** (`AbstractModelService`) and a compact **DTO** system (`AbstractDTO`).

---

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

[](#table-of-contents)

1. Overview
2. Features
3. Requirements
4. Installation
5. Quick Start (Examples)
6. Deep Dive — API Reference

    - `AbstractDTO`
    - `AbstractModelService`
    - `IsDTO` contract
7. Behavior &amp; Implementation Notes (the "magic")
8. Extending &amp; Customization
9. Testing (outside Laravel)
10. Best Practices
11. Packagist / Composer Publishing Tips
12. Contributing
13. Changelog
14. License

---

1. Overview
-----------

[](#1-overview)

ConcretePHP focuses on two complementary building blocks:

- **DTOs (Data Transfer Objects)** — `AbstractDTO` gives you a small, serializable object model for carrying validated data between layers.
- **Service Layer** — `AbstractModelService` wraps an Eloquent model instance and centralizes create/find/update operations while accepting DTOs or arrays.

This combination reduces coupling between HTTP layer and persistence, makes unit-testing trivial, and keeps your controllers focused on request/response logic.

---

2. Features
-----------

[](#2-features)

- Accept DTOs or arrays uniformly when creating/updating records.
- Fluent interface for updating / setting records.
- Built-in helpers for DTO serialization (`toArray`, `toJson`, `fromArray`, `fromJson`), filtering (`except`) and immutability-like cloning (`cloneWith`).
- Sensible convention: `UserService` → `App\Models\User` (pluggable by overriding `getModelPath`).
- Lightweight — no repository abstractions, no heavy DI required.

---

3. Requirements
---------------

[](#3-requirements)

- PHP 8.0 or later (union types and `static` return types are used)
- Laravel 8+ (for typical usage), or `illuminate/database` if you want to use Eloquent standalone.

---

4. Installation
---------------

[](#4-installation)

```
composer require alvarez/concrete-php
```

If you plan to run tests or use the package outside Laravel, install Eloquent components:

```
composer require illuminate/database
```

---

5. Quick Start (Examples)
-------------------------

[](#5-quick-start-examples)

### DTO Definition

[](#dto-definition)

```
use Alvarez\ConcretePhp\Data\AbstractDTO;

final class CreateUserDTO extends AbstractDTO
{
    public function __construct(
        public string $name,
        public string $email,
        public string $password,
    ) {}
}
```

### Service Definition

[](#service-definition)

```
use Alvarez\ConcretePhp\Services\AbstractModelService;

class UserService extends AbstractModelService
{
    // Add domain-specific helpers here (e.g., changePassword, activate, archive)
}
```

### Create a User (DTO or array)

[](#create-a-user-dto-or-array)

```
$dto = new CreateUserDTO(name: 'Jane', email: 'jane@example.com', password: bcrypt('secret'));
$userService = UserService::create($dto);

// or
$userService = UserService::create([
    'name' => 'Jane',
    'email' => 'jane@example.com',
    'password' => bcrypt('secret'),
]);
```

### Find, Update, and Access Model

[](#find-update-and-access-model)

```
$service = UserService::find(1); // findOrFail under the hood
$service->update(['name' => 'Jane Updated']);
$user = $service->getRecord(); // Eloquent model
```

---

6. Deep Dive — API Reference
----------------------------

[](#6-deep-dive--api-reference)

### `Alvarez\ConcretePhp\Contracts\IsDTO`

[](#alvarezconcretephpcontractsisdto)

```
interface IsDTO
{
    public function toArray(): array;
    public static function fromArray(array $data): static;
    public function toJson(): string;
    public static function fromJson(string $json): self;
}
```

This contract ensures DTOs can be converted to/from arrays and JSON. Services rely on `toArray()` to persist DTO data.

### `Alvarez\ConcretePhp\Data\AbstractDTO`

[](#alvarezconcretephpdataabstractdto)

Public methods provided:

- `public static function fromArray(array $data): static` — Creates a DTO instance from an array using `new static(...$data)`.
- `public function toArray(): array` — Returns an associative array of the DTO’s public properties using `get_object_vars($this)`.
- `public function except(array $keys): array` — Returns DTO properties, excluding the provided keys.
- `public function cloneWith(array $values): static` — Returns a *new* DTO instance with merged values (original remains unchanged).
- `public static function fromJson(string $json): static` — Create DTO from JSON string.
- `public function toJson(): string` — Convert DTO to JSON string.

**Notes &amp; caveats**

- `fromArray` uses the argument unpacking operator (`...$data`) to pass array values to the DTO constructor. This means the order of values matters — the array must provide values in the same order as the constructor parameters. If you prefer keyed mapping, build DTO instances manually or implement a small factory.
- `toArray()` returns *public* properties only. If you use protected/private fields in a derived DTO, they won't be part of the serialized form.

### `Alvarez\ConcretePhp\Services\AbstractModelService`

[](#alvarezconcretephpservicesabstractmodelservice)

Public methods provided:

- `public static function create(array|IsDTO $data): static` — Create a new model record and return a Service instance that wraps it. Accepts either a DTO (calls `toArray()`), or a plain array.
- `public static function find(string|int $id): static` — Uses `findOrFail` on the resolved model path and returns a Service wrapping the model.
- `public function update(array|IsDTO $data): static` — Update the internal model with data from an array or DTO, and return `$this` for chaining.
- `public function getRecord(): Model` — Get the underlying Eloquent model instance.
- `public function setRecord(Model $record): static` — Replace the stored model instance — returns `$this`.
- `public static function getModelPath(): string` — Resolve the Model FQN by convention. Defaults to `App\Models\{ModelName}` derived from service class name.

**Behavior details**

- When `create()` is called with a DTO, `create($data->toArray())` will be executed on the resolved Eloquent model class. For arrays, `create($data)` is executed directly.
- `find()` uses `findOrFail()` to surface `ModelNotFoundException` when the record is absent (this maps to a 404 in HTTP contexts when using Laravel's exception handler).
- `update()` delegates to Eloquent's `update()` method on the stored model instance.

---

7. Behavior &amp; Implementation Notes (the "magic")
----------------------------------------------------

[](#7-behavior--implementation-notes-the-magic)

These are the small, opinionated choices that make ConcretePHP feel *magical* and at the same time predictable — and why they matter:

- **Constructor-wrapped Model**: `AbstractModelService` stores a single Eloquent model instance. This makes services lightweight stateful wrappers — ideal for per-request domain operations.
- **DTO-first flow**: Services accept DTOs and arrays interchangeably. Passing DTOs clarifies intent and ensures you work with validated, explicit contracts before touching persistence.
- **Convention-based model resolution**: `getModelPath()` extracts the service class basename and removes the `Service` suffix. `UserService` → `App\Models\User`. Override it if your models live elsewhere or use a different naming scheme.
- **Fluent API &amp; `static` return types**: Methods return `static` so child service classes preserve fluent chaining and typing.
- **Immutability-friendly DTOs**: `cloneWith()` returns a new instance — original DTOs remain unchanged, which helps reasoning about state when composing domain operations.
- **Testing-friendly**: The service layer is pure PHP and touches Eloquent only through the Model instance — which means you can inject mocked or in-memory models for unit tests.

8. Extending &amp; Customization
--------------------------------

[](#8-extending--customization)

### Overriding model path

[](#overriding-model-path)

If your models live outside `App\Models`, or you want to point to a mock in tests, just override `getModelPath()`:

```
public static function getModelPath(): string
{
    return Task::class; // or 'App\\MyModels\\Task'
}
```

### Add domain methods

[](#add-domain-methods)

A service should contain domain-specific operations, not just CRUD helpers:

```
class UserService extends AbstractModelService
{
    public function activate(): static
    {
        $this->getRecord()->update(['active' => true]);
        return $this;
    }

    public function changePassword(string $password): static
    {
        $this->getRecord()->update(['password' => bcrypt($password)]);
        return $this;
    }
}
```

### Validation &amp; Form Requests

[](#validation--form-requests)

Keep validation responsibility in Form Requests (or a validation layer) and pass a DTO into the service. This keeps the service focused on business logic and persistence.

---

9. Testing (outside Laravel)
----------------------------

[](#9-testing-outside-laravel)

The repository includes example tests which demonstrate how to bootstrap Eloquent in-memory via `Illuminate\Database\Capsule\Manager` (useful for package tests or library CI):

- Boot Eloquent with an in-memory SQLite DB.
- Create test schema using `Capsule::schema()->create(...)`.
- Define a small mock model (with `$fillable`) and a concrete Service class that overrides `getModelPath()` to return the mock model class.

Example (conceptual):

```
$capsule = new Capsule;
$capsule->addConnection(['driver' => 'sqlite','database' => ':memory:']);
$capsule->setAsGlobal();
$capsule->bootEloquent();

Capsule::schema()->create('tasks', function (Blueprint $t) { $t->id(); $t->string('title'); $t->timestamps(); });

class Task extends Model { protected $fillable = ['title']; }
class TaskService extends AbstractModelService { public static function getModelPath(): string { return Task::class; } }

// Then use TaskService::create([...]) and assertions as in the package tests.
```

This pattern is included in the test-suite so Continuous Integration can verify package behavior without a full Laravel app.

---

10. Best Practices
------------------

[](#10-best-practices)

- Prefer DTOs over raw arrays when passing input to services.
- Keep services focused: orchestrate domain logic and talk to models — do not implement HTTP concerns.
- Use Form Requests (or a Validator) to return validated data before creating DTOs.
- Override `getModelPath()` in tests for deterministic behavior.
- When using `AbstractDTO::fromArray`, ensure the input array values follow the constructor parameter order.

---

11. Packagist / Composer Publishing Tips
----------------------------------------

[](#11-packagist--composer-publishing-tips)

Suggested `composer.json` excerpt for the package root:

```
{
  "name": "alvarez/concrete-php",
  "description": "Lightweight Service Layer and DTO helpers for Laravel and Eloquent",
  "type": "library",
  "license": "MIT",
  "require": {
    "php": "^8.0",
    "illuminate/database": "^9.0|^10.0"
  },
  "autoload": {
    "psr-4": {
      "Alvarez\\ConcretePhp\\": "src/"
    }
  }
}
```

- Tag releases semantically (v1.0.0, v1.1.0).
- Add a `CHANGELOG.md` and keep it updated.

---

12. Contributing
----------------

[](#12-contributing)

Contributions are welcome!

- Open issues for bugs or feature requests.
- Send PRs against `main`.
- Keep changes small and focused, and include tests for new behavior.

Suggested CI checks:

- PHPUnit tests
- Static analysis (Psalm/Phan)
- PHPStan level 7+

---

13. Changelog
-------------

[](#13-changelog)

See `CHANGELOG.md` for details. Keep the first release note brief:

- `v1.0.0` — Initial release: `AbstractDTO`, `AbstractModelService`, contract and basic tests.

---

14. License
-----------

[](#14-license)

MIT — see `LICENSE` for full text.

---

If you want, I can:

- Produce a shorter `README.md` suitable for Packagist’s limited preview.
- Generate a `composer.json` complete file ready for publishing.
- Add ready-to-copy badges and GitHub templates (issue template, PR template).
- Create `CHANGELOG.md` or `CONTRIBUTING.md` files.

Tell me which of the above you want next and I’ll add them directly to the repository content.

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance76

Regular maintenance activity

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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 ~0 days

Total

3

Last Release

133d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/649f3f8d8178deb1077e017f913bf348113e72851e6e4b786bbc8ab1229132fe?d=identicon)[skyrimdaniel](/maintainers/skyrimdaniel)

---

Top Contributors

[![DevDanielAlvarez](https://avatars.githubusercontent.com/u/91484468?v=4)](https://github.com/DevDanielAlvarez "DevDanielAlvarez (39 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/alvarez-concrete-php/health.svg)

```
[![Health](https://phpackages.com/badges/alvarez-concrete-php/health.svg)](https://phpackages.com/packages/alvarez-concrete-php)
```

###  Alternatives

[barryvdh/laravel-ide-helper

Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.

14.9k123.0M687](/packages/barryvdh-laravel-ide-helper)[orchestra/canvas

Code Generators for Laravel Applications and Packages

20917.2M158](/packages/orchestra-canvas)[fumeapp/modeltyper

Generate TypeScript interfaces from Laravel Models

196277.9k](/packages/fumeapp-modeltyper)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[flarum/core

Delightfully simple forum software.

211.3M1.9k](/packages/flarum-core)[zonneplan/laravel-module-loader

Module loader for Laravel

24118.4k](/packages/zonneplan-laravel-module-loader)

PHPackages © 2026

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