jeyroik/extas-repositories-mongo

View on GitHub
src/components/repositories/drivers/DriverMongo.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php
namespace extas\components\repositories\drivers;

use extas\components\THasConfig;
use extas\interfaces\IItem;
use MongoDB\Collection;

class DriverMongo extends Driver
{
    protected const FIELD__DSN = 'dsn';
    protected const FIELD__DB = 'db';
    protected const FIELD__TABLE = 'table';

    use THasConfig;
    use THasConfig {
        THasConfig::__construct as baseConstruct;
    }

    /**
     * @var Collection
     */
    protected ?Collection $collection = null;

    /**
     * ClientTableMongo constructor.
     *
     * @param $collection
     */
    public function __construct(array $config = [])
    {
        $this->baseConstruct($config);

        $driver = new \MongoDB\Client($this->getDsn());
        $dbName = $this->getDbName();
        $tableName = $this->getTableName();
        $this->collection = $driver->$dbName->$tableName;
    }

    /**
     * @param array|Where $query
     * @param int $offset
     * @param array $fields
     *
     * @return array|IItem|null
     * @throws
     */
    public function findOne(array $query = [], int $offset = 0, array $fields = [])
    {
        $this->prepareQuery($query);

        $options = ['skip' => $offset];

        if (!empty($fields)) {
            $options['projection'] = $fields;
        }
        
        $record = $this->collection->findOne($query, $options);

        if ($record) {
            $record = $record->getArrayCopy();
            if ($this->getPk() != '_id') {
                unset($record['_id']);
            } else {
                $record['_id'] = (string)$record['_id'];
            }
            return $this->unSerializeItem($record);
        }

        return $record;
    }

    /**
     * @param array|Where $query
     * @param int $limit
     * @param int $offset
     * @param array $orderBy
     * @param array $fields
     *
     * @return array|IItem[]
     * @throws
     */
    public function findAll(array $query = [], int $limit = 0, int $offset = 0, array $orderBy = [], array $fields = [])
    {
        /**
         * @var $recordsCursor Cursor
         */
        $this->prepareQuery($query);
        $options = ['skip' => $offset, 'limit' => $limit, 'sort' => $orderBy];

        if (!empty($fields)) {
            $options['projection'] = $fields;
        }

        $rawRecords = $this->collection->find($query, $fields);
        $records = [];

        foreach ($rawRecords as $record) {
            $record = $this->unSerializeItem($record);
            if ($this->getPk() != '_id') {
                unset($record['_id']);
            } else {
                $record['_id'] = (string)$record['_id'];
            }
            $records[] = $record;
        }

        return $records;
    }

    /**
     * @param IItem $item
     *
     * @return IItem
     * @throws \Exception
     */
    public function insert($item)
    {
        $itemData = $item->__toArray();
        $result = $this->collection->insertOne($itemData);

        if ($result->getInsertedCount()) {
            if ($this->getPk() == '_id') {
                $item['_id'] = $result->getInsertedId();
            }
            return $item;
        }

        throw new \Exception('Can not insert a record');
    }

    /**
     * @param array $query
     * @param $data
     *
     * @return int
     * @throws
     */
    public function updateMany($query, $data)
    {
        if ($data instanceof IItem) {
            $data = $data->__toArray();
        }

        $result = $this->collection->updateMany($query, $this->prepareForUpdate($data));

        return $result->getModifiedCount();
    }

    /**
     * @param IItem $item
     *
     * @return bool
     * @throws
     */
    public function update($item): bool
    {
        $pk = $this->getPk() == '_id' ? new \MongoId($item[$this->getPk()]) : $item[$this->getPk()];
        if (isset($item['_id'])) {
            unset($item['_id']);
        }

        $result = $this->collection->updateOne([$this->getPk() => $pk], $this->prepareForUpdate($item->__toArray()));

        return $result->getModifiedCount() ? true : false;
    }

    /**
     * @param array $query
     *
     * @return int
     * @throws
     */
    public function deleteMany($query)
    {
        if ($query instanceof IItem) {
            $query = $query->__toArray();
        }

        $this->prepareQuery($query);
        $result = $this->collection->deleteMany($query);

        return $result->getDeletedCount();
    }

    /**
     * @param IItem $item
     *
     * @return bool
     * @throws
     */
    public function delete($item): bool
    {
        if ($this->getPk() == '_id') {
            $item[$this->getPk()] = new \MongoId($item[$this->getPk()]);
        }

        $result = $this->collection->deleteOne([$this->getPk() => $item[$this->getPk()]]);

        return $result->getDeletedCount() ? true : false;
    }

    /**
     * @return bool
     */
    public function drop(): bool
    {
        $this->collection->drop();

        return true;
    }

    /**
     * @param array $groupBy
     *
     * @return $this
     * @throws
     */
    public function  group(array $groupBy)
    {
        $this->collection->aggregate(function ($a) use ($groupBy) {
            /**
             * @var $a Aggregation
             */
            $a->group(function ($g) use ($groupBy) {
                /**
                 * @var $g Group
                 */
                $g->by([$groupBy]);
            });
        });

        return $this;
    }

    public function getDbName(): string
    {
        return $this->config[static::FIELD__DB] ?? '';
    }

    public function getDsn(): string
    {
        return $this->config[static::FIELD__DSN] ?? '';
    }

    /**
     * @param $item
     *
     * @return array
     */
    protected function prepareForUpdate($item)
    {
        return ['$set' => $item];
    }

    /**
     * @param $item
     *
     * @return array
     */
    protected function unSerializeItem($item)
    {
        $unSerialized = [];

        $item = (array) $item;

        foreach ($item as $field => $value) {
            if (is_object($value)) {
                $value = $this->unSerializeItem($value);
            }

            $unSerialized[$field] = $value;
        }

        return $unSerialized;
    }

    /**
     * @param array $query
     */
    protected function prepareQuery(&$query)
    {
        foreach ($query as $fieldName => $fieldValue) {
            if (is_array($fieldValue)) {
                $query[$fieldName] = ['$in' => $fieldValue];
            }
        }
    }
}