PHPackages                             marble/entity-manager - 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. marble/entity-manager

ActiveLibrary[Database &amp; ORM](/categories/database)

marble/entity-manager
=====================

An ORM-less entity lifecycle manager for PHP.

0.14.3(1mo ago)4104MITPHPPHP ^8.2CI passing

Since Dec 27Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/marble-php/entity-manager)[ Packagist](https://packagist.org/packages/marble/entity-manager)[ Docs](https://github.com/marble-php/entity-manager)[ RSS](/packages/marble-entity-manager/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (18)Versions (30)Used By (0)

Marble Entity Manager
=====================

[](#marble-entity-manager)

[![Minimum PHP version](https://camo.githubusercontent.com/5031c494bed173436e4f949f64470b4b93c0cbad21a1ac95f87a624977dce5c8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d76382e322b2d383839324246)](https://camo.githubusercontent.com/5031c494bed173436e4f949f64470b4b93c0cbad21a1ac95f87a624977dce5c8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d76382e322b2d383839324246)[![Tests](https://camo.githubusercontent.com/d940ad7f0752e2cbe0d63c50dcebf329078807390051c41fe63258f1b5c4e182/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d70617373696e672d627269676874677265656e)](https://camo.githubusercontent.com/d940ad7f0752e2cbe0d63c50dcebf329078807390051c41fe63258f1b5c4e182/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d70617373696e672d627269676874677265656e)[![Installs via Packagist](https://camo.githubusercontent.com/9d8a9ea524d2f9c97292077a6be4306688ffeffb15265901ab9c4ec1dc1a0ce3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6172626c652f656e746974792d6d616e61676572)](https://camo.githubusercontent.com/9d8a9ea524d2f9c97292077a6be4306688ffeffb15265901ab9c4ec1dc1a0ce3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6172626c652f656e746974792d6d616e61676572)[![Latest stable version](https://camo.githubusercontent.com/992d7489d5ecf1e218efaab47cc69d76c3997fcac88d530fdc32b97ffecb38b1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6172626c652f656e746974792d6d616e61676572)](https://camo.githubusercontent.com/992d7489d5ecf1e218efaab47cc69d76c3997fcac88d530fdc32b97ffecb38b1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6172626c652f656e746974792d6d616e61676572)[![License](https://camo.githubusercontent.com/41d0117fc42e327baf5a12e45ed0f4f386c96caa964cabec741d8e4a34ec55a2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6d6172626c652f656e746974792d6d616e61676572)](https://camo.githubusercontent.com/41d0117fc42e327baf5a12e45ed0f4f386c96caa964cabec741d8e4a34ec55a2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6d6172626c652f656e746974792d6d616e61676572)

This library provides some of the great features of ORM frameworks — entity manager, unit of work, identity map, repository factory, query caching — without the object-relational mapping itself. It’s up to you to implement the actual reading and writing of entity data from and to whatever persistence layer you’re using.

- **Unit of work:** Bundles all your database operations into a single transaction-like flush, ensuring data consistency.
- **Identity map:** Ensures that each unique entity is instantiated only once per session, preventing conflicting object states.
- **Automatic change tracking:** Automatically detects modifications to your entities, so you only save what has actually changed.
- **Ordered flushes:** Intelligently calculates the correct order to persist entities based on their associations, handling dependencies for you.
- **Persistence-agnostic:** Since you implement the readers and writers, you can use any storage engine — SQL, NoSQL, or even external APIs — while keeping your domain logic clean.

Why Marble?
-----------

[](#why-marble)

I love Doctrine ORM as much as the next guy, but often enough the object-relational mapping part of it is just overly constrained (1 property = 1 column) and overly constraining (Doctrine collections all over your domain code). Its configuration, especially if you want to get around those constraints, can get very (very) elaborate.

Moreover, ORM libraries are limited to relational (i.e. SQL-based) storage engines. Add NoSQL or external APIs into the mix, and you'll need to bring in additional entity-managing libraries to bridge the gap.

Marble gives you full control over how your data is saved and loaded, without sacrificing the benefits of a robust unit of work and identity map.

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

[](#installation)

Use Composer to install: `composer require marble/entity-manager`

This library requires PHP 8.2 or higher.

> **Note:** if you’re building a Symfony application, consider installing the [bundle](https://github.com/marble-php/entity-manager-bundle) instead. It comes with a default implementation of `EntityIoProvider` and provides several features to make your life easier.

Getting started
---------------

[](#getting-started)

1. All your entity classes should implement the `Entity` interface. For identifiers you may use the provided `SimpleId` or Uid classes, or any other implementation of the `Identifier` interface.
2. Create a class that implements `EntityReader` for every entity class that you want to fetch from your application code or from other readers. See [Fetching](#fetching) for more details.
3. Create one or more classes that implement `EntityWriter`. Typically you’ll have either a single writer for all entities, or a writer per entity class except those that are always written and deleted by other writers. See [Persisting](#persisting) and [Removing](#removing) for more details.
4. Implement the `EntityIoProvider` interface and have it return the correct `EntityReader` and `EntityWriter` for a given `Entity`. Of course one class may implement both interfaces.

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

[](#how-it-works)

### Persisting

[](#persisting)

- To persist a newly created entity, `add` it to its repository or pass it to `EntityManager::persist`. Preexisting, fetched entities don’t need to be re-registered; any changes to them will be automatically detected. To actually write data changes to your persistence layer, call `EntityManager::flush`; until then all changes are in memory only. Note that an entity’s writer will only be called if the entity indeed has changes or must be removed.
- Multiple entities in the same entity class hierarchy must never have the same identifier. It’s okay if new entities don’t have an identifier yet, but once an entity is persisted it must have an identifier.
- A flush order is calculated by sorting known entities such that a given entity’s associations are all ranked higher than the entity itself. As such, when persisting an entity in `EntityWriter::write`, its associated entities will have been passed to their writers already, and will have an id. This algorithm does not allow circular entity associations.
- You may use a writer to persist not just its own entity but particular associated entities ("child entities") as well, e.g. an aggregate root’s writer also persisting other entities in the aggregate. Make sure to call `markPersisted` on the passed `WriteContext` to let the unit of work know that these were indeed persisted. `EntityIoProvider::getWriter` can return `null` for entity classes that are always persisted by other writers.
- Your entity writer should throw an `EntitySkippedException` when you need to leave the writing or removing of the entity to a parent entity’s writer later in the flush order. The library will check that all necessary writes and removals are done by the end of the flush.
- The first argument to `EntityWriter::write` is an instance of `HasChanged` if the entity is not new.

### Fetching

[](#fetching)

- Fetch entities by getting their repository from the entity manager and passing any kind of object to one of the `fetch*` methods. The object represents the query to be executed. It will be passed to the entity reader, where you should handle it appropriately. A special case is objects implementing the `Identifier` interface; these are only allowed on the `fetchOne` method.
- Your query classes may be as simple or complex as you find helpful, and may carry any information needed for the corresponding entity reader to do correct data retrieval (e.g. construct SQL statements), including sorting and pagination. A `Criteria` class is provided by this library, to represent a set of values to filter on.
- Entity readers should not instantiate entities themselves; instead, they pass identifier/data combinations into the `DataCollector`. After the entity reader is done with data retrieval, the repository will make sure entities are instantiated, registered in the unit of work, added to the identity map and returned.
- When fetching an entity, use the passed `ReadContext` to access other repositories and fetch associated entities through them. Any associated sub-entity is replaced with its equivalent in the identity map, if it exists there already. So even with nested associations, only one instance of a particular entity will exist at any time.
- You may use a custom repository for a particular entity class by having `EntityIoProvider::getCustomRepository` return either an instantiated custom repository, or its class name. It must extend `CustomRepository`, and it should use the `fetchOne` and `fetchMany` methods of its parent. The `EntityReader` will still handle all actual read operations (e.g. database interaction); custom repositories allow you to hide query construction details from domain code. Custom repositories may also simplify dependency injection, e.g. injecting into a service only the specific repositories it requires, instead of the entity manager.

> **Note:** One known limitation of this library compared to some other ORM libraries, is that all `fetch*` calls to the repository are forwarded to your entity reader. This means queries only operate directly on the persistence layer, and will ignore in-memory, pre-flush changes and additions in the unit of work. There is currently no extension point in the library to circumvent this limitation.
>
> However, if you’re following Domain-Driven Design (DDD) practices, particularly the "one aggregate per transaction" rule, in most cases you shouldn’t be querying the repository *after* mutating an aggregate but *before* flushing. Nonetheless, be careful when doing batch processing or handling synchronous, pre-flush events.

### Removing

[](#removing)

- To remove an entity, `remove` it from its repository or pass it to `EntityManager::remove`. Again, only on flush are deletions actually executed through `EntityWriter::delete`.
- You may use a writer to delete not just its own entity but associated sub-entities as well, whether explicitly or via database-level cascade rules. Make sure to call `markRemoved` on the passed `DeleteContext`to let the unit of work know that these were indeed removed.
- Removed entities are removed from the identity map. Fetching the same entity again will return a new instance.

License
-------

[](#license)

Marble is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

About
-----

[](#about)

Made by [a Dutch guy](https://github.com/mjpvandenberg) in his spare time, with some frustrations towards (as well as love for) Doctrine ORM.

###  Health Score

48

—

FairBetter than 95% of packages

Maintenance90

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity63

Established project with proven stability

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

Recently: every ~8 days

Total

29

Last Release

50d ago

PHP version history (2 changes)0.1.0PHP ^8.1

0.10.0PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/8d5821a6441eaf1a0fba671c51a66198a5f54f4f929b7f8922e1a105b0af9b8f?d=identicon)[mjpvandenberg](/maintainers/mjpvandenberg)

---

Top Contributors

[![mjpvandenberg](https://avatars.githubusercontent.com/u/2412861?v=4)](https://github.com/mjpvandenberg "mjpvandenberg (41 commits)")

---

Tags

ormentityrepositoryentity-managerunit of work

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/marble-entity-manager/health.svg)

```
[![Health](https://phpackages.com/badges/marble-entity-manager/health.svg)](https://phpackages.com/packages/marble-entity-manager)
```

###  Alternatives

[analogue/orm

An intuitive Data Mapper ORM for PHP and Laravel

63547.1k3](/packages/analogue-orm)[vlucas/spot2

Simple DataMapper built on top of Doctrine DBAL

605392.8k7](/packages/vlucas-spot2)[ergebnis/factory-bot

Provides a fixture factory for doctrine/orm entities.

81702.8k](/packages/ergebnis-factory-bot)[kassko/data-mapper

A mapper which gives a lot of features to representate some raw data like objects

1338.5k1](/packages/kassko-data-mapper)[tommyknocker/pdo-database-class

Framework-agnostic PHP database library with unified API for MySQL, MariaDB, PostgreSQL, SQLite, MSSQL, and Oracle. Query Builder, caching, sharding, window functions, CTEs, JSON, migrations, ActiveRecord, CLI tools, AI-powered analysis. Zero external dependencies.

845.7k](/packages/tommyknocker-pdo-database-class)[liqueurdetoile/cakephp-orm-json

Cakephp plugin to provide easy control over JSON type fields in database

1461.1k](/packages/liqueurdetoile-cakephp-orm-json)

PHPackages © 2026

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