Search by

Database- and framework agnostic ORM.

Maintainers

Package info

github.com/slendium/ocd

pkg:composer/slendium/ocd

Transparency log

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

v0.1.0 2026-08-31 14:35 UTC

This package is auto-updated.

Last update: 2026-09-03 16:14:06 UTC


README

Database- and framework agnostic database abstraction layer / ORM for PHP.

  • Supports MongoDB and SQL-based databases, can be extended further
  • Supports custom / derived types as long as they go back to the same primitives
  • Entities are based on plain PHP classes, adding #[Attribute]'s only where more specificity is needed
  • Does not force the active record pattern, inheritance, or a rich domain model (nor exclude them)
  • Developed without LLM's

Installation

Requires PHP >= 8.5. Run composer require slendium/ocd to add it to your project. However, most likely you are looking for a database-specific implementation of the library:

For implementors there is also the OCD conformance package, which contains implementation guides and PHPUnit tests.

Examples

Data definition

Basic entity

A basic entity only requires a plain PHP class declaration.

final readonly class Product implements Entity, Entity\Identifiable {

	public function __construct(

		#[Override]
		public Entity\Id $id,

		public string $name,

		public float $price,

		public bool $visible = false,

		public int $stock = 0,

	) { }

}

Relationships and indices

TODO

Querying

Data can be queried using a set of generalized expressions. You can manually construct them, use a predefined builder utility or create your own builder. An example using the built-in "document shape" query builder:

use Slendium\Ocd\Predicate\DocumentShape as Q;

$filter = Q::shape([
	'year' => 2026, // match a literal int
	'views' => Q::gte(1000),
	'title' => Q::regex('^'),
	'tags' => Q::containsSome([ 'news', 'updates' ])
]);

$cursor = $collection->openCursor()
	|> Cursor::filter(?, $filter)
	|> Cursor::skip(?, $page * PAGE_SIZE)
	|> Cursor::limit(?, PAGE_SIZE);

foreach ($cursor as $doc) { } // do something with each document

Manipulating data

Inserting data

Data can be inserted by passing "documents" to an insert command. A document is any object that is ArrayAccess&Countable&Traversable with non-empty strings for keys. See the documentation of the Collection::startInsert() function to learn more about what types of values can be contained in inserted documents. An example:

$document = new MutableDocument([
	'name' => 'foo',
	'createdAt' => new DateTime,
	'location' => [ $latitude, $longitude ],
	'owner' => $userEntity,
]);

$collection->startInsert([ $document ])
	|> InsertCommand::fireAndForget(?);

Updating data

Data can be updated by providing a query and a list of update statements. Analogous to the data querying example, update statement lists can be constructed manually or through builder utilities. An example:

use Slendium\Ocd\Predicate\DocumentShape as Q;
use Slendium\Ocd\Update\DocumentUpdate as U;

$filter = Q::shape([ 'id' => $updateId ]);

$updates = U::create([
	'name' => $newName,
	'modificationCount' => U::add(1),
	'auditLog' => U::append("At $modifiedAt, user {$user->name} changed name to `$newName`"),
	'details' => U::path([ 'neverModifiedFlag', U::unset() ])
]);

$collection->startUpdate($filter, $updates)
	->execute();

Deleting data

Data can be deleted by creating a delete command and executing it. An example:

$filter = Q::shape([ 'id' => $id ]);

$collection->startDelete($filter)
	|> DeleteCommand::enforceLimit(?, 1)
	|> DeleteCommand::execute(?);

Roadmap

  1. [Done] Describing the schema
  2. [Done] Data manipulation (create, update, delete)
  3. Data querying
    1. [Done] Reading from a cursor
    2. Entity reassembly
  4. Relationships and indices
  5. Geospatial data
  6. Data aggregation
  7. Transactions

Out of scope

Tracking entity changes, persisting entities, and the active record pattern.