PHP code example of honey-odm / core

1. Go to this page and download the library: Download honey-odm/core library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

honey-odm / core example snippets


namespace App;

use DateTimeInterface;
use Honey\ODM\Core\Config\AsDocument;
use Honey\ODM\Core\Config\AsField;
use Honey\ODM\Core\Config\TransformerMetadata;
use Honey\ODM\Core\Mapper\PropertyTransformer\DateTimeImmutableTransformer;
use Honey\ODM\Core\Mapper\PropertyTransformer\RelationTransformer;

#[AsDocument(collection: 'books')]
final class Book
{
    public function __construct(
        #[AsField(primary: true)]
        public string $id,

        #[AsField(name: 'title')]
        public string $name,

        #[AsField(name: 'author_id', transformer: RelationTransformer::class)]
        public ?Author $author = null,

        #[AsField(name: 'published_at', transformer: new TransformerMetadata(DateTimeImmutableTransformer::class, ['from_format' => 'Y-m-d', 'to_format' => 'Y-m-d']))]
        public ?DateTimeInterface $publishedAt = null,
    ) {
    }
}

use Honey\ODM\Core\Config\AsDocument;
use Honey\ODM\Core\Config\AsField;
use Honey\ODM\Meilisearch\Config as Meili;

#[AsDocument(collection: 'books')]
#[Meili\Document(rankingRules: ['words', 'typo', 'sort'])]
final class Book
{
    public function __construct(
        #[AsField(primary: true)]
        #[Meili\Attribute(filterable: true, sortable: true)]
        public int $id,
    ) {
    }
}

$classMetadata = $objectManager->getClassMetadata(Book::class);

$classMetadata->getPlatformMetadata(Meili\Document::class)?->rankingRules;
$classMetadata->getPropertyMetadata('id')->getPlatformMetadata(Meili\Attribute::class)?->filterable;

use Honey\ODM\Core\Config\AsDocument;
use Honey\ODM\Core\Config\ClassMetadataRegistry;

$registry = new ClassMetadataRegistry(configurations: [
    Book::class => new AsDocument(collection: 'books'),
]);

$registry = new ClassMetadataRegistry(configurations: [Book::class, Author::class]);

use Honey\ODM\Core\Criteria\Criteria;

use function Honey\ODM\Core\Criteria\field;
use function Honey\ODM\Core\Criteria\not;

$criteria = Criteria::create()
    ->search('gatsby')
    ->where(field('publishedAt')->greaterThan('1920-01-01'))
    ->andWhere(field('author')->in([1, 2, 3]))
    ->orWhere(not(field('name')->startsWith('Draft:')))
    ->orderBy('publishedAt', 'desc')
    ->orderBy('name')
    ->limit(20)
    ->offset(40);

$books = $objectManager->getRepository(Book::class)->findBy($criteria);

$repository->findBy(['name' => 'The Great Gatsby']);   // shorthand for Criteria::fromArray([...])
$repository->findOneBy(['id' => '123']);
$repository->findAll();

field('summary')->exists();     // the key is present in the document, whatever its value - including null
field('summary')->isNotNull();  // the key is present AND its value is not null
field('summary')->isEmpty();    // the value is null, '', [] or {}

field('price')->between(10, 100);                        // 10 <= price <= 100
field('price')->between(10, 100, 

field('tags')->in(['monument', 'paris']);       // tagged monument OR paris
field('tags')->hasAll(['monument', 'paris']);   // tagged monument AND paris

field('coordinates')->withinGeoRadius(48.8566, 2.3522, 5000);          // within 5 km of Paris
field('coordinates')->withinGeoBoundingBox(48.80, 2.22, 48.90, 2.47);  // swLat, swLon, neLat, neLon

use Honey\ODM\Core\Criteria\Geo\BoundingBox;
use Honey\ODM\Core\Criteria\Geo\Coordinates;
use Honey\ODM\Core\Criteria\Geo\Radius;

new Coordinates(91.0, 0.0);                              // InvalidArgumentException: Invalid latitude
new Radius(new Coordinates(48.85, 2.35), -1);            // InvalidArgumentException: Radius must be greater than 0
new BoundingBox(new Coordinates(48.9, 2.2), new Coordinates(48.8, 2.4)); // InvalidArgumentException

new Comparison('coordinates', Operator::WITHIN_GEO_RADIUS, $radius);

$criteria = Criteria::create()
    ->search('a bee on a flower')
    ->metadata('vector', $embedding)
    ->metadata('semanticRatio', 0.9);

use Honey\ODM\Core\Criteria\UnsupportedExpressionException;

throw UnsupportedExpressionException::expression($expression);
throw UnsupportedExpressionException::operator(Operator::STARTS_WITH);
throw UnsupportedExpressionException::feature('offset');

use Honey\ODM\Core\Manager\ObjectManager;

$objectManager = new ObjectManager(new MyTransport(...));

$objectManager = new ObjectManager(
    transport: $transport,
    classMetadataRegistry: new ClassMetadataRegistry(),  // default
    documentMapper: new DocumentMapper(),                // default
    eventDispatcher: $psr14EventDispatcher,              // defaults to a no-op dispatcher
    defaultFlushOptions: [],                             // implementation-specific options passed to the transport
    repositoryFactory: null,                             // Closure(ObjectManager, class-string): ObjectRepositoryInterface
);

$book = new Book(id: '1', name: 'The Great Gatsby');

$objectManager->persist($book);
$objectManager->flush();

$objectManager->remove($book);
$objectManager->flush(['wait' => true]); // options are merged with $defaultFlushOptions

$book = $objectManager->find(Book::class, '1');
$books = $objectManager->getRepository(Book::class)->findBy(['name' => 'The Great Gatsby']);

$objectManager->clear(); // detaches everything and resets the Unit of Work

$objectManager = new ObjectManager(
    transport: $transport,
    repositoryFactory: fn (ObjectManager $om, string $className) => new MyRepository($om, $className),
);

$objectManager->registerRepository(Book::class, new BookRepository($objectManager, Book::class));

interface ObjectRepositoryInterface
{
    public function findBy(Criteria|array|null $criteria): iterable;
    public function findAll(): iterable;
    public function findOneBy(Criteria|array $criteria): ?object;
    public function find(mixed $id): ?object;
}

$book1 = $objectManager->find(Book::class, '123');
$book2 = $objectManager->find(Book::class, '123');

var_dump($book1 === $book2); // true - same instance returned

use Honey\ODM\Core\Event\PrePersistEvent;

$eventDispatcher->addListener(PrePersistEvent::class, function (PrePersistEvent $event) {
    $event->object->createdAt = new DateTimeImmutable();
});

use Honey\ODM\Core\Config\AsField;
use Honey\ODM\Core\Mapper\MappingContextInterface;
use Honey\ODM\Core\Mapper\PropertyTransformer\PropertyTransformerInterface;

final class MoneyTransformer implements PropertyTransformerInterface
{
    public function fromDocument(mixed $value, AsField $propertyMetadata, MappingContextInterface $context): ?Money
    {
        return null === $value ? null : Money::fromCents($value);
    }

    public function toDocument(mixed $value, AsField $propertyMetadata, MappingContextInterface $context): ?int
    {
        return $value?->cents;
    }
}

use Honey\ODM\Core\Mapper\DocumentMapper;
use Honey\ODM\Core\Mapper\PropertyTransformer\PropertyTransformers;

$transformers = new PropertyTransformers();
$transformers->register(new MoneyTransformer());

$objectManager = new ObjectManager(
    transport: $transport,
    documentMapper: new DocumentMapper(transformers: $transformers),
);

use Honey\ODM\Core\Manager\ObjectManager;
use Honey\ODM\Core\Transport\InMemoryTransport;

$transport = new InMemoryTransport();
$objectManager = new ObjectManager($transport);

$objectManager->persist(new Book('123', 'The Great Gatsby'));
$objectManager->flush();

$books = $objectManager->getRepository(Book::class)->findBy(
    Criteria::create()->where(field('name')->contains('Gatsby')),
);

$transport->storage['books'] = [
    '123' => ['id' => '123', 'title' => 'The Great Gatsby', 'author_id' => 1],
];

interface TransportInterface
{
    public function flushPendingOperations(UnitOfWork $unitOfWork, array $flushOptions = []): void;

    /**
     * @param AsDocument<object> $classMetadata
     * @return iterable<array<string, mixed>>
     * @throws UnsupportedExpressionException
     */
    public function retrieveDocuments(AsDocument $classMetadata, Criteria $criteria): iterable;

    /**
     * @param AsDocument<object> $classMetadata
     * @return array<string, mixed>|null
     */
    public function retrieveDocumentById(AsDocument $classMetadata, mixed $id): ?array;
}

use Honey\ODM\Core\Mapper\MappingContext;
use Honey\ODM\Core\Transport\TransportInterface;
use Honey\ODM\Core\UnitOfWork\UnitOfWork;

final class RestTransport implements TransportInterface
{
    public function __construct(
        private ClientInterface $httpClient,
        private string $baseUrl,
    ) {
    }

    public function flushPendingOperations(UnitOfWork $unitOfWork, array $flushOptions = []): void
    {
        $objectManager = $unitOfWork->objectManager;
        $registry = $objectManager->classMetadataRegistry;
        $mapper = $objectManager->documentMapper;

        foreach ($unitOfWork->getPendingUpserts() as $object) {
            $classMetadata = $registry->getClassMetadata($object::class);
            $context = new MappingContext($classMetadata, $objectManager, $object, []);
            $document = $mapper->objectToDocument($object, [], $context);
            $id = $objectManager->getIdentifier($object);
            $endpoint = $this->baseUrl . '/' . $classMetadata->collection;

            $this->httpClient->put("{$endpoint}/{$id}", ['json' => $document]);
        }

        foreach ($unitOfWork->getPendingDeletes() as $object) {
            $classMetadata = $registry->getClassMetadata($object::class);
            $id = $objectManager->getIdentifier($object); // Doesn't initialize lazy objects

            $this->httpClient->delete("{$this->baseUrl}/{$classMetadata->collection}/{$id}");
        }
    }

    // retrieveDocuments() / retrieveDocumentById() omitted for brevity
}

namespace MyODM\Config;

use Attribute;
use Honey\ODM\Core\Config\PlatformMetadataInterface;

#[Attribute(Attribute::TARGET_CLASS)]
final readonly class Collection implements PlatformMetadataInterface
{
    public function __construct(
        public ?int $shards = null,
    ) {
    }
}

$objectManager = new ObjectManager(new RestTransport($httpClient, 'https://api.example.com'));

$book = new Book(id: '1', name: 'The Great Gatsby');
$objectManager->persist($book);
$objectManager->flush(); // HTTP PUT /books/1

$foundBook = $objectManager->find(Book::class, '1'); // HTTP GET /books/1