quellabs / objectquel
A sophisticated ORM system with a unique query language and streamlined architecture
Requires
- ext-curl: *
- ext-fileinfo: *
- ext-gd: *
- ext-json: *
- ext-mysqli: *
- ext-pdo: *
- cakephp/database: *
- quellabs/annotation-reader: ^1.
- quellabs/contracts: ^1.
- quellabs/sculpt: ^1.
- quellabs/signal-hub: ^1.
- quellabs/support: ^1.
- robmorgan/phinx: *
- softcreatr/jsonpath: *
- dev-main
- 1.0.66
- 1.0.65
- 1.0.64
- 1.0.63
- 1.0.62
- 1.0.61
- 1.0.60
- 1.0.59
- 1.0.58
- 1.0.57
- 1.0.56
- 1.0.55
- 1.0.54
- 1.0.53
- 1.0.52
- 1.0.51
- 1.0.50
- 1.0.49
- 1.0.48
- 1.0.47
- 1.0.46
- 1.0.45
- 1.0.44
- 1.0.43
- 1.0.42
- 1.0.41
- 1.0.40
- 1.0.39
- 1.0.38
- 1.0.37
- 1.0.36
- 1.0.35
- 1.0.34
- 1.0.33
- 1.0.32
- 1.0.31
- 1.0.30
- 1.0.29
- 1.0.28
- 1.0.27
- 1.0.26
- 1.0.25
- 1.0.24
- 1.0.23
- 1.0.22
- 1.0.21
- 1.0.20
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- dev-feature/linear-array
- dev-feature/collection-filtering
This package is auto-updated.
Last update: 2026-03-10 14:47:06 UTC
README
A PHP ORM with its own query language. ObjectQuel uses the Data Mapper pattern and a declarative syntax inspired by QUEL to express entity queries at the domain level — not the table level.
$results = $entityManager->executeQuery(" range of p is App\\Entity\\Product range of c is App\\Entity\\Category via p.categories retrieve (p, c.name as categoryName) where p.price < :maxPrice and c.active = true sort by p.name asc ", [ 'maxPrice' => 50.00 ]);
The engine resolves entity relationships, decomposes the query into optimized SQL, and hydrates the results. You write intent; ObjectQuel handles the mechanics.
What the query language can do that others can't
Most ORM query languages are SQL with different syntax. ObjectQuel's abstraction layer sits above SQL, which lets it do things that aren't possible in DQL, Eloquent, or raw query builders:
Pattern matching and regex in where clauses:
// Wildcard matching — no LIKE syntax needed retrieve (p) where p.sku = "ABC*XYZ" // Regex with flags retrieve (p) where p.name = /^tech/i
The equivalent in Doctrine requires $qb->expr()->like() or a raw REGEXP call. In Eloquent you'd write whereRaw('name REGEXP ?', [...]). ObjectQuel treats patterns as first-class query expressions.
Full-text search with boolean operators and weighting:
retrieve (p) where search(p.description, "banana +pear -apple")
No raw SQL, no engine-specific syntax. The query engine translates this to the appropriate full-text implementation for your database.
Hybrid data sources — database + JSON in one query:
range of order is App\\Entity\\OrderEntity
range of product is json_source('external/product_catalog.json')
retrieve (order, product.name, product.manufacturer)
where order.productSku = product.sku and order.status = :status
sort by order.orderDate desc
ObjectQuel can join database entities with JSON files in a single query — the engine handles the cross-source matching. Neither Doctrine nor Eloquent can do this. You'd query the database, load the JSON separately, and merge results in PHP. ObjectQuel also supports JSONPath prefiltering to extract nested structures before the query runs, keeping memory usage low on large files.
Existence checks as expressions:
// In the retrieve clause retrieve (p.name, ANY(o.orderId) as hasOrders) // In the where clause retrieve (p) where ANY(o.orderId)
Automatic query decomposition:
Complex queries are split into optimized sub-tasks by the engine rather than sent as a single monolithic SQL statement. This means ObjectQuel can optimize execution paths that a single SQL query cannot express efficiently.
Comparison
A multi-entity query with filtering and relationship traversal:
ObjectQuel:
$results = $entityManager->executeQuery(" range of o is App\\Entity\\Order range of c is App\\Entity\\Customer via o.customerId retrieve (o, c.name) where o.createdAt > :since sort by o.createdAt desc window 0 using window_size 20 ");
Doctrine DQL:
$results = $entityManager->createQuery( 'SELECT o, c.name FROM App\\Entity\\Order o JOIN o.customer c WHERE o.createdAt > :since ORDER BY o.createdAt DESC' )->setParameter('since', $since) ->setMaxResults(20) ->getResult();
Eloquent:
$results = Order::with('customer:id,name') ->where('created_at', '>', $since) ->orderByDesc('created_at') ->take(20) ->get();
The difference becomes more pronounced with regex filtering, existence checks, hybrid sources, and multi-relationship traversals — operations that require raw SQL or post-processing in other ORMs.
Installation
composer require quellabs/objectquel
Supports MySQL, PostgreSQL, SQLite, and SQL Server through CakePHP's database abstraction layer (used for connection handling and SQL execution only — ObjectQuel implements its own query engine, Data Mapper, and entity management).
Quick start
use Quellabs\ObjectQuel\Configuration; use Quellabs\ObjectQuel\EntityManager; $config = new Configuration(); $config->setEntityNamespace('App\\Entity'); $config->setEntityPath(__DIR__ . '/src/Entity'); $entityManager = new EntityManager($config, $connection); // Standard lookups $product = $entityManager->find(Product::class, 101); $active = $entityManager->findBy(Product::class, ['active' => true]); // ObjectQuel for anything more complex $results = $entityManager->executeQuery(" range of p is App\\Entity\\Product retrieve (p) where p.name = /^Tech/i sort by p.createdAt desc window 0 using window_size 10 ");
ORM capabilities
ObjectQuel is a full Data Mapper ORM, not just a query language:
- Entity mapping — annotation-based with
@Orm\Table,@Orm\Column, and relationship annotations - Relationships — OneToOne, ManyToOne, OneToMany, ManyToMany (via bridge entities)
- Unit of Work — change tracking with persist and flush
- Lazy loading — configurable proxy generation with caching
- Immutable entities — for database views and read-only tables
- Optimistic locking — version-based concurrency control
- Cascading — configurable cascade operations across relationships
- Lifecycle events — pre/post persist, update, and delete via SignalHub
- Custom repositories — optional repository pattern with type-safe access
- Indexing — annotation-driven index management
- Migrations — database schema migrations powered by Phinx
CLI tooling
ObjectQuel ships with Sculpt, a CLI tool for entity and schema management:
# Generate a new entity interactively php bin/sculpt make:entity # Reverse-engineer entities from an existing database table php bin/sculpt make:entity-from-table # Generate migrations from entity changes php bin/sculpt make:migrations # Run pending migrations php bin/sculpt quel:migrate
make:entity-from-table is particularly useful when adopting ObjectQuel in an existing project — point it at your tables and get annotated entities without writing them by hand.
Framework integration
ObjectQuel works standalone or with the Canvas framework. The quellabs/canvas-objectquel package provides automatic service discovery, dependency injection, and Sculpt CLI integration within Canvas.
For other frameworks, configure the EntityManager directly — it has no framework dependencies.
Documentation
Full query language reference, entity mapping guide, and architecture docs: objectquel.com/docs
License
MIT