Download the PHP package honey-odm/core without Composer

On this page you can find all versions of the php package honey-odm/core. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package core

🐝 Honey / ODM

A framework-agnostic, core foundation library for building modern Object Document Mappers (ODM) in PHP.

CI Workflow codecov

Overview

Honey ODM provides the essential components and patterns needed to build robust ODMs on top of any data source: REST APIs, search engines, NoSQL databases, or any custom storage backend.

The mapping layer is portable: a class annotated for Honey ODM can run on any implementation (Meilisearch, SQLite, Elasticsearch, ...) without being re-mapped. Everything platform-specific lives in dedicated attributes, and querying goes through a generic, compilable criteria model.

Key Features

Requirements

Installation

The library ships a polyfill for the native PHP 8.6 SortDirection enum, so sorting works on PHP 8.4+.

Glossary

Mapping your documents

Mapping relies on two final core attributes:

Platform-specific metadata

Implementations provide their own attributes implementing PlatformMetadataInterface. They are placed alongside the core attributes and collected by the registry:

Retrieve them from either level:

The same class can therefore carry metadata for several platforms at once β€” each implementation simply ignores what isn't addressed to it.

External metadata

Classes you cannot annotate (third-party classes, anonymous classes) can be registered by providing their AsDocument instance to the registry:

Passing a plain list of class names instead warms up the registry eagerly:

Otherwise, metadata is read lazily on first access. Note that external metadata replaces the class-level attribute only: properties are still read from their #[AsField] attributes.

Querying

Queries are expressed with the generic Criteria object, in PHP property names. Each transport compiles them into its own native query language.

Properties are named after their PHP name (publishedAt, not published_at), but values are compared against their storage representation: property transformers are not applied to criteria values. In the example above, field('author') is matched against author ids, since that's what the author_id field holds.

For simple equality filters, an array is enough β€” it is AND-combined:

Expressions

The expression tree is built from three node types, all implementing ExpressionInterface:

Node Built with
Comparison field('property')->equals($value), or new Comparison(...)
CompositeExpression CompositeExpression::and(...) / ::or(...)
Negation not($expression)

Available operators (Honey\ODM\Core\Criteria\Operator):

Field method Operator Opposite
equals($value) EQUALS notEquals($value) *
greaterThan($value) GREATER_THAN β€”
greaterThanOrEquals($value) GREATER_THAN_OR_EQUALS β€”
lessThan($value) LESS_THAN β€”
lessThanOrEquals($value) LESS_THAN_OR_EQUALS β€”
between($left, $right, $includeLeft = true, $includeRight = true) BETWEEN notBetween(...)
in(array $values) IN notIn(array $values) *
hasAll(array $values) HAS_ALL notHasAll(array $values)
contains(string $value) CONTAINS notContains($value)
startsWith(string $value) STARTS_WITH notStartsWith($value)
endsWith(string $value) ENDS_WITH notEndsWith($value)
isNull() IS_NULL isNotNull() *
exists() EXISTS notExists()
isEmpty() IS_EMPTY isNotEmpty()
withinGeoRadius($lat, $lon, $meters) WITHIN_GEO_RADIUS outsideGeoRadius(...)
withinGeoBoundingBox($swLat, $swLon, $neLat, $neLon) WITHIN_GEO_BOUNDING_BOX outsideGeoBoundingBox(...)

search() is a full-text search term, only meaningful on search-capable platforms.

Only the three starred methods are operators of their own (NOT_EQUALS, NOT_IN, IS_NOT_NULL), because every platform spells them natively. All the other not* / outside* methods return a Negation wrapping their positive counterpart β€” field('name')->notContains('draft') is exactly not(field('name')->contains('draft')). Transports get them for free, and adapter authors have nothing to implement beyond Negation itself.

⚠️ A negation is not the mirror of its operator on absent values: notBetween(10, 100) and outsideGeoRadius(...) match documents holding no value at all, since those don't match the positive form either.

Criteria is mutable and fluent: where() replaces the current filter, andWhere() / orWhere() combine with it. Clone it if you want to derive several queries from a common base.

Presence, emptiness and nullity

Three distinct questions, three operators:

A document whose summary is explicitly null satisfies exists() and isEmpty(), but fails isNotNull().

Ranges

between() builds a single BETWEEN node carrying a Range value object, so adapters can emit native range syntax rather than a pair of comparisons. Inclusivity is per-side, and either bound may be null (but not both):

A null field value never matches a range.

Sets

On an array field, in() matches when at least one of the given values is held, hasAll() when all of them are:

Their opposites are easy to mix up, so here they are side by side:

tags in([a, b]) notIn([a, b]) hasAll([a, b]) notHasAll([a, b])
['a', 'b'] βœ… ❌ βœ… ❌
['a'] βœ… ❌ ❌ βœ…
['c'] ❌ βœ… ❌ βœ…

notIn() means none of them; notHasAll() means not all of them.

contains() / startsWith() / endsWith() are substring operators on string fields β€” don't confuse contains('paris') with hasAll(['paris']).

Geo

Geo filters carry value objects from Honey\ODM\Core\Criteria\Geo, and distances are always in meters:

Bounding boxes go from the south-west (min) corner to the north-east (max) one, following GeoJSON, PostGIS, OGC/WMS, Leaflet and Google Maps. Platforms expecting another corner pair β€” Elasticsearch takes north-west / south-east, Meilisearch north-east / south-west β€” convert on their side. A box whose west longitude is greater than its east longitude legitimately crosses the antimeridian.

Coordinates, Radius and BoundingBox validate their input, so an impossible query fails at build time rather than at the storage layer:

Holding a pre-built value object? Use the node directly:

Platform-specific criteria

Some queries have no portable form at all: vector search, image similarity, a matching strategy… Rather than forcing you to subclass Criteria, it carries a free-form metadata bag that transports read at will:

A transport that knows the key uses it; one that doesn't ignores it silently β€” this is opt-in, so unlike an unsupported operator it never throws. Keys are yours; prefix them if you target several platforms at once.

Capability mismatches

No platform supports everything. When a transport cannot compile an expression, an operator or a feature, it throws UnsupportedExpressionException:

Using the Object Manager

That's the only mandatory argument. Everything else is optional and has a sensible default:

Persisting and retrieving

Objects returned by find() / repositories are lazy ghosts: the document is only mapped to the object when one of its properties is actually accessed.

Repositories

By default, getRepository() returns the generic ObjectRepository. Implementations exposing native query capabilities can provide their own default through the repositoryFactory closure:

You can also register a repository for a single class:

Repositories implement ObjectRepositoryInterface:

Identity management

Objects are tracked and deduplicated per class + id:

Changes made on managed objects are detected at flush time by the Unit of Work β€” you don't need to persist() an object that is already managed.

Change detection compares the object's current serialization with the one taken when it was hydrated (or when it was last flushed). Both sides therefore go through the same mapping pipeline: reading documents never schedules a write, even when the stored document holds attributes your class doesn't map, or when a transformer can't round-trip its value (a relation pointing to a missing document, for instance).

Values are compared strictly: null, '', [], 0 and false are distinct from one another, so a null -> '' change is written instead of being silently dropped. Objects are the exception β€” they are compared by value, so two equivalent value objects don't count as a change.

An id has exactly one managed instance: persisting a fresh object carrying the id of an already managed one detaches the latter, so the last persisted instance wins. Without it, both would be handed over to the transport within the same batch, and the surviving document would be picked by iteration order.

Objects which were loaded but never accessed stay lazy β€” they can't be dirty, so they're skipped when changesets are computed. ObjectManager::getIdentifier() returns an object's id without initializing it, which is what the transformers and transports use to resolve relations and deletions.

Events

Available events:

Pre-flush events may modify objects: changesets are recomputed until they stabilize. Calling flush() from within a listener is a no-op, to prevent recursion.

Property transformers

Transformers convert values between the storage representation and PHP:

Transformer Purpose Options
DateTimeImmutableTransformer Dates from_format, from_tz, to_format, to_tz, to_type
BackedEnumTransformer Backed enums (target class inferred from the property type) target_class
RelationTransformer To-one relation, stored as the related document id target_class
RelationsTransformer To-many relation, stored as a list of ids target_class (required)
StringableTransformer Value objects exposing fromString() and __toString() (e.g. Ulid) β€”

Writing your own is a matter of implementing PropertyTransformerInterface:

Then register it in the transformers container passed to the mapper:

PropertyTransformers is a PSR-11 container keyed by class name β€” any other PSR-11 container will do.

Testing without a database

InMemoryTransport is a complete transport backed by a plain array. Swap it in and your tests exercise the real object manager β€” mapping, transformers, identity map, unit of work, events β€” with no service to boot:

Its $storage property is public, so fixtures can be seeded without going through a flush β€” documents are indexed by collection, then by id:

It supports every operator of the criteria API, which also makes it the executable specification of what a transport is supposed to do. Its filtering is naive (it walks the whole collection in PHP) β€” it's built for correctness in tests, not for volume.

search() matches literal substrings across every string field, ignoring case and diacritics β€” cafe finds CafΓ©. No typo tolerance, no stemming, no ranking, and that's deliberate: a double that approximated a real engine's relevance would let tests pass locally and fail in production. The diacritics folding needs ext-intl, which the package doesn't require β€” searching without it throws, everything else works.

Building your own ODM

Since metadata, mapping, criteria, repositories and the object manager are all provided by the core, an implementation boils down to one transport, plus optional platform metadata attributes.

1. Implement the transport

Important:

src/Transport/InMemoryTransport.php is a complete reference implementation (filters, negation, composite expressions, multi-key sorting, pagination, search) β€” a good starting point for adapter authors.

2. Flushing pending operations

The Unit of Work exposes getPendingInserts(), getPendingUpdates(), getPendingUpserts(), getPendingDeletes(), getChangedObjects() and getPendingOperation($object) β€” use whichever granularity your backend needs (an API with a dedicated POST for creations, a bulk endpoint, ...).

3. Add platform metadata (optional)

4. Wire everything

Contributing

We welcome contributions! Here's how to get started:

Development Setup

  1. Clone the repository:

  2. Install dependencies:

  3. Run checks:

Testing

The library uses Pest for testing. Tests are located in the tests/ directory:

Run the full test suite:

Code Standards

Submitting Changes

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with tests
  4. Ensure all checks pass (composer ci:check)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Reporting Issues

Please use GitHub Issues to report bugs or request features. Include:

Known Implementations

License

MIT.


All versions of core with dependencies

PHP Build Version
Package Version
Requires php Version >=8.4
psr/event-dispatcher Version ^1.0
symfony/property-access Version ~7.0|~8.0
bentools/reflection-plus Version ^1.0
psr/container Version ^2.0
bentools/iterable-functions Version ^2.3
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package honey-odm/core contains the following files

Loading the files please wait ...