wizbii/mongo-bundle

Installs: 12 631

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Forks: 0

Type:symfony-bundle

3.2.1 2023-03-08 09:02 UTC

README

pipeline status coverage report

Objectives

This package aims to help communicate with mongodb in an elegant manner for developers. It lifts the burden of having to manipulate arrays, depends on an heavy package like Doctrine ODM, ... It is based on a MongoClient class which provides most of basic mongodb operations but it's much more easy to use than the base mongodb implementation. This class implements a MongoClientInterface that is also implemented by two other implementations :

  • an InMemory implementation that is designed for unitary tests. Most query operators and updaters are supported. See below for a complete reference
  • a LocalFile implementation that is designed for functional tests. Its is based on the same LocalEngine as the InMemory implementation and therefore, supports the same query operators and updaters

Installation

composer require wizbii/mongo-bundle

Usage in production mode

First, create an object that holds your model. Please note that this object must implement ArraySerializable interface as explained in https://gitlab.com/wizbii-open-source/json-serializer-bundle:

<?php 

namespace App\Model;

use Wizbii\JsonSerializerBundle\ArraySerializable;

class SimpleObject implements ArraySerializable
{
    private string $id;
    private string $value;

    public function __construct(string $id, string $value)
    {
        $this->id = $id;
        $this->value = $value;
    }

    public function serialize(): array
        {
            return [
                '_id' => $this->id,
                'value' => $this->value,
            ];
        }
    
        public static function deserialize(array $contentAsArray)
        {
            return new self($contentAsArray['_id'], $contentAsArray['value']);
        }
}

Then, create a service that needs to manipulate this object from mongodb:

<?php 

namespace App\Repository;

use Wizbii\OpenSource\MongoBundle\MongoClientBuilderInterface;
use Wizbii\OpenSource\MongoBundle\MongoClientInterface;
use App\Model\SimpleObject;

class SimpleObjectRepository
{
    /** @phpstan-var MongoClientInterface<SimpleObject> */
    private MongoClientInterface $mongoClient;

    /** @phpstan-param MongoClientBuilderInterface<SimpleObject> $mongoClientBuilder */
    public function __construct(MongoClientBuilderInterface $mongoClientBuilder)
    {
        $this->mongoClient = $mongoClientBuilder->buildFor('test_database', 'simple_object_collection', SimpleObject::class);
    }

    public function readSimpleObject(string $simpleObjectId): SimpleObject
    {
        /** @var SimpleObject */
        $simpleObject = $this->mongoClient->get($simpleObjectId); 
        return $simpleObject;
    }

    /** @return SimpleObject[] */
    public function findSimpleObjectNamedRemi(int $rows = 10, int $offset = 0): array
    {
        return $this->mongoClient->findBy(['name' => 'Rémi'], $rows, $offset);
    }

    public function createSimpleObject(): string
    {
        $this->mongoClient->put(new SimpleObject('id-abcd', 'Rémi'));
    }
}

Of course, you can tune the mongo configuration using :

<?php 
namespace App\Repository;

use Wizbii\OpenSource\MongoBundle\MongoClientBuilderInterface;
use Wizbii\OpenSource\MongoBundle\MongoClientInterface;
use App\Model\SimpleObject;

class SimpleObjectRepository
{
    /** @phpstan-var MongoClientInterface<SimpleObject> */
    private MongoClientInterface $mongoClient;

    /** @phpstan-param MongoClientBuilderInterface<SimpleObject> $mongoClientBuilder */
    public function __construct(MongoClientBuilderInterface $mongoClientBuilder)
    {
        $this->mongoClient = $mongoClientBuilder
            ->overrideConfigurationFor('test_database', 'simple_object_collection', SimpleObject::class)
            ->setReadPreference('primary')
            ->setRetryWrites(true)
            //->...
            ->end()->build();
    }
}

Usage in development mode

The code of your repository does not change: it is the same for production and development modes. But when you execute your unitary tests, the class that will be injected for the MongoClientBuilderInterface constructor argument will be using the InMemory implementation.

This is an example of how to write unitary tests for such a Repository:

<?php

namespace App\Tests\Repository;

use App\Model\SimpleObject;
use App\Repository\SimpleObjectRepository;
use Tests\Wizbii\OpenSource\MongoBundle\MongoClientTestCase;

class SimpleObjectRepositoryTest extends MongoClientTestCase
{
    public function test_it_can_create_and_retrieve_simple_objects()
    {
        $simpleObjectRepository = new SimpleObjectRepository($this->getMongoClientBuilder());
        $simpleObjectRepository->createSimpleObject($this->getSimpleObject('remi', 'Rémi'));
        $simpleObject = $simpleObjectRepository->readSimpleObject('remi');
        $this->assertThat($simpleObject, $this->isInstanceOf(SimpleObject::class));
        $this->assertThat($simpleObject->getValue(), $this->equalTo('Rémi'));
    }

    public function test_it_can_find_simple_objects_by_name()
    {
        $simpleObjectRepository = new SimpleObjectRepository($this->getMongoClientBuilder());
        $simpleObjectRepository->create($this->getSimpleObject('remi-1', 'Rémi'));
        $simpleObjectRepository->create($this->getSimpleObject('mark', 'Mark'));
        $simpleObjectRepository->create($this->getSimpleObject('remi-2', 'Rémi'));
        $simpleObjectRepository->create($this->getSimpleObject('remi-3', 'Rémi'));
        $simpleObjects = $simpleObjectRepository->findSimpleObjectNamedRemi();
        $this->assertThat($simpleObjects, $this->countOf(3));
        $this->assertThat($simpleObjects[0]->getId(), $this->equalTo('remi-1'));
        $this->assertThat($simpleObjects[1]->getId(), $this->equalTo('remi-2'));
        $this->assertThat($simpleObjects[2]->getId(), $this->equalTo('remi-3'));
    }

    private function getSimpleObject(string $id, string $value): SimpleObject
    {
        return new SimpleObject($id, $value);
    }
}

Supported Mongo Features

Query OperatorReal ImplementationLocal EngineComments
$eqOKOK
$gtOKOK
$gteOKOK
$inOKOK
$ltOKOK
$lteOKOK
$neOKOK
$ninOKOK
$andOKOK
$notOKOK
$norOKOK
$orOKOK
$existsOKOK
$typeOKOKSome types are not supported. See TypeFilter class for details
$exprOKKO
$jsonSchemaOKKO
$modOKKO
$regexpOKOK
$textOKKO
$whereOKKO
$geoIntersectsOKKO
$geoWithinOKKO
$nearOKKO
$nearSphereOKKO
$allOKOK
$elemMatchOKOK
$sizeOKOK
$bitsAllClearOKKO
$bitsAllSetOKKO
$bitsAnyClearOKKO
$bitsAnySetOKKO
$commentOKOK
UpdatersReal ImplementationLocal EngineComments
$currentDateOKKO
$incOKKO
$minOKKO
$maxOKKO
$mulOKKO
$renameOKKO
$setOKOK
$setOnInsertOKKO
$unsetOKKO
$addToSetOKKO
$popOKKO
$pullOKKO
$pushOKKO
$pushAllOKKO
$eachOKKO
$positionOKKO
$sliceOKKO
$sortOKKO
$bitOKKO

Contribute

  1. Fork the repository
  2. Make your changes
  3. Test them with composer dev:checks (it will run test, phpstan and cs:lint subcommands)
  4. Create a Merge Request