php-vivace/db

View on GitHub
src/sql/Finder.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
/**
 * Created by PhpStorm.
 * User: macbookpro
 * Date: 14.02.2018
 * Time: 12:48
 */

namespace vivace\db\sql;

use vivace\db\Relation;
use vivace\db\Reader;
use vivace\db\mixin;
use vivace\db\sql\Statement\Count;
use vivace\db\sql\Statement\Delete;
use vivace\db\sql\Statement\Select;
use vivace\db\sql\Statement\Update;

class Finder implements \vivace\db\Finder
{
    use mixin\Filter;
    use mixin\Projection;

    protected $limit;
    protected $skip;
    protected $sort = [];
    /** @var Storage */
    protected $storage;


    public function __construct(Storage $storage, array $projection = [])
    {
        $this->storage = $storage;
        $this->projection = $projection;
    }

    /**
     * @param int|null $value
     *
     * @return Finder
     */
    public function limit(?int $value)
    {
        $o = clone $this;
        $o->limit = $value;
        return $o;
    }

    public function storage(): Storage
    {
        return $this->storage;
    }

    /**
     * @param int|null $value
     *
     * @return Finder
     */
    public function skip(?int $value)
    {
        $o = clone $this;
        $o->skip = $value;
        return $o;
    }

    public function sort(?array $map)
    {
        $o = clone $this;
        if ($map === null) {
            $o->sort = [];
        } elseif ($map) {
            $o->sort = array_merge($this->sort, $map);
        }

        return $o;
    }

    /**
     * @param array $projection
     * @param array $filter
     *
     * @return array
     * @throws \Exception
     */
    protected function normalizeFilter(?array $projection, array $filter): array
    {
        if (!$filter) {
            return $filter;
        }

        $schema = $this->storage()->schema();

        if (!isset($filter[0])) {
            $normalized = [];
            foreach ($filter as $key => $value) {
                $key = $projection && is_string($projection[$key]) ? $projection[$key] : $key;
                $normalized[$key] = $this->storage()->driver()->typecastIn($schema->get($key), $value);
            }
            return $normalized;
        }

        switch ($filter[0]) {
            case '=':
            case '>':
            case '<':
            case '>=':
            case '<=':
            case '!=':
                if ($projection && is_string($projection[$filter[1]])) {
                    $filter[1] = $projection[$filter[1]];
                }
                $property = $schema->get($filter[1]);
                $filter[2] = $this->storage()->driver()->typecastIn($property, $filter[2]);
                break;
            case 'and':
            case 'or':
                $cnt = count($filter);
                for ($i = 1; $i < $cnt; $i++) {
                    $filter[$i] = $this->normalizeFilter($projection, $filter[$i]);
                }
                break;
            case 'in':
                if (is_array($filter[1])) {
                    foreach ($filter[1] as $i => &$column) {

                        if ($projection && is_string($projection[$column])) {
                            $column = $projection[$column];
                        }
                        $property = $schema->get($column);

                        foreach ($filter[2] as &$values) {
                            $values[$i] = $this->storage()->driver()->typecastIn($property, $values[$i]);
                        }
                    }
                } else {
                    if ($projection && is_string($projection[$filter[1]])) {
                        $filter[1] = $projection[$filter[1]];
                    }
                    $property = $schema->get($filter[1]);
                    foreach ($filter[2] as &$value) {
                        $value = $this->storage()->driver()->typecastIn($property, $value);
                    }
                }

                break;
            case 'between':
                if ($projection && is_string($projection[$filter[1]])) {
                    $filter[1] = $projection[$filter[1]];
                }
                $property = $schema->get($filter[1]);
                $filter[2] = $this->storage()->driver()->typecastIn($property, $filter[2]);
                $filter[3] = $this->storage()->driver()->typecastIn($property, $filter[3]);
                break;
        }

        return $filter;
    }

    /**
     * @param array $projection
     * @param array $sort
     *
     * @return array
     */
    protected function normalizeSort(?array $projection, array $sort): array
    {
        $new = [];
        foreach ($sort as $key => $value) {
            if ($projection && is_string($projection[$key])) {
                $key = $projection[$key];
            }
            $new[$key] = $value;
        }

        return $new;
    }

    /**
     * @return \vivace\db\Reader
     * @throws \Exception
     */
    public function fetch(): Reader
    {
        $query = new Select($this->storage()->getSource());

        if ($this->filter) {
            $query->where = $this->normalizeFilter($this->projection, $this->filter);
        }
        if ($this->sort) {
            $query->order = $this->normalizeSort($this->projection, $this->sort);
        }

        $query->limit = $this->limit;
        $query->offset = $this->skip;
        if ($this->projection)
            foreach ($this->projection as $key => $value) {
                if (is_bool($value) && $value) {
                    $query->projection[] = $key;
                } elseif (is_string($value)) {
                    $query->projection[$value] = $key;
                }
            }

        $reader = $this->storage()->driver()->fetch($query);
        $reader = new \vivace\db\sql\Reader($reader, $this->storage(), $this->projection);

        return $reader;
    }

    /**
     * @param array $data
     *
     * @return int
     * @throws \Exception
     */
    public function update(array $data): int
    {
        $schema = $this->storage()->schema();
        $query = new Update($this->storage()->getSource(), $data);

        if ($this->projection) {
            $copyData = [];
            foreach ($query->set as $name => $value) {
                if (isset($this->projection[$name]) && is_string($this->projection[$name])) {
                    $name = $this->projection[$name];
                }
                $field = $schema->get($name);
                $copyData[$name] = $this->storage()->driver()->typecastIn($field, $value);
            }
            $query->set = $copyData;

            if ($this->filter) {
                $query->where = $this->normalizeFilter($this->projection, $this->filter);
            }
            if ($this->sort) {
                $query->order = $this->normalizeSort($this->projection, $this->sort);
            }
        }

        $query->limit = $this->limit;
        $query->offset = $this->skip;

        return (int)$this->storage()->driver()->execute($query)->getAffected();
    }

    /**
     * Delete found rows
     *
     * @return int Number deleted rows
     * @see \vivace\db\Storage::delete()
     * @throws \Exception
     */
    public function delete(): int
    {
        $query = new Delete($this->storage()->getSource());
        $query->limit = $this->limit;
        $query->offset = $this->skip;
        if ($this->filter) {
            $query->where = $this->normalizeFilter($this->projection, $this->filter);
        }
        if ($this->sort) {
            $query->order = $this->normalizeSort($this->projection, $this->sort);
        }

        return (int)$this->storage()->driver()->execute($query)->getAffected();
    }

    /**
     * Count found entities
     *
     * @return int
     * @see \vivace\db\Storage::count()
     * @throws \Exception
     */
    public function count(): int
    {
        $query = new Count($this->storage()->getSource());

        if ($this->filter) {
            $query->where = $this->normalizeFilter($this->projection, $this->filter);
        }
        if ($this->sort) {
            $query->order = $this->normalizeSort($this->projection, $this->sort);
        }

        $query->limit = $this->limit;
        $query->offset = $this->skip;

        return (int)$this->storage()->driver()->fetch($query)->scalar();
    }
}