PHPackages                             innmind/doctrine - 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. [Database &amp; ORM](/categories/database)
4. /
5. innmind/doctrine

Abandoned → [formal/orm](/?search=formal%2Form)ArchivedLibrary[Database &amp; ORM](/categories/database)

innmind/doctrine
================

Declarative approach to eliminate state where not necessary

3.0.0(1y ago)45.1k2MITPHPPHP ~8.2

Since Jul 13Pushed 1y ago1 watchersCompare

[ Source](https://github.com/Innmind/Doctrine)[ Packagist](https://packagist.org/packages/innmind/doctrine)[ Docs](http://github.com/innmind/doctrine)[ RSS](/packages/innmind-doctrine/feed)WikiDiscussions develop Synced 5d ago

READMEChangelog (10)Dependencies (11)Versions (21)Used By (0)

doctrine
========

[](#doctrine)

[![Build Status](https://github.com/innmind/doctrine/workflows/CI/badge.svg?branch=master)](https://github.com/innmind/doctrine/actions?query=workflow%3ACI)[![codecov](https://camo.githubusercontent.com/6014cba2ad36bba32ae66f69494064c73da99658c1d94db4a8b842f7efed89ee/68747470733a2f2f636f6465636f762e696f2f67682f696e6e6d696e642f646f637472696e652f6272616e63682f646576656c6f702f67726170682f62616467652e737667)](https://codecov.io/gh/innmind/doctrine)[![Type Coverage](https://camo.githubusercontent.com/1fff8f1558315098edbd09808ed471916bfadfc5aaced604645dd4d3ff0bfb02/68747470733a2f2f73686570686572642e6465762f6769746875622f696e6e6d696e642f646f637472696e652f636f7665726167652e737667)](https://shepherd.dev/github/innmind/doctrine)

Important

This project has been a stepping stone for the [Formal ORM](https://formal-php.github.io/orm/). It's no longer actively maintained and will be archived at some point.

This library is an abstraction on top of [Doctrine](https://packagist.org/packages/doctrine/orm) with the intention to remove all implicit states.

Managing the state in an application can become hard when the codebase grows and states (especially implicit ones) is one of the source of bugs in applications.

Doctrine is usually the default choice (not an actual fact) when interacting with databases as it comes bundled with [Symfony](https://symfony.com). Its interface cover many use cases but expose many implicit states such as transactions or cursors in collections.

This is solved here by using principles from the functional programming paradigm.

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

[](#installation)

```
composer require innmind/doctrine
```

Design choices
--------------

[](#design-choices)

 Click to expand### `Sequence` vs `Set`

[](#sequence-vs-set)

`Set` has been discarded for this library as the unicity of entities cannot be guaranted from the returned collections. It also would prevent the use of the `map` function as many entities may be mapped to a single new value, this may lead to unexpected behaviour for newcomers to such paradigm. This is mainly why the choice has been toward `Sequence`.

In case you really want to use sets, you may use [`innmind/immutable`](https://github.com/innmind/immutable/#set).

### Enforcing the use of an `Id`

[](#enforcing-the-use-of-an-id)

Doctrine allows you to generate an id for you when your entities are persisted. This is an implicit state change.

In order to avoid this implicit you need to specify the id before persisting your entities. This prevents you from relying on the auto generated id from your database as you can't avoid collisions.

The unique solution (that I'm aware of) is to use `UUID`s. The `Id` provided by this library use them so you don't have to think of it anymore.

### A single `Id` class for all entities

[](#a-single-id-class-for-all-entities)

This is no longer a problem as it is provided with a template understood by [`vimeo/psalm`](https://github.com/vimeo/psalm/blob/master/docs/annotating_code/templated_annotations.md).

### No `flush` method on the `Manager`

[](#no-flush-method-on-the-manager)

Being free to call the `persist` and `flush` methods when you wish it opens the door to implicit states in your codebase. You may end up either flushing unwanted persisted entities (`persist` calls before an error occured) or forgetting to `flush` persisted entities (resulting in lost state change).

Here this is avoided by forcing to execute all mutations in a given context (via `Manager::mutate()` and `Manager::transaction()`). So it's always all or nothing.

Usage
-----

[](#usage)

All the use cases below use the code declared in the [`example` folder](example/).

Pre-requisite for all use cases:

```
use Innmind\Doctrine\{
    Manager,
    Sort,
};
use Example\Innmind\Doctrine\User;

$manager = Manager::of($entityManager);
```

### Fetching all entities from the database

[](#fetching-all-entities-from-the-database)

```
$manager
    ->repository(User::class)
    ->all()
    ->sort('username', Sort::asc)
    ->fetch()
    ->foreach(function(User $user): void {
        echo $user->username()."\n";
    });
```

**Note**: The queries are delayed to the last moment possible to leverage the database as most as possible.

### Pagination

[](#pagination)

```
$numberOfElementPerPage = 10;
$manager
    ->repository(User::class)
    ->all()
    ->sort('username', Sort::asc)
    ->drop($page * $numberOfElementPerPage)
    ->take($numberOfElementPerPage)
    ->fetch()
    ->foreach(function(User $user): void {
        echo $user->username()."\n";
    });
```

### Filtering

[](#filtering)

It uses the [`Specification` pattern](https://en.wikipedia.org/wiki/Specification_pattern) (normalized in the library [`innmind/specification`](https://github.com/innmind/specification)).

```
use Example\Innmind\Doctrine\Username;

$manager
    ->repository(User::class)
    ->matching(
        Username::of('alice')->or(
            Username::of('jane'),
        ),
    )
    ->sort('username', Sort::asc)
    ->drop(20)
    ->take(10)
    ->fetch()
    ->foreach(function(User $user): void {
        echo $user->username()."\n";
    });
```

This example is the equivalent of `SELECT * FROM user WHERE username = 'alice' OR username = 'jane' ORDER BY username OFFSET 20 LIMIT 10`.

**Note**: This chain of method calls result once again in a single database call.

### Adding new entities

[](#adding-new-entities)

```
use Innmind\Doctrine\Id;
use Innmind\Immutable\Either;

$user = $manager->mutate(function($manager): Either {
    $user = new User(
        Id::new(),
        'someone',
    );
    $manager
        ->repository(User::class)
        ->add($user);

    return Either::right($user);
});
```

If you try to call `Repository::add()` or `Repository::remove()` outside the function it will raise an exception.

**Note**: If the function throws an exception or an `Either::left` is returned then nothing will be flushed to the database.

### Transactions

[](#transactions)

```
$manager->transaction(function($manager, $flush): Either {
    $progress = 0;
    $repository = $manager->repository(User::class);

    foreach ($someSource as $args) {
        $repository->add(new User(...$args));
        ++$progress;

        if ($progress % 20 === 0) {
            // flush entities to the database every 20 additions
            $flush();
        }
    }

    return Either::right(null);
});
```

**Note**: Call the `$flush` function only when in a context of imports as it will detach all the entities from entity manager, meaning if you kept references to entities they will no longer be understood by doctrine

### Accessing the values inside a sequence

[](#accessing-the-values-inside-a-sequence)

Sometimes you may want to manipulate an array so it can be used with php functions such as `json_encode`.

```
use Symfony\Component\HttpFoundation\JsonResponse;

/** @var list */
$data = $manager
    ->repository(User::class)
    ->all()
    ->sort('registerIndex', Sort::asc)
    ->fetch()
    ->map(static fn(User $user): array => [
        'username' => $user->username(),
        'registerIndex' => $user->registerIndex(),
    ])
    ->toList();

new JsonResponse($data);
```

### Filtering on relations

[](#filtering-on-relations)

You can specify the property of an entity relationship in a specification property field.

```
use Example\Innmind\Doctrine\Child;

$users = $manager
    ->repository(User::class)
    ->matching(
        Child::of('alice')->or(
            Child::of('jane'),
        ),
    );
```

The `Child` specification use the property `children.username` thus specifying the username of a user's children.

**Note**: for now only one level relationship is allowed in a specification property.

### Lazy loading collections

[](#lazy-loading-collections)

In certain cases the amount of entities you will fetch won't fit in memory. To still work with this king of scenario you need to use a lazy `Sequence` and perform periodic clears.

```
$_ = $manager
    ->repository(User::class)
    ->all()
    ->lazy() // instruct to load one entity at a time
    ->fetch()
    ->foreach(function($user) use ($manager) {
        doStuff($user);
        // this clear is important to make doctrine forget about the loaded
        // entities and will consequently free memory
        $manager->clear();
    });
```

**Note**: this feature won't work however if you use bi-directional relationships in your entities, for some reason doctrine won't free memory.

**Note 2**: you should only use this feature when reading data. Using this in a write context may have unexpected side effects!

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance33

Infrequent updates — may be unmaintained

Popularity23

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity76

Established project with proven stability

 Bus Factor1

Top contributor holds 99.3% 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 ~80 days

Total

19

Last Release

679d ago

Major Versions

1.6.0 → 2.0.02022-04-16

2.5.1 → 3.0.02024-07-07

PHP version history (4 changes)1.0.0PHP ~7.4

1.1.0PHP ~7.4|~8.0

2.0.0PHP ~8.1

2.5.0PHP ~8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/851425?v=4)[Baptiste Langlade](/maintainers/Baptouuuu)[@Baptouuuu](https://github.com/Baptouuuu)

---

Top Contributors

[![Baptouuuu](https://avatars.githubusercontent.com/u/851425?v=4)](https://github.com/Baptouuuu "Baptouuuu (150 commits)")[![drupol](https://avatars.githubusercontent.com/u/252042?v=4)](https://github.com/drupol "drupol (1 commits)")

---

Tags

doctrinedeclarativefunctional-programming

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/innmind-doctrine/health.svg)

```
[![Health](https://phpackages.com/badges/innmind-doctrine/health.svg)](https://phpackages.com/packages/innmind-doctrine)
```

###  Alternatives

[knplabs/doctrine-behaviors

Doctrine Behavior Traits

92212.7M64](/packages/knplabs-doctrine-behaviors)[scienta/doctrine-json-functions

A set of extensions to Doctrine that add support for json query functions.

58523.9M36](/packages/scienta-doctrine-json-functions)[laravel-doctrine/orm

An integration library for Laravel and Doctrine ORM

8425.3M87](/packages/laravel-doctrine-orm)[damienharper/auditor-bundle

Integrate auditor library in your Symfony projects.

4542.8M](/packages/damienharper-auditor-bundle)[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

1022.4k](/packages/rcsofttech-audit-trail-bundle)[ahmed-bhs/doctrine-doctor

Runtime analysis tool for Doctrine ORM integrated into Symfony Web Profiler. Unlike static linters, it analyzes actual query execution at runtime to detect performance bottlenecks, security vulnerabilities, and best practice violations during development with real execution context and data.

813.1k](/packages/ahmed-bhs-doctrine-doctor)

PHPackages © 2026

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