diff --git a/.travis.yml b/.travis.yml index 88688609..54eb6bab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,46 @@ language: php +services: + - mongodb + cache: - directories: - - $HOME/.composer/cache + directories: + - $HOME/.composer/cache branches: except: - - /^analysis-.*$/ - - /^patch-.*$/ + - /^analysis-.*$/ + - /^patch-.*$/ php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - hhvm + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - hhvm env: - global: - - TEST_COMMAND="composer test" + global: + - TEST_COMMAND="composer test" matrix: - fast_finish: true - allow_failures: - - php: hhvm + fast_finish: true + allow_failures: + - php: hhvm + +before_install: + + - if [ ${TRAVIS_PHP_VERSION:0:2} == "5." ]; then yes '' | pecl -q install -f mongo; fi + - if [ ${TRAVIS_PHP_VERSION:0:2} == "7." ]; then pecl install -f mongodb; fi + - if [ ${TRAVIS_PHP_VERSION:0:2} == "7." ]; then composer require "alcaeus/mongo-php-adapter=^1.0.0" --ignore-platform-reqs; fi + + + - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "memory_limit=2G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi; + - if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi; install: - travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction script: - - $TEST_COMMAND + - $TEST_COMMAND diff --git a/composer.json b/composer.json index df10fc54..59cfed0a 100644 --- a/composer.json +++ b/composer.json @@ -13,14 +13,19 @@ { "name": "Kacper Gunia", "email": "kacper@gunia.me" + }, + { + "name": "Peter Gribanov", + "email": "info@peter-gribanov.ru" } ], "require": { - "php": ">=5.4", - "doctrine/orm": ">=2.2.3" + "php": ">=5.4" }, "require-dev": { - "phpspec/phpspec": "~2.1" + "phpspec/phpspec": "~2.1", + "doctrine/orm": "~2.2", + "doctrine/mongodb-odm": "~1.0.0" }, "autoload": { "psr-4": { diff --git a/src/BaseSpecification.php b/src/BaseSpecification.php deleted file mode 100644 index 26829763..00000000 --- a/src/BaseSpecification.php +++ /dev/null @@ -1,79 +0,0 @@ -dqlAlias = $dqlAlias; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string - */ - public function getFilter(QueryBuilder $qb, $dqlAlias) - { - $spec = $this->getSpec(); - if ($spec instanceof Filter) { - return $spec->getFilter($qb, $this->getAlias($dqlAlias)); - } - - return; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - $spec = $this->getSpec(); - if ($spec instanceof QueryModifier) { - $spec->modify($qb, $this->getAlias($dqlAlias)); - } - } - - /** - * Return all the specifications. - * - * @return Specification - */ - protected function getSpec() - { - return; - } - - /** - * @param string $dqlAlias - * - * @return string - */ - private function getAlias($dqlAlias) - { - if ($this->dqlAlias !== null) { - return $this->dqlAlias; - } - - return $dqlAlias; - } -} diff --git a/src/EntitySpecificationRepository.php b/src/EntitySpecificationRepository.php deleted file mode 100644 index e5a96815..00000000 --- a/src/EntitySpecificationRepository.php +++ /dev/null @@ -1,15 +0,0 @@ -getQuery($specification, $modifier); - - return $query->execute(); - } - - /** - * Get single result when you match with a Specification. - * - * @param Filter|QueryModifier $specification - * @param ResultModifier $modifier - * - * @throw Exception\NonUniqueException If more than one result is found - * @throw Exception\NoResultException If no results found - * - * @return mixed - */ - public function matchSingleResult($specification, ResultModifier $modifier = null) - { - $query = $this->getQuery($specification, $modifier); - - try { - return $query->getSingleResult(); - } catch (NonUniqueResultException $e) { - throw new Exception\NonUniqueResultException($e->getMessage(), $e->getCode(), $e); - } catch (NoResultException $e) { - throw new Exception\NoResultException($e->getMessage(), $e->getCode(), $e); - } - } - - /** - * Get single result or null when you match with a Specification. - * - * @param Filter|QueryModifier $specification - * @param ResultModifier $modifier - * - * @throw Exception\NonUniqueException If more than one result is found - * - * @return mixed|null - */ - public function matchOneOrNullResult($specification, ResultModifier $modifier = null) - { - try { - return $this->matchSingleResult($specification, $modifier); - } catch (Exception\NoResultException $e) { - return; - } - } - - /** - * Prepare a Query with a Specification. - * - * @param Filter|QueryModifier $specification - * @param ResultModifier $modifier - * - * @return \Doctrine\ORM\Query - */ - public function getQuery($specification, ResultModifier $modifier = null) - { - $qb = $this->createQueryBuilder($this->alias); - $this->applySpecification($qb, $specification); - $query = $qb->getQuery(); - - if ($modifier !== null) { - $modifier->modify($query); - } - - return $query; - } - - /** - * @param string $alias - * - * @return $this - */ - public function setAlias($alias) - { - $this->alias = $alias; - - return $this; - } - - /** - * @return string - */ - public function getAlias() - { - return $this->alias; - } - - /** - * @param QueryBuilder $queryBuilder - * @param Filter|QueryModifier $specification - * @param string $alias - * - * @throws \InvalidArgumentException - */ - protected function applySpecification(QueryBuilder $queryBuilder, $specification = null, $alias = null) - { - if (null === $specification) { - return; - } - - if (!$specification instanceof QueryModifier && !$specification instanceof Filter) { - throw new \InvalidArgumentException(sprintf( - 'Expected argument of type "%s" or "%s", "%s" given.', - 'Happyr\DoctrineSpecification\Query\QueryModifier', - 'Happyr\DoctrineSpecification\Filter\Filter', - is_object($specification) ? get_class($specification) : gettype($specification) - )); - } - - if ($specification instanceof QueryModifier) { - $specification->modify($queryBuilder, $alias ?: $this->getAlias()); - } - - if ($specification instanceof Filter - && $filter = (string) $specification->getFilter($queryBuilder, $alias ?: $this->getAlias()) - ) { - $queryBuilder->andWhere($filter); - } - } -} diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index cff92b04..0b941ba0 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -1,7 +1,66 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Exception; class InvalidArgumentException extends \InvalidArgumentException { + /** + * @param array $supported_formats + * @param string $format + * + * @return self + */ + public static function invalidLikeFormat(array $supported_formats, $format) + { + return new self(sprintf( + '"%s" is not a valid format. Valid format are: "%s"', + $format, + implode(', ', $supported_formats) + )); + } + + /** + * @param array $supported_types + * @param string $type + * + * @return self + */ + public static function invalidJoinConditionType(array $supported_types, $type) + { + return new self(sprintf( + '"%s" is not a valid condition type. Valid condition type are: "%s"', + $type, + implode(', ', $supported_types) + )); + } + + /** + * @return self + */ + public static function joinRequireConditionAndConditionType() + { + return new self('Join specification must have a condition and condition type.'); + } + + /** + * @param array $supported_types + * @param string $type + * + * @return self + */ + public static function invalidOrderType(array $supported_types, $type) + { + return new self(sprintf( + '"%s" is not a valid order. Valid order are: "%s"', + $type, + implode(', ', $supported_types) + )); + } } diff --git a/src/Exception/LogicException.php b/src/Exception/LogicException.php deleted file mode 100644 index cf622b0c..00000000 --- a/src/Exception/LogicException.php +++ /dev/null @@ -1,7 +0,0 @@ - + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Exception; diff --git a/src/Exception/NonUniqueResultException.php b/src/Exception/NonUniqueResultException.php index e7a533fe..9216c12b 100644 --- a/src/Exception/NonUniqueResultException.php +++ b/src/Exception/NonUniqueResultException.php @@ -1,4 +1,11 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Exception; diff --git a/src/Exception/UnexpectedResultException.php b/src/Exception/UnexpectedResultException.php index 5242a20f..1c5dd85c 100644 --- a/src/Exception/UnexpectedResultException.php +++ b/src/Exception/UnexpectedResultException.php @@ -1,4 +1,11 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Exception; diff --git a/src/Filter/Comparison.php b/src/Filter/Comparison.php index af73ba05..4a1e016b 100644 --- a/src/Filter/Comparison.php +++ b/src/Filter/Comparison.php @@ -1,114 +1,56 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; -use Doctrine\ORM\Query\Expr\Comparison as DoctrineComparison; -use Happyr\DoctrineSpecification\Exception\InvalidArgumentException; -use Happyr\DoctrineSpecification\ValueConverter; - /** * Comparison class. * * This is used when you need to compare two values */ -class Comparison implements Filter +abstract class Comparison implements Filter { - const EQ = '='; - const NEQ = '<>'; - const LT = '<'; - const LTE = '<='; - const GT = '>'; - const GTE = '>='; - const LIKE = 'LIKE'; - /** * @var string field */ - protected $field; + private $field; /** * @var string value */ - protected $value; - - /** - * @var string dqlAlias - */ - protected $dqlAlias; - - /** - * @var array - */ - private static $operators = array( - self::EQ, self::NEQ, - self::LT, self::LTE, - self::GT, self::GTE, - self::LIKE, - ); - - /** - * @var string - */ - private $operator; + private $value; /** * Make sure the $field has a value equals to $value. * - * @param string $operator * @param string $field * @param string $value - * @param string $dqlAlias - * - * @throws InvalidArgumentException */ - public function __construct($operator, $field, $value, $dqlAlias = null) + public function __construct($field, $value) { - if (!in_array($operator, self::$operators)) { - throw new InvalidArgumentException(sprintf( - '"%s" is not a valid comparison operator. Valid operators are: "%s"', - $operator, - implode(', ', self::$operators) - )); - } - - $this->operator = $operator; $this->field = $field; $this->value = $value; - $this->dqlAlias = $dqlAlias; } /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * * @return string */ - public function getFilter(QueryBuilder $qb, $dqlAlias) + public function getField() { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - - $paramName = $this->getParameterName($qb); - $qb->setParameter($paramName, ValueConverter::convertToDatabaseValue($this->value, $qb)); - - return (string) new DoctrineComparison( - sprintf('%s.%s', $dqlAlias, $this->field), - $this->operator, - sprintf(':%s', $paramName) - ); + return $this->field; } /** - * Get a good unique parameter name. - * - * @param QueryBuilder $qb - * * @return string */ - protected function getParameterName(QueryBuilder $qb) + public function getValue() { - return sprintf('comparison_%d', $qb->getParameters()->count()); + return $this->value; } } diff --git a/src/Filter/Equals.php b/src/Filter/Equals.php index cc475f42..bac084a3 100644 --- a/src/Filter/Equals.php +++ b/src/Filter/Equals.php @@ -1,16 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; class Equals extends Comparison { - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - */ - public function __construct($field, $value, $dqlAlias = null) - { - parent::__construct(self::EQ, $field, $value, $dqlAlias); - } } diff --git a/src/Filter/Filter.php b/src/Filter/Filter.php index b6310bb6..ef372746 100644 --- a/src/Filter/Filter.php +++ b/src/Filter/Filter.php @@ -1,16 +1,16 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Specification; -interface Filter +interface Filter extends Specification { - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string - */ - public function getFilter(QueryBuilder $qb, $dqlAlias); } diff --git a/src/Filter/GreaterOrEqualThan.php b/src/Filter/GreaterOrEqualThan.php index 0f86e4bb..8af097eb 100644 --- a/src/Filter/GreaterOrEqualThan.php +++ b/src/Filter/GreaterOrEqualThan.php @@ -1,16 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; class GreaterOrEqualThan extends Comparison { - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - */ - public function __construct($field, $value, $dqlAlias = null) - { - parent::__construct(self::GTE, $field, $value, $dqlAlias); - } } diff --git a/src/Filter/GreaterThan.php b/src/Filter/GreaterThan.php index e52d0910..2deeaa66 100644 --- a/src/Filter/GreaterThan.php +++ b/src/Filter/GreaterThan.php @@ -1,16 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; class GreaterThan extends Comparison { - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - */ - public function __construct($field, $value, $dqlAlias = null) - { - parent::__construct(self::GT, $field, $value, $dqlAlias); - } } diff --git a/src/Filter/In.php b/src/Filter/In.php index 6900b7c0..8834d0c6 100644 --- a/src/Filter/In.php +++ b/src/Filter/In.php @@ -1,80 +1,51 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; -use Happyr\DoctrineSpecification\ValueConverter; - class In implements Filter { /** - * @var string field - */ - protected $field; - - /** - * @var mixed value + * @var string */ - protected $value; + private $field; /** - * @var string dqlAlias + * @var mixed */ - protected $dqlAlias; + private $value; /** * Make sure the $field has a value equals to $value. * * @param string $field * @param mixed $value - * @param string $dqlAlias */ - public function __construct($field, $value, $dqlAlias = null) + public function __construct($field, $value) { $this->field = $field; $this->value = $value; - $this->dqlAlias = $dqlAlias; } /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * * @return string */ - public function getFilter(QueryBuilder $qb, $dqlAlias) + public function getField() { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - - $value = $this->value; - if (is_array($value)) { - foreach ($value as $k => $v) { - $value[$k] = ValueConverter::convertToDatabaseValue($v, $qb); - } - } else { - $value = ValueConverter::convertToDatabaseValue($value, $qb); - } - - $paramName = $this->getParameterName($qb); - $qb->setParameter($paramName, $value); - - return (string) $qb->expr()->in( - sprintf('%s.%s', $dqlAlias, $this->field), - sprintf(':%s', $paramName) - ); + return $this->field; } /** - * Get a good unique parameter name. - * - * @param QueryBuilder $qb - * - * @return string + * @return mixed */ - protected function getParameterName(QueryBuilder $qb) + public function getValue() { - return sprintf('in_%d', $qb->getParameters()->count()); + return $this->value; } } diff --git a/src/Filter/InstanceOfX.php b/src/Filter/InstanceOfX.php index a84001b6..7799ef12 100644 --- a/src/Filter/InstanceOfX.php +++ b/src/Filter/InstanceOfX.php @@ -1,43 +1,34 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; - class InstanceOfX implements Filter { /** - * @var null|string dqlAlias + * @var string */ - protected $dqlAlias; + private $value; /** - * @var string value + * @param string $value */ - protected $value; - - /** - * @param string $value - * @param string|null $dqlAlias - */ - public function __construct($value, $dqlAlias = null) + public function __construct($value) { - $this->dqlAlias = $dqlAlias; $this->value = $value; } /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * * @return string */ - public function getFilter(QueryBuilder $qb, $dqlAlias) + public function getValue() { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - - return sprintf('%s INSTANCE OF %s', $dqlAlias, $this->value); + return $this->value; } } diff --git a/src/Filter/IsNotNull.php b/src/Filter/IsNotNull.php index 9455bab3..89bdc5c0 100644 --- a/src/Filter/IsNotNull.php +++ b/src/Filter/IsNotNull.php @@ -1,23 +1,34 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; - -class IsNotNull extends IsNull +class IsNotNull implements Filter { /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string + * @var string + */ + protected $field; + + /** + * @param string $field */ - public function getFilter(QueryBuilder $qb, $dqlAlias) + public function __construct($field) { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } + $this->field = $field; + } - return (string) $qb->expr()->isNotNull(sprintf('%s.%s', $dqlAlias, $this->field)); + /** + * @return string + */ + public function getField() + { + return $this->field; } } diff --git a/src/Filter/IsNull.php b/src/Filter/IsNull.php index ac824582..1d28db1b 100644 --- a/src/Filter/IsNull.php +++ b/src/Filter/IsNull.php @@ -1,43 +1,34 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; - class IsNull implements Filter { /** - * @var string field + * @var string */ protected $field; /** - * @var null|string dqlAlias + * @param string $field */ - protected $dqlAlias; - - /** - * @param string $field - * @param string|null $dqlAlias - */ - public function __construct($field, $dqlAlias = null) + public function __construct($field) { $this->field = $field; - $this->dqlAlias = $dqlAlias; } /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * * @return string */ - public function getFilter(QueryBuilder $qb, $dqlAlias) + public function getField() { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - - return (string) $qb->expr()->isNull(sprintf('%s.%s', $dqlAlias, $this->field)); + return $this->field; } } diff --git a/src/Filter/LessOrEqualThan.php b/src/Filter/LessOrEqualThan.php index a8d2176e..c0364559 100644 --- a/src/Filter/LessOrEqualThan.php +++ b/src/Filter/LessOrEqualThan.php @@ -1,16 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; class LessOrEqualThan extends Comparison { - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - */ - public function __construct($field, $value, $dqlAlias = null) - { - parent::__construct(self::LTE, $field, $value, $dqlAlias); - } } diff --git a/src/Filter/LessThan.php b/src/Filter/LessThan.php index cd2b97f6..e38213ff 100644 --- a/src/Filter/LessThan.php +++ b/src/Filter/LessThan.php @@ -1,16 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; class LessThan extends Comparison { - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - */ - public function __construct($field, $value, $dqlAlias = null) - { - parent::__construct(self::LT, $field, $value, $dqlAlias); - } } diff --git a/src/Filter/Like.php b/src/Filter/Like.php index 4c94c4ee..73c4e4c7 100644 --- a/src/Filter/Like.php +++ b/src/Filter/Like.php @@ -1,33 +1,56 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; +use Happyr\DoctrineSpecification\Exception\InvalidArgumentException; + class Like extends Comparison { - const CONTAINS = '%%%s%%'; - const ENDS_WITH = '%%%s'; - const STARTS_WITH = '%s%%'; + const ENDS_WITH = 1; + const STARTS_WITH = 2; + const CONTAINS = 3; // self::ENDS_WITH | self::STARTS_WITH + + /** + * @var int + */ + private $format; + + /** + * @var array + */ + private static $formats = [ + self::ENDS_WITH, + self::STARTS_WITH, + self::CONTAINS, + ]; /** * @param string $field * @param string $value - * @param string $format - * @param string $dqlAlias + * @param int $format */ - public function __construct($field, $value, $format = self::CONTAINS, $dqlAlias = null) + public function __construct($field, $value, $format = self::CONTAINS) { - $formattedValue = $this->formatValue($format, $value); - parent::__construct(self::LIKE, $field, $formattedValue, $dqlAlias); + if (!in_array($format, self::$formats)) { + throw InvalidArgumentException::invalidLikeFormat(self::$formats, $format); + } + + $this->format = $format; + parent::__construct($field, $value); } /** - * @param string $format - * @param string $value - * - * @return string + * @return int */ - private function formatValue($format, $value) + public function getFormat() { - return sprintf($format, $value); + return $this->format; } } diff --git a/src/Filter/Logic/AndX.php b/src/Filter/Logic/AndX.php new file mode 100644 index 00000000..f5036539 --- /dev/null +++ b/src/Filter/Logic/AndX.php @@ -0,0 +1,59 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Filter\Logic; + +use Happyr\DoctrineSpecification\Filter\Filter; + +class AndX implements Filter +{ + /** + * @var Filter[] + */ + private $filters; + + /** + * Construct it with two or more instances of Specification. + * + * @param Filter $filter1 + * @param Filter $filter2 + */ + public function __construct(Filter $filter1, Filter $filter2) + { + foreach (func_get_args() as $filter) { + $this->andX($filter); + } + } + + /** + * Append an other specification with a logic AND. + * + * + * $spec = new AndX(A, B); + * $spec->andX(C); + * + * // We be the same as + * $spec = new AndX(A, B, C); + * + * + * @param Filter $filter + */ + public function andX(Filter $filter) + { + $this->filters[] = $filter; + } + + /** + * @return Filter[] + */ + public function getFilters() + { + return $this->filters; + } +} diff --git a/src/Filter/Logic/Not.php b/src/Filter/Logic/Not.php new file mode 100644 index 00000000..575e50b7 --- /dev/null +++ b/src/Filter/Logic/Not.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Filter\Logic; + +use Happyr\DoctrineSpecification\Filter\Filter; + +class Not implements Filter +{ + /** + * @var Filter + */ + private $filter; + + /** + * @param Filter $filter + */ + public function __construct(Filter $filter) + { + $this->filter = $filter; + } + + /** + * @return Filter + */ + public function getFilter() + { + return $this->filter; + } +} diff --git a/src/Filter/Logic/OrX.php b/src/Filter/Logic/OrX.php new file mode 100644 index 00000000..fea19f20 --- /dev/null +++ b/src/Filter/Logic/OrX.php @@ -0,0 +1,59 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Filter\Logic; + +use Happyr\DoctrineSpecification\Filter\Filter; + +class OrX implements Filter +{ + /** + * @var Filter[] + */ + private $filters; + + /** + * Construct it with two or more instances of Specification. + * + * @param Filter $filter1 + * @param Filter $filter2 + */ + public function __construct(Filter $filter1, Filter $filter2) + { + foreach (func_get_args() as $filter) { + $this->orX($filter); + } + } + + /** + * Append an other specification with a logic OR. + * + * + * $spec = new OrX(A, B); + * $spec->orX(C); + * + * // We be the same as + * $spec = new OrX(A, B, C); + * + * + * @param Filter $filter + */ + public function orX(Filter $filter) + { + $this->filters[] = $filter; + } + + /** + * @return Filter[] + */ + public function getFilters() + { + return $this->filters; + } +} diff --git a/src/Filter/NotEquals.php b/src/Filter/NotEquals.php index 1d210a7f..a54b19db 100644 --- a/src/Filter/NotEquals.php +++ b/src/Filter/NotEquals.php @@ -1,16 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace Happyr\DoctrineSpecification\Filter; class NotEquals extends Comparison { - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - */ - public function __construct($field, $value, $dqlAlias = null) - { - parent::__construct(self::NEQ, $field, $value, $dqlAlias); - } } diff --git a/src/Logic/AndX.php b/src/Logic/AndX.php deleted file mode 100644 index 3b6e558f..00000000 --- a/src/Logic/AndX.php +++ /dev/null @@ -1,29 +0,0 @@ - - * $spec = Spec::andX(A, B); - * $spec->andX(C); - * - * // We be the same as - * $spec = Spec::andX(A, B, C); - * - * - * @param |Happyr\DoctrineSpecification\Filter\Filter|\Happyr\DoctrineSpecification\Query\QueryModifier $child - */ - public function andX($child) - { - $this->append($child); - } -} diff --git a/src/Logic/LogicX.php b/src/Logic/LogicX.php deleted file mode 100644 index 17635de4..00000000 --- a/src/Logic/LogicX.php +++ /dev/null @@ -1,83 +0,0 @@ -expression = $expression; - $this->children = $children; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string - */ - public function getFilter(QueryBuilder $qb, $dqlAlias) - { - return call_user_func_array( - array($qb->expr(), $this->expression), - array_map( - function ($spec) use ($qb, $dqlAlias) { - if ($spec instanceof Filter) { - return $spec->getFilter($qb, $dqlAlias); - } - }, - $this->children - ) - ); - } - - /** - * @param QueryBuilder $query - * @param string $dqlAlias - */ - public function modify(QueryBuilder $query, $dqlAlias) - { - foreach ($this->children as $child) { - if ($child instanceof QueryModifier) { - $child->modify($query, $dqlAlias); - } - } - } - - /** - * Add another child to this logic tree. - * - * @param |Happyr\DoctrineSpecification\Filter\Filter|\Happyr\DoctrineSpecification\Query\QueryModifier $child - */ - protected function append($child) - { - $this->children[] = $child; - } -} diff --git a/src/Logic/Not.php b/src/Logic/Not.php deleted file mode 100644 index 1e7a9734..00000000 --- a/src/Logic/Not.php +++ /dev/null @@ -1,46 +0,0 @@ -child = $expr; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string - */ - public function getFilter(QueryBuilder $qb, $dqlAlias) - { - return (string) $qb->expr()->not($this->child->getFilter($qb, $dqlAlias)); - } - - /** - * @param QueryBuilder $query - * @param string $dqlAlias - */ - public function modify(QueryBuilder $query, $dqlAlias) - { - if ($this->child instanceof QueryModifier) { - $this->child->modify($query, $dqlAlias); - } - } -} diff --git a/src/Logic/OrX.php b/src/Logic/OrX.php deleted file mode 100644 index a5ae7ccd..00000000 --- a/src/Logic/OrX.php +++ /dev/null @@ -1,29 +0,0 @@ - - * $spec = Spec::orX(A, B); - * $spec->orX(C); - * - * // We be the same as - * $spec = Spec::orX(A, B, C); - * - * - * @param |Happyr\DoctrineSpecification\Filter\Filter|\Happyr\DoctrineSpecification\Query\QueryModifier $child - */ - public function orX($child) - { - $this->append($child); - } -} diff --git a/src/Query/AbstractJoin.php b/src/Query/AbstractJoin.php deleted file mode 100644 index 19b829e5..00000000 --- a/src/Query/AbstractJoin.php +++ /dev/null @@ -1,59 +0,0 @@ -field = $field; - $this->newAlias = $newAlias; - $this->dqlAlias = $dqlAlias; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - - $join = $this->getJoinType(); - $qb->$join(sprintf('%s.%s', $dqlAlias, $this->field), $this->newAlias); - } - - /** - * Return a join type (ie a function of QueryBuilder) like: "join", "innerJoin", "leftJoin". - * - * @return string - */ - abstract protected function getJoinType(); -} diff --git a/src/Query/GroupBy.php b/src/Query/GroupBy.php deleted file mode 100644 index 2dcc7c2d..00000000 --- a/src/Query/GroupBy.php +++ /dev/null @@ -1,40 +0,0 @@ -field = $field; - $this->dqlAlias = $dqlAlias; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - $qb->addGroupBy(sprintf('%s.%s', $dqlAlias, $this->field)); - } -} diff --git a/src/Query/InnerJoin.php b/src/Query/InnerJoin.php deleted file mode 100644 index 2915bdbc..00000000 --- a/src/Query/InnerJoin.php +++ /dev/null @@ -1,14 +0,0 @@ -limit = $limit; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - $qb->setMaxResults($this->limit); - } -} diff --git a/src/Query/Offset.php b/src/Query/Offset.php deleted file mode 100644 index 12b8913b..00000000 --- a/src/Query/Offset.php +++ /dev/null @@ -1,30 +0,0 @@ -offset = $offset; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - $qb->setFirstResult($this->offset); - } -} diff --git a/src/Query/OrderBy.php b/src/Query/OrderBy.php deleted file mode 100644 index 23766eef..00000000 --- a/src/Query/OrderBy.php +++ /dev/null @@ -1,48 +0,0 @@ -field = $field; - $this->order = $order; - $this->dqlAlias = $dqlAlias; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - if ($this->dqlAlias !== null) { - $dqlAlias = $this->dqlAlias; - } - - $qb->addOrderBy(sprintf('%s.%s', $dqlAlias, $this->field), $this->order); - } -} diff --git a/src/Query/QueryModifier.php b/src/Query/QueryModifier.php deleted file mode 100644 index 66e41d40..00000000 --- a/src/Query/QueryModifier.php +++ /dev/null @@ -1,14 +0,0 @@ -children = func_get_args(); - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - foreach ($this->children as $child) { - if (!$child instanceof QueryModifier) { - throw new InvalidArgumentException( - sprintf( - 'Child passed to QueryModifierCollection must be an instance of Happyr\DoctrineSpecification\Query\QueryModifier, but instance of %s found', - get_class($child) - ) - ); - } - - $child->modify($qb, $dqlAlias); - } - } -} diff --git a/src/Query/Slice.php b/src/Query/Slice.php deleted file mode 100644 index bd75aa07..00000000 --- a/src/Query/Slice.php +++ /dev/null @@ -1,41 +0,0 @@ -sliceSize = $sliceSize; - $this->sliceNumber = $sliceNumber; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - $qb->setMaxResults($this->sliceSize); - - if ($this->sliceNumber > 0) { - $qb->setFirstResult($this->sliceNumber * $this->sliceSize); - } - } -} diff --git a/src/QueryModifier/AbstractJoin.php b/src/QueryModifier/AbstractJoin.php new file mode 100644 index 00000000..99610808 --- /dev/null +++ b/src/QueryModifier/AbstractJoin.php @@ -0,0 +1,101 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +use Happyr\DoctrineSpecification\Exception\InvalidArgumentException; +use Happyr\DoctrineSpecification\Filter\Filter; + +abstract class AbstractJoin implements QueryModifier +{ + const ON = 'ON'; + const WITH = 'WITH'; + + /** + * @var string + */ + private $field; + + /** + * @var string + */ + private $alias; + + /** + * @var Filter|null + */ + private $condition; + + /** + * @var int|null + */ + private $conditionType; + + /** + * @var array + */ + private static $conditionTypes = [ + self::ON, + self::WITH, + ]; + + /** + * @param string $field + * @param string $alias + * @param int|null $conditionType + * @param Filter|null $condition + */ + public function __construct($field, $alias, $conditionType = null, Filter $condition = null) + { + if ($conditionType && !in_array($conditionType, self::$conditionTypes)) { + throw InvalidArgumentException::invalidJoinConditionType(self::$conditionTypes, $conditionType); + } + + if ((!$conditionType && $condition) || ($conditionType && !$condition)) { + throw InvalidArgumentException::joinRequireConditionAndConditionType(); + } + + $this->field = $field; + $this->alias = $alias; + $this->condition = $condition; + $this->conditionType = $conditionType; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * @return Filter|null + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return int|null + */ + public function getConditionType() + { + return $this->conditionType; + } +} diff --git a/src/QueryModifier/GroupBy.php b/src/QueryModifier/GroupBy.php new file mode 100644 index 00000000..6652db67 --- /dev/null +++ b/src/QueryModifier/GroupBy.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +class GroupBy implements QueryModifier +{ + /** + * @var string + */ + private $field; + + /** + * @param string $field + */ + public function __construct($field) + { + $this->field = $field; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } +} diff --git a/src/QueryModifier/Having.php b/src/QueryModifier/Having.php new file mode 100644 index 00000000..0142b22b --- /dev/null +++ b/src/QueryModifier/Having.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +use Happyr\DoctrineSpecification\Filter\Filter; + +class Having implements QueryModifier +{ + /** + * @var Filter child + */ + private $filter; + + /** + * @param Filter $filter + */ + public function __construct(Filter $filter) + { + $this->filter = $filter; + } + + /** + * @return Filter + */ + public function getFilter() + { + return $this->filter; + } +} diff --git a/src/QueryModifier/InnerJoin.php b/src/QueryModifier/InnerJoin.php new file mode 100644 index 00000000..10bd3bd7 --- /dev/null +++ b/src/QueryModifier/InnerJoin.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +class InnerJoin extends AbstractJoin +{ +} diff --git a/src/QueryModifier/Join.php b/src/QueryModifier/Join.php new file mode 100644 index 00000000..1428d664 --- /dev/null +++ b/src/QueryModifier/Join.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +class Join extends AbstractJoin +{ +} diff --git a/src/QueryModifier/LeftJoin.php b/src/QueryModifier/LeftJoin.php new file mode 100644 index 00000000..08435069 --- /dev/null +++ b/src/QueryModifier/LeftJoin.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +class LeftJoin extends AbstractJoin +{ +} diff --git a/src/QueryModifier/Map.php b/src/QueryModifier/Map.php new file mode 100644 index 00000000..83ddcf1e --- /dev/null +++ b/src/QueryModifier/Map.php @@ -0,0 +1,18 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +interface Map extends QueryModifier +{ + /** + * @return string + */ + public function getMap(); +} diff --git a/src/QueryModifier/QueryModifier.php b/src/QueryModifier/QueryModifier.php new file mode 100644 index 00000000..7d6ec179 --- /dev/null +++ b/src/QueryModifier/QueryModifier.php @@ -0,0 +1,16 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +use Happyr\DoctrineSpecification\Specification; + +interface QueryModifier extends Specification +{ +} diff --git a/src/QueryModifier/QueryModifierCollection.php b/src/QueryModifier/QueryModifierCollection.php new file mode 100644 index 00000000..2a7f3389 --- /dev/null +++ b/src/QueryModifier/QueryModifierCollection.php @@ -0,0 +1,47 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +class QueryModifierCollection implements QueryModifier +{ + /** + * @var QueryModifier[] + */ + private $modifiers; + + /** + * Construct it with two or more instances of QueryModifier. + * + * @param QueryModifier $modifier1 + * @param QueryModifier $modifier2 + */ + public function __construct(QueryModifier $modifier1, QueryModifier $modifier2) + { + foreach (func_get_args() as $modifier) { + $this->addModifier($modifier); + } + } + + /** + * @param QueryModifier $modifier + */ + public function addModifier(QueryModifier $modifier) + { + $this->modifiers[] = $modifier; + } + + /** + * @return QueryModifier[] + */ + public function getModifiers() + { + return $this->modifiers; + } +} diff --git a/src/QueryModifier/Reduce.php b/src/QueryModifier/Reduce.php new file mode 100644 index 00000000..5ff8aa9f --- /dev/null +++ b/src/QueryModifier/Reduce.php @@ -0,0 +1,18 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\QueryModifier; + +interface Reduce extends QueryModifier +{ + /** + * @return string + */ + public function getReduce(); +} diff --git a/src/Result/AsArray.php b/src/Result/AsArray.php deleted file mode 100644 index 60b3f466..00000000 --- a/src/Result/AsArray.php +++ /dev/null @@ -1,20 +0,0 @@ -setHydrationMode(Query::HYDRATE_ARRAY); - } -} diff --git a/src/Result/AsSingleScalar.php b/src/Result/AsSingleScalar.php deleted file mode 100644 index 6fd30190..00000000 --- a/src/Result/AsSingleScalar.php +++ /dev/null @@ -1,20 +0,0 @@ -setHydrationMode(Query::HYDRATE_SINGLE_SCALAR); - } -} diff --git a/src/Result/ResultModifier.php b/src/Result/ResultModifier.php deleted file mode 100644 index c07df6ef..00000000 --- a/src/Result/ResultModifier.php +++ /dev/null @@ -1,13 +0,0 @@ -children = func_get_args(); - } - - /** - * @param AbstractQuery $query - */ - public function modify(AbstractQuery $query) - { - foreach ($this->children as $child) { - if (!$child instanceof ResultModifier) { - throw new InvalidArgumentException( - sprintf( - 'Child passed to ResultModifierCollection must be an instance of Happyr\DoctrineSpecification\Result\ResultModifier, but instance of %s found', - get_class($child) - ) - ); - } - - $child->modify($query); - } - } -} diff --git a/src/Result/RoundDateTime.php b/src/Result/RoundDateTime.php deleted file mode 100644 index d9eae48c..00000000 --- a/src/Result/RoundDateTime.php +++ /dev/null @@ -1,42 +0,0 @@ -roundSeconds = $roundSeconds; - } - - /** - * @param AbstractQuery $query - */ - public function modify(AbstractQuery $query) - { - foreach ($query->getParameters() as $parameter) { - /* @var $parameter Parameter */ - if ($parameter->getValue() instanceof \DateTimeInterface) { - // round down so that the results do not include data that should not be there. - $date = clone $parameter->getValue(); - $date = $date->setTimestamp(floor($date->getTimestamp() / $this->roundSeconds) * $this->roundSeconds); - - $query->setParameter($parameter->getName(), $date, $parameter->getType()); - } - } - } -} diff --git a/src/ResultManagement/CountOf.php b/src/ResultManagement/CountOf.php new file mode 100644 index 00000000..abc64d09 --- /dev/null +++ b/src/ResultManagement/CountOf.php @@ -0,0 +1,16 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultManagement; + +use Happyr\DoctrineSpecification\Specification; + +class CountOf implements Specification +{ +} diff --git a/src/ResultManagement/Limit.php b/src/ResultManagement/Limit.php new file mode 100644 index 00000000..bdf98bf7 --- /dev/null +++ b/src/ResultManagement/Limit.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultManagement; + +use Happyr\DoctrineSpecification\Specification; + +class Limit implements Specification +{ + /** + * @var int + */ + private $limit; + + /** + * @param int $limit + */ + public function __construct($limit) + { + $this->limit = $limit; + } + + /** + * @return int + */ + public function getLimit() + { + return $this->limit; + } +} diff --git a/src/ResultManagement/Offset.php b/src/ResultManagement/Offset.php new file mode 100644 index 00000000..23a7f8f4 --- /dev/null +++ b/src/ResultManagement/Offset.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultManagement; + +use Happyr\DoctrineSpecification\Specification; + +class Offset implements Specification +{ + /** + * @var int + */ + private $offset; + + /** + * @param int $offset + */ + public function __construct($offset) + { + $this->offset = $offset; + } + + /** + * @return int + */ + public function getOffset() + { + return $this->offset; + } +} diff --git a/src/ResultManagement/OrderBy.php b/src/ResultManagement/OrderBy.php new file mode 100644 index 00000000..5d1b3411 --- /dev/null +++ b/src/ResultManagement/OrderBy.php @@ -0,0 +1,67 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultManagement; + +use Happyr\DoctrineSpecification\Exception\InvalidArgumentException; +use Happyr\DoctrineSpecification\Specification; + +class OrderBy implements Specification +{ + const ASC = 'ASC'; + const DESC = 'DESC'; + + /** + * @var string + */ + private $field; + + /** + * @var array + */ + private static $orders = [ + self::ASC, + self::DESC, + ]; + + /** + * @var string + */ + private $order; + + /** + * @param string $field + * @param string $order + */ + public function __construct($field, $order = self::ASC) + { + if (!in_array($order, self::$orders)) { + throw InvalidArgumentException::invalidOrderType(self::$orders, $order); + } + + $this->field = $field; + $this->order = $order; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * @return string + */ + public function getOrder() + { + return $this->order; + } +} diff --git a/src/ResultManagement/Slice.php b/src/ResultManagement/Slice.php new file mode 100644 index 00000000..949ab2fb --- /dev/null +++ b/src/ResultManagement/Slice.php @@ -0,0 +1,51 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultManagement; + +use Happyr\DoctrineSpecification\Specification; + +class Slice implements Specification +{ + /** + * @var int + */ + private $sliceSize; + + /** + * @var int + */ + private $sliceNumber = 0; + + /** + * @param int $sliceSize + * @param int $sliceNumber + */ + public function __construct($sliceSize, $sliceNumber = 0) + { + $this->sliceSize = $sliceSize; + $this->sliceNumber = $sliceNumber; + } + + /** + * @return int + */ + public function getSliceSize() + { + return $this->sliceSize; + } + + /** + * @return int + */ + public function getSliceNumber() + { + return $this->sliceNumber; + } +} diff --git a/src/ResultModifier/AsArray.php b/src/ResultModifier/AsArray.php new file mode 100644 index 00000000..74af47f7 --- /dev/null +++ b/src/ResultModifier/AsArray.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultModifier; + +class AsArray implements ResultModifier +{ +} diff --git a/src/ResultModifier/AsSingleScalar.php b/src/ResultModifier/AsSingleScalar.php new file mode 100644 index 00000000..a6e47cee --- /dev/null +++ b/src/ResultModifier/AsSingleScalar.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultModifier; + +class AsSingleScalar implements ResultModifier +{ +} diff --git a/src/Result/Cache.php b/src/ResultModifier/Cache.php similarity index 53% rename from src/Result/Cache.php rename to src/ResultModifier/Cache.php index a4b39475..be66d38f 100644 --- a/src/Result/Cache.php +++ b/src/ResultModifier/Cache.php @@ -1,8 +1,13 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ -namespace Happyr\DoctrineSpecification\Result; - -use Doctrine\ORM\AbstractQuery; +namespace Happyr\DoctrineSpecification\ResultModifier; class Cache implements ResultModifier { @@ -20,10 +25,10 @@ public function __construct($cacheLifetime) } /** - * @param AbstractQuery $query + * @return int */ - public function modify(AbstractQuery $query) + public function getCacheLifetime() { - $query->setResultCacheLifetime($this->cacheLifetime); + return $this->cacheLifetime; } } diff --git a/src/ResultModifier/ResultModifier.php b/src/ResultModifier/ResultModifier.php new file mode 100644 index 00000000..c52d7147 --- /dev/null +++ b/src/ResultModifier/ResultModifier.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultModifier; + +interface ResultModifier +{ +} diff --git a/src/ResultModifier/ResultModifierCollection.php b/src/ResultModifier/ResultModifierCollection.php new file mode 100644 index 00000000..3028aaaa --- /dev/null +++ b/src/ResultModifier/ResultModifierCollection.php @@ -0,0 +1,47 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultModifier; + +class ResultModifierCollection implements ResultModifier +{ + /** + * @var ResultModifier[] + */ + private $modifiers = []; + + /** + * Construct it with two or more instances of ResultModifier. + * + * @param ResultModifier $modifier1 + * @param ResultModifier $modifier2 + */ + public function __construct(ResultModifier $modifier1, ResultModifier $modifier2) + { + foreach (func_get_args() as $modifier) { + $this->addModifier($modifier); + } + } + + /** + * @param ResultModifier $modifier + */ + public function addModifier(ResultModifier $modifier) + { + $this->modifiers[] = $modifier; + } + + /** + * @return ResultModifier[] + */ + public function getModifiers() + { + return $this->modifiers; + } +} diff --git a/src/ResultModifier/RoundDateTime.php b/src/ResultModifier/RoundDateTime.php new file mode 100644 index 00000000..ed662adf --- /dev/null +++ b/src/ResultModifier/RoundDateTime.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\ResultModifier; + +/** + * Round a \DateTime to enable caching. + */ +class RoundDateTime implements ResultModifier +{ + /** + * @var int How may seconds to round time + */ + private $roundSeconds; + + /** + * @param int $roundSeconds How may seconds to round time + */ + public function __construct($roundSeconds) + { + $this->roundSeconds = $roundSeconds; + } + + /** + * @return int + */ + public function getRoundSeconds() + { + return $this->roundSeconds; + } +} diff --git a/src/Spec.php b/src/Spec.php deleted file mode 100644 index d430da9b..00000000 --- a/src/Spec.php +++ /dev/null @@ -1,377 +0,0 @@ -newInstanceArgs($args); - } - - /** - * @return OrX - */ - public static function orX() - { - $args = func_get_args(); - $reflection = new \ReflectionClass('Happyr\DoctrineSpecification\Logic\OrX'); - - return $reflection->newInstanceArgs($args); - } - - /** - * @param Filter $spec - * - * @return Not - */ - public static function not(Filter $spec) - { - return new Not($spec); - } - - /* - * Query modifier - */ - - /** - * @param string $field - * @param string $newAlias - * @param string $dqlAlias - * - * @return Join - */ - public static function join($field, $newAlias, $dqlAlias = null) - { - return new Join($field, $newAlias, $dqlAlias); - } - - /** - * @param string $field - * @param string $newAlias - * @param string $dqlAlias - * - * @return LeftJoin - */ - public static function leftJoin($field, $newAlias, $dqlAlias = null) - { - return new LeftJoin($field, $newAlias, $dqlAlias); - } - - /** - * @param string $field - * @param string $newAlias - * @param string $dqlAlias - * - * @return InnerJoin - */ - public static function innerJoin($field, $newAlias, $dqlAlias = null) - { - return new InnerJoin($field, $newAlias, $dqlAlias); - } - - /** - * @param int $count - * - * @return Limit - */ - public static function limit($count) - { - return new Limit($count); - } - - /** - * @param int $count - * - * @return Offset - */ - public static function offset($count) - { - return new Offset($count); - } - - /** - * @param int $sliceSize - * @param int $sliceNumber - * - * @return Slice - */ - public static function slice($sliceSize, $sliceNumber = 0) - { - return new Slice($sliceSize, $sliceNumber); - } - - /** - * @param string $field - * @param string $order - * @param string|null $dqlAlias - * - * @return OrderBy - */ - public static function orderBy($field, $order = 'ASC', $dqlAlias = null) - { - return new OrderBy($field, $order, $dqlAlias); - } - - /** - * @param string $field - * @param string $dqlAlias - * - * @return GroupBy - */ - public static function groupBy($field, $dqlAlias = null) - { - return new GroupBy($field, $dqlAlias); - } - - /* - * Result modifier - */ - - /** - * @return AsArray - */ - public static function asArray() - { - return new AsArray(); - } - - /** - * @return AsSingleScalar - */ - public static function asSingleScalar() - { - return new AsSingleScalar(); - } - - /** - * @param int $cacheLifetime How many seconds the cached entry is valid - * - * @return Cache - */ - public static function cache($cacheLifetime) - { - return new Cache($cacheLifetime); - } - - /** - * @param int $roundSeconds How may seconds to round time - * - * @return RoundDateTime - */ - public static function roundDateTimeParams($roundSeconds) - { - return new RoundDateTime($roundSeconds); - } - - /* - * Filters - */ - - /** - * @param string $field - * @param string|null $dqlAlias - * - * @return IsNull - */ - public static function isNull($field, $dqlAlias = null) - { - return new IsNull($field, $dqlAlias); - } - - /** - * @param string $field - * @param string|null $dqlAlias - * - * @return IsNotNull - */ - public static function isNotNull($field, $dqlAlias = null) - { - return new IsNotNull($field, $dqlAlias); - } - - /** - * Make sure the $field has a value equals to $value. - * - * @param string $field - * @param mixed $value - * @param string $dqlAlias - * - * @return In - */ - public static function in($field, $value, $dqlAlias = null) - { - return new In($field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param mixed $value - * @param string $dqlAlias - * - * @return Not - */ - public static function notIn($field, $value, $dqlAlias = null) - { - return new Not(new In($field, $value, $dqlAlias)); - } - - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - * - * @return Comparison - */ - public static function eq($field, $value, $dqlAlias = null) - { - return new Comparison(Comparison::EQ, $field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - * - * @return Comparison - */ - public static function neq($field, $value, $dqlAlias = null) - { - return new Comparison(Comparison::NEQ, $field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - * - * @return Comparison - */ - public static function lt($field, $value, $dqlAlias = null) - { - return new Comparison(Comparison::LT, $field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - * - * @return Comparison - */ - public static function lte($field, $value, $dqlAlias = null) - { - return new Comparison(Comparison::LTE, $field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - * - * @return Comparison - */ - public static function gt($field, $value, $dqlAlias = null) - { - return new Comparison(Comparison::GT, $field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param string $value - * @param string $dqlAlias - * - * @return Comparison - */ - public static function gte($field, $value, $dqlAlias = null) - { - return new Comparison(Comparison::GTE, $field, $value, $dqlAlias); - } - - /** - * @param string $field - * @param string $value - * @param string $format - * @param string $dqlAlias - * - * @return Like - */ - public static function like($field, $value, $format = Like::CONTAINS, $dqlAlias = null) - { - return new Like($field, $value, $format, $dqlAlias); - } - - /** - * @param string $value - * @param null $dqlAlias - * - * @return InstanceOfX - */ - public static function instanceOfX($value, $dqlAlias = null) - { - return new InstanceOfX($value, $dqlAlias); - } - - /* - * Specifications - */ - - /** - * @param Specification $spec - * - * @return CountOf - */ - public static function countOf(Specification $spec) - { - return new CountOf($spec); - } - - /** - * @param Filter|string $spec - * - * @return Having - */ - public static function having($spec) - { - return new Having($spec); - } -} diff --git a/src/Specification.php b/src/Specification.php new file mode 100644 index 00000000..7edeecee --- /dev/null +++ b/src/Specification.php @@ -0,0 +1,14 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification; + +interface Specification +{ +} diff --git a/src/Specification/CountOf.php b/src/Specification/CountOf.php deleted file mode 100644 index 1260d7a0..00000000 --- a/src/Specification/CountOf.php +++ /dev/null @@ -1,46 +0,0 @@ -child = $child; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string - */ - public function getFilter(QueryBuilder $qb, $dqlAlias) - { - $qb->select(sprintf('COUNT(%s)', $dqlAlias)); - - return $this->child->getFilter($qb, $dqlAlias); - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - $this->child->modify($qb, $dqlAlias); - } -} diff --git a/src/Specification/Having.php b/src/Specification/Having.php deleted file mode 100644 index 53853e79..00000000 --- a/src/Specification/Having.php +++ /dev/null @@ -1,49 +0,0 @@ -child = $child; - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - */ - public function modify(QueryBuilder $qb, $dqlAlias) - { - if ($this->child instanceof QueryModifier) { - $this->child->modify($qb, $dqlAlias); - } - } - - /** - * @param QueryBuilder $qb - * @param string $dqlAlias - * - * @return string - */ - public function getFilter(QueryBuilder $qb, $dqlAlias) - { - if ($this->child instanceof Filter) { - $qb->having($this->child->getFilter($qb, $dqlAlias)); - } else { - $qb->having($this->child); - } - } -} diff --git a/src/Specification/Specification.php b/src/Specification/Specification.php deleted file mode 100644 index b051c2e2..00000000 --- a/src/Specification/Specification.php +++ /dev/null @@ -1,10 +0,0 @@ - + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification; + +class SpecificationCollection implements Specification +{ + /** + * @var Specification[] + */ + private $specifications; + + /** + * Construct it with two or more instances of Specification. + * + * @param Specification $specification1 + * @param Specification $specification2 + */ + public function __construct(Specification $specification1, Specification $specification2) + { + foreach (func_get_args() as $specification) { + $this->addSpecification($specification); + } + } + + /** + * @param Specification $specification + */ + public function addSpecification(Specification $specification) + { + $this->specifications[] = $specification; + } + + /** + * @return Specification[] + */ + public function getSpecifications() + { + return $this->specifications; + } +} diff --git a/src/DBALTypesResolver.php b/src/Transformer/Doctrine/DBALTypesResolver.php similarity index 84% rename from src/DBALTypesResolver.php rename to src/Transformer/Doctrine/DBALTypesResolver.php index 3eb18503..c3685645 100644 --- a/src/DBALTypesResolver.php +++ b/src/Transformer/Doctrine/DBALTypesResolver.php @@ -1,6 +1,13 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ -namespace Happyr\DoctrineSpecification; +namespace Happyr\DoctrineSpecification\Transformer\Doctrine; use Doctrine\DBAL\Types\Type; diff --git a/src/Transformer/Doctrine/ODM/MongoDB/DoctrineODMMongoDBTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/DoctrineODMMongoDBTransformer.php new file mode 100644 index 00000000..414f2e0f --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/DoctrineODMMongoDBTransformer.php @@ -0,0 +1,44 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB; + +use Doctrine\MongoDB\Query\Query; +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformerCollection; + +class DoctrineODMMongoDBTransformer +{ + /** + * @var QueryBuilderTransformerCollection + */ + private $transformer; + + /** + * @param QueryBuilderTransformerCollection $transformer + */ + public function __construct(QueryBuilderTransformerCollection $transformer) + { + $this->transformer = $transformer; + } + + /** + * @param Specification $specification + * @param Builder $qb + * + * @return Query + */ + public function transform(Specification $specification, Builder $qb) + { + $this->transformer->transform($specification, $qb); + + return $qb->getQuery(); + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepository.php b/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepository.php new file mode 100644 index 00000000..3a28cbd1 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepository.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\DocumentRepository; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Persistence\ObjectRepository; + +/** + * This class allows you to use a Specification to query entities. + */ +class EntitySpecificationRepository extends DocumentRepository implements EntitySpecificationRepositoryInterface, ObjectRepository, Selectable +{ + use EntitySpecificationRepositoryTrait; + + /** + * @param DocumentManager $dm the DocumentManager to use + * @param UnitOfWork $uow the UnitOfWork to use + * @param ClassMetadata $class the class descriptor + * @param DoctrineODMMongoDBTransformer $transformer The Specification transformer + */ + public function __construct( + DocumentManager $dm, + UnitOfWork $uow, + ClassMetadata $class, + DoctrineODMMongoDBTransformer $transformer + ) { + parent::__construct($dm, $uow, $class); + $this->setTransformer($transformer); + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepositoryInterface.php b/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepositoryInterface.php new file mode 100644 index 00000000..ae001122 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepositoryInterface.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Specification; + +/** + * This interface should be used by an DocumentRepository implementing the Specification pattern. + */ +interface EntitySpecificationRepositoryInterface +{ + /** + * Get results when you match with a Specification. + * + * @param Specification $specification + * + * @return mixed[] + */ + public function match(Specification $specification); + + /** + * Get single result when you match with a Specification. + * + * @param Specification $specification + * + * @return mixed + */ + public function matchSingleResult(Specification $specification); + + /** + * Prepare a Query with a Specification. + * + * @param Specification $specification + * + * @return Builder + */ + public function getQuery(Specification $specification); + + /** + * @param DoctrineODMMongoDBTransformer $transformer + * + * @return self + */ + public function setTransformer(DoctrineODMMongoDBTransformer $transformer); +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepositoryTrait.php b/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepositoryTrait.php new file mode 100644 index 00000000..5c3a4edd --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/EntitySpecificationRepositoryTrait.php @@ -0,0 +1,81 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Doctrine\ODM\MongoDB\Query\Query; +use Happyr\DoctrineSpecification\Specification; + +/** + * This trait should be used by a class extending \Doctrine\ODM\MongoDB\DocumentRepository. + */ +trait EntitySpecificationRepositoryTrait +{ + /** + * @var DoctrineODMMongoDBTransformer + */ + private $transformer; + + /** + * Get results when you match with a Specification. + * + * @param Specification $specification + * + * @return mixed[] + */ + public function match(Specification $specification) + { + return $this->getQuery($specification)->execute(); + } + + /** + * Get single result when you match with a Specification. + * + * @param Specification $specification + * + * @return mixed + */ + public function matchSingleResult(Specification $specification) + { + return $this->getQuery($specification)->getSingleResult(); + } + + /** + * Prepare a Query with a Specification. + * + * @param Specification $specification + * + * @return Query + */ + public function getQuery(Specification $specification) + { + /* @var $qb Builder */ + $qb = $this->createQueryBuilder(); + + // apply specification + if ($this->transformer instanceof DoctrineODMMongoDBTransformer) { + return $this->transformer->transform($specification, $qb); + } else { + return $qb->getQuery(); + } + } + + /** + * @param DoctrineODMMongoDBTransformer $transformer + * + * @return self + */ + public function setTransformer(DoctrineODMMongoDBTransformer $transformer) + { + $this->transformer = $transformer; + + return $this; + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/EqualsTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/EqualsTransformer.php new file mode 100644 index 00000000..eb914a18 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/EqualsTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\Equals; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class EqualsTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof Equals) { + $qb->field($specification->getField())->equals($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/GreaterOrEqualThanTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/GreaterOrEqualThanTransformer.php new file mode 100644 index 00000000..a060655b --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/GreaterOrEqualThanTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\GreaterOrEqualThan; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class GreaterOrEqualThanTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof GreaterOrEqualThan) { + $qb->field($specification->getField())->gte($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/GreaterThanTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/GreaterThanTransformer.php new file mode 100644 index 00000000..116d7570 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/GreaterThanTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\GreaterThan; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class GreaterThanTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof GreaterThan) { + $qb->field($specification->getField())->gt($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/InTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/InTransformer.php new file mode 100644 index 00000000..f0eefd3e --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/InTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\In; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class InTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof In) { + $qb->field($specification->getField())->equals($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/LessOrEqualThanTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/LessOrEqualThanTransformer.php new file mode 100644 index 00000000..6fda4b89 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/LessOrEqualThanTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\LessOrEqualThan; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class LessOrEqualThanTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof LessOrEqualThan) { + $qb->field($specification->getField())->lte($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/LessThanTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/LessThanTransformer.php new file mode 100644 index 00000000..5e713719 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/LessThanTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\LessThan; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class LessThanTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof LessThan) { + $qb->field($specification->getField())->lt($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/Logic/AndXTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/Logic/AndXTransformer.php new file mode 100644 index 00000000..6cb4efe1 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/Logic/AndXTransformer.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter\Logic; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\Logic\AndX; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +class AndXTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof AndX) { + foreach ($specification->getFilters() as $filter) { + $this->collection->transform($filter, $qb); + } + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/NotEqualsTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/NotEqualsTransformer.php new file mode 100644 index 00000000..a0c85864 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/Filter/NotEqualsTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\Filter; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Filter\NotEquals; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class NotEqualsTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof NotEquals) { + $qb->field($specification->getField())->notEqual($specification->getValue()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformer.php new file mode 100644 index 00000000..f5d08877 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformer.php @@ -0,0 +1,22 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Specification; + +interface QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb); +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollection.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollection.php new file mode 100644 index 00000000..3c540293 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollection.php @@ -0,0 +1,54 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Specification; + +class QueryBuilderTransformerCollection implements QueryBuilderTransformer +{ + /** + * @var QueryBuilderTransformer[] + */ + private $transformers = []; + + /** + * @param QueryBuilderTransformer[] $transformers + */ + public function __construct(array $transformers = []) + { + foreach ($transformers as $transformer) { + $this->addTransformer($transformer); + } + } + + /** + * @param QueryBuilderTransformer $transformer + */ + public function addTransformer(QueryBuilderTransformer $transformer) + { + $this->transformers[] = $transformer; + + if ($transformer instanceof QueryBuilderTransformerCollectionAware) { + $transformer->setCollection($this); + } + } + + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + foreach ($this->transformers as $transformer) { + $transformer->transform($specification, $qb); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollectionAware.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollectionAware.php new file mode 100644 index 00000000..d5227f35 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollectionAware.php @@ -0,0 +1,18 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder; + +interface QueryBuilderTransformerCollectionAware extends QueryBuilderTransformer +{ + /** + * @param QueryBuilderTransformerCollection $collection + */ + public function setCollection(QueryBuilderTransformerCollection $collection); +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollectionAwareTrait.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollectionAwareTrait.php new file mode 100644 index 00000000..22e17e60 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryBuilderTransformerCollectionAwareTrait.php @@ -0,0 +1,26 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder; + +trait QueryBuilderTransformerCollectionAwareTrait +{ + /** + * @var QueryBuilderTransformerCollection + */ + protected $collection; + + /** + * @param QueryBuilderTransformerCollection $collection + */ + public function setCollection(QueryBuilderTransformerCollection $collection) + { + $this->collection = $collection; + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryModifier/MapTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryModifier/MapTransformer.php new file mode 100644 index 00000000..2031d732 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryModifier/MapTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryModifier; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\QueryModifier\Map; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class MapTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof Map) { + $qb->map($specification->getMap()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryModifier/ReduceTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryModifier/ReduceTransformer.php new file mode 100644 index 00000000..0e462e11 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/QueryModifier/ReduceTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryModifier; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\QueryModifier\Reduce; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class ReduceTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof Reduce) { + $qb->reduce($specification->getReduce()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/CountOfTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/CountOfTransformer.php new file mode 100644 index 00000000..58b50dfa --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/CountOfTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\ResultManagement; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\ResultManagement\CountOf; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class CountOfTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof CountOf) { + $qb->count(); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/LimitTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/LimitTransformer.php new file mode 100644 index 00000000..b27b131d --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/LimitTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\ResultManagement; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\ResultManagement\Limit; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class LimitTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof Limit) { + $qb->limit($specification->getLimit()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/OffsetTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/OffsetTransformer.php new file mode 100644 index 00000000..de703a6a --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/OffsetTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\ResultManagement; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\ResultManagement\Offset; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class OffsetTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof Offset) { + $qb->skip($specification->getOffset()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/OrderByTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/OrderByTransformer.php new file mode 100644 index 00000000..1d1d565f --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/OrderByTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\ResultManagement; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\ResultManagement\OrderBy; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class OrderByTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof OrderBy) { + $qb->sort($specification->getField(), $specification->getOrder()); + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/SliceTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/SliceTransformer.php new file mode 100644 index 00000000..8213c43a --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/ResultManagement/SliceTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\ResultManagement; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\ResultManagement\Slice; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder\QueryBuilderTransformer; + +class SliceTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof Slice) { + $qb->limit($specification->getSliceSize()); + + if ($specification->getSliceNumber() > 0) { + $qb->skip($specification->getSliceNumber() * $specification->getSliceSize()); + } + } + } +} diff --git a/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/SpecificationCollectionTransformer.php b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/SpecificationCollectionTransformer.php new file mode 100644 index 00000000..5de3f2d0 --- /dev/null +++ b/src/Transformer/Doctrine/ODM/MongoDB/QueryBuilder/SpecificationCollectionTransformer.php @@ -0,0 +1,32 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ODM\MongoDB\QueryBuilder; + +use Doctrine\ODM\MongoDB\Query\Builder; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\SpecificationCollection; + +class SpecificationCollectionTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param Builder $qb + */ + public function transform(Specification $specification, Builder $qb) + { + if ($specification instanceof SpecificationCollection) { + foreach ($specification->getSpecifications() as $specification) { + $this->collection->transform($specification, $qb); + } + } + } +} diff --git a/src/Transformer/Doctrine/ORM/DoctrineORMTransformer.php b/src/Transformer/Doctrine/ORM/DoctrineORMTransformer.php new file mode 100644 index 00000000..cfa4798e --- /dev/null +++ b/src/Transformer/Doctrine/ORM/DoctrineORMTransformer.php @@ -0,0 +1,71 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM; + +use Doctrine\ORM\AbstractQuery; +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; + +class DoctrineORMTransformer +{ + /** + * @var QueryTransformerCollection + */ + private $qTransformer; + + /** + * @var QueryBuilderTransformerCollection + */ + private $qbTransformer; + + /** + * @param QueryTransformerCollection $qTransformer + * @param QueryBuilderTransformerCollection $qbTransformer + */ + public function __construct( + QueryTransformerCollection $qTransformer, + QueryBuilderTransformerCollection $qbTransformer + ) { + $this->qTransformer = $qTransformer; + $this->qbTransformer = $qbTransformer; + } + + /** + * @param Specification $specification + * @param ResultModifier|null $modifier + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return AbstractQuery + */ + public function transform( + Specification $specification, + ResultModifier $modifier = null, + QueryBuilder $qb, + $dqlAlias + ) { + $condition = $this->qbTransformer->transform($specification, $qb, $dqlAlias); + + if ($condition) { + $qb->andWhere($condition); + } + + $query = $qb->getQuery(); + + if ($modifier !== null) { + $this->qTransformer->transform($modifier, $query); + } + + return $query; + } +} diff --git a/src/Transformer/Doctrine/ORM/EntitySpecificationRepository.php b/src/Transformer/Doctrine/ORM/EntitySpecificationRepository.php new file mode 100644 index 00000000..e5867ee7 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/EntitySpecificationRepository.php @@ -0,0 +1,35 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityRepository; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Persistence\ObjectRepository; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * This class allows you to use a Specification to query entities. + */ +class EntitySpecificationRepository extends EntityRepository implements EntitySpecificationRepositoryInterface, ObjectRepository, Selectable +{ + use EntitySpecificationRepositoryTrait; + + /** + * @param EntityManager $em The EntityManager to use + * @param ClassMetadata $class The class descriptor + * @param DoctrineORMTransformer $transformer The Specification transformer + */ + public function __construct($em, ClassMetadata $class, DoctrineORMTransformer $transformer) + { + parent::__construct($em, $class); + $this->setTransformer($transformer); + } +} diff --git a/src/Transformer/Doctrine/ORM/EntitySpecificationRepositoryInterface.php b/src/Transformer/Doctrine/ORM/EntitySpecificationRepositoryInterface.php new file mode 100644 index 00000000..5a1f8406 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/EntitySpecificationRepositoryInterface.php @@ -0,0 +1,79 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM; + +use Doctrine\ORM\Query; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Specification; + +/** + * This interface should be used by an EntityRepository implementing the Specification pattern. + */ +interface EntitySpecificationRepositoryInterface +{ + /** + * Get results when you match with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @return mixed[] + */ + public function match(Specification $specification, ResultModifier $modifier); + + /** + * Get single result when you match with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @throw NonUniqueException If more than one result is found + * @throw NoResultException If no results found + * + * @return mixed + */ + public function matchSingleResult(Specification $specification, ResultModifier $modifier); + + /** + * Get single result or null when you match with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @throw NonUniqueException If more than one result is found + * + * @return mixed|null + */ + public function matchOneOrNullResult(Specification $specification, ResultModifier $modifier); + + /** + * Prepare a Query with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @return Query + */ + public function getQuery(Specification $specification, ResultModifier $modifier); + + /** + * @param string $alias + * + * @return self + */ + public function setAlias($alias); + + /** + * @param DoctrineORMTransformer $transformer + * + * @return self + */ + public function setTransformer(DoctrineORMTransformer $transformer); +} diff --git a/src/Transformer/Doctrine/ORM/EntitySpecificationRepositoryTrait.php b/src/Transformer/Doctrine/ORM/EntitySpecificationRepositoryTrait.php new file mode 100644 index 00000000..d05a42cf --- /dev/null +++ b/src/Transformer/Doctrine/ORM/EntitySpecificationRepositoryTrait.php @@ -0,0 +1,134 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM; + +use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; +use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Exception\NonUniqueResultException as HappyrNonUniqueResultException; +use Happyr\DoctrineSpecification\Exception\NoResultException as HappyrNoResultException; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Specification; + +/** + * This trait should be used by a class extending \Doctrine\ORM\EntityRepository. + */ +trait EntitySpecificationRepositoryTrait +{ + /** + * @var string alias + */ + private $alias = 'e'; + + /** + * @var DoctrineORMTransformer + */ + private $transformer; + + /** + * Get results when you match with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @return mixed[] + */ + public function match(Specification $specification, ResultModifier $modifier = null) + { + return $this->getQuery($specification, $modifier)->execute(); + } + + /** + * Get single result when you match with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @throw HappyrNonUniqueResultException If more than one result is found + * @throw HappyrNoResultException If no results found + * + * @return mixed + */ + public function matchSingleResult(Specification $specification, ResultModifier $modifier = null) + { + try { + return $this->getQuery($specification, $modifier)->getSingleResult(); + } catch (NonUniqueResultException $e) { + throw new HappyrNonUniqueResultException($e->getMessage(), $e->getCode(), $e); + } catch (NoResultException $e) { + throw new HappyrNoResultException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Get single result or null when you match with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @throw HappyrNonUniqueResultException If more than one result is found + * + * @return mixed|null + */ + public function matchOneOrNullResult(Specification $specification, ResultModifier $modifier = null) + { + try { + return $this->matchSingleResult($specification, $modifier); + } catch (HappyrNoResultException $e) { + return null; + } + } + + /** + * Prepare a Query with a Specification. + * + * @param Specification $specification + * @param ResultModifier $modifier + * + * @return Query + */ + public function getQuery(Specification $specification, ResultModifier $modifier = null) + { + /* @var $qb QueryBuilder */ + $qb = $this->createQueryBuilder($this->alias); + + // apply specification + if ($this->transformer instanceof DoctrineORMTransformer) { + return $this->transformer->transform($specification, $modifier, $qb, $this->alias); + } else { + return $qb->getQuery(); + } + } + + /** + * @param string $alias + * + * @return self + */ + public function setAlias($alias) + { + $this->alias = $alias; + + return $this; + } + + /** + * @param DoctrineORMTransformer $transformer + * + * @return self + */ + public function setTransformer(DoctrineORMTransformer $transformer) + { + $this->transformer = $transformer; + + return $this; + } +} diff --git a/src/Transformer/Doctrine/ORM/Exception/InvalidArgumentException.php b/src/Transformer/Doctrine/ORM/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..a5127800 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Exception/InvalidArgumentException.php @@ -0,0 +1,30 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Exception; + +use Happyr\DoctrineSpecification\Exception\InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidArgumentException extends BaseInvalidArgumentException +{ + /** + * @param array $supported_operators + * @param string $operator + * + * @return self + */ + public static function invalidComparisonOperator(array $supported_operators, $operator) + { + return new self(sprintf( + '"%s" is not a valid comparison operator. Valid operators are: "%s"', + $operator, + implode(', ', $supported_operators) + )); + } +} diff --git a/src/Transformer/Doctrine/ORM/Query/QueryTransformer.php b/src/Transformer/Doctrine/ORM/Query/QueryTransformer.php new file mode 100644 index 00000000..a8447834 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/QueryTransformer.php @@ -0,0 +1,22 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query; + +use Doctrine\ORM\AbstractQuery; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; + +interface QueryTransformer +{ + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query); +} diff --git a/src/Transformer/Doctrine/ORM/Query/QueryTransformerCollection.php b/src/Transformer/Doctrine/ORM/Query/QueryTransformerCollection.php new file mode 100644 index 00000000..1e95e1d2 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/QueryTransformerCollection.php @@ -0,0 +1,50 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query; + +use Doctrine\ORM\AbstractQuery; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; + +class QueryTransformerCollection implements QueryTransformer +{ + /** + * @var QueryTransformer[] + */ + private $transformers = []; + + /** + * @param QueryTransformer[] $transformers + */ + public function __construct(array $transformers = []) + { + foreach ($transformers as $transformer) { + $this->addTransformer($transformer); + } + } + + /** + * @param QueryTransformer $transformer + */ + public function addTransformer(QueryTransformer $transformer) + { + $this->transformers[] = $transformer; + } + + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query) + { + foreach ($this->transformers as $transformer) { + $transformer->transform($modifier, $query); + } + } +} diff --git a/src/Transformer/Doctrine/ORM/Query/ResultModifier/AsArrayTransformer.php b/src/Transformer/Doctrine/ORM/Query/ResultModifier/AsArrayTransformer.php new file mode 100644 index 00000000..fcebcc9e --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/ResultModifier/AsArrayTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier; + +use Doctrine\ORM\AbstractQuery; +use Happyr\DoctrineSpecification\ResultModifier\AsArray; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformer; + +class AsArrayTransformer implements QueryTransformer +{ + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query) + { + if ($modifier instanceof AsArray) { + $query->setHydrationMode(AbstractQuery::HYDRATE_ARRAY); + } + } +} diff --git a/src/Transformer/Doctrine/ORM/Query/ResultModifier/AsSingleScalarTransformer.php b/src/Transformer/Doctrine/ORM/Query/ResultModifier/AsSingleScalarTransformer.php new file mode 100644 index 00000000..c7ba3697 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/ResultModifier/AsSingleScalarTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier; + +use Doctrine\ORM\AbstractQuery; +use Happyr\DoctrineSpecification\ResultModifier\AsSingleScalar; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformer; + +class AsSingleScalarTransformer implements QueryTransformer +{ + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query) + { + if ($modifier instanceof AsSingleScalar) { + $query->setHydrationMode(AbstractQuery::HYDRATE_SINGLE_SCALAR); + } + } +} diff --git a/src/Transformer/Doctrine/ORM/Query/ResultModifier/CacheTransformer.php b/src/Transformer/Doctrine/ORM/Query/ResultModifier/CacheTransformer.php new file mode 100644 index 00000000..b446fea1 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/ResultModifier/CacheTransformer.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier; + +use Doctrine\ORM\AbstractQuery; +use Happyr\DoctrineSpecification\ResultModifier\Cache; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformer; + +class CacheTransformer implements QueryTransformer +{ + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query) + { + if ($modifier instanceof Cache) { + $query->setResultCacheLifetime($modifier->getCacheLifetime()); + } + } +} diff --git a/src/Transformer/Doctrine/ORM/Query/ResultModifier/ResultModifierCollectionTransformer.php b/src/Transformer/Doctrine/ORM/Query/ResultModifier/ResultModifierCollectionTransformer.php new file mode 100644 index 00000000..23d81f14 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/ResultModifier/ResultModifierCollectionTransformer.php @@ -0,0 +1,45 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier; + +use Doctrine\ORM\AbstractQuery; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifierCollection; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformerCollection; + +class ResultModifierCollectionTransformer implements QueryTransformer +{ + /** + * @var QueryTransformerCollection + */ + private $collection; + + /** + * @param QueryTransformerCollection $collection + */ + public function __construct(QueryTransformerCollection $collection) + { + $this->collection = $collection; + } + + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query) + { + if ($modifier instanceof ResultModifierCollection) { + foreach ($modifier->getModifiers() as $child) { + $this->collection->transform($child, $query); + } + } + } +} diff --git a/src/Transformer/Doctrine/ORM/Query/ResultModifier/RoundDateTimeTransformer.php b/src/Transformer/Doctrine/ORM/Query/ResultModifier/RoundDateTimeTransformer.php new file mode 100644 index 00000000..876bf738 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/Query/ResultModifier/RoundDateTimeTransformer.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier; + +use Doctrine\ORM\AbstractQuery; +use Doctrine\ORM\Query\Parameter; +use Happyr\DoctrineSpecification\ResultModifier\RoundDateTime; +use Happyr\DoctrineSpecification\ResultModifier\ResultModifier; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformer; + +class RoundDateTimeTransformer implements QueryTransformer +{ + /** + * @param ResultModifier $modifier + * @param AbstractQuery $query + */ + public function transform(ResultModifier $modifier, AbstractQuery $query) + { + if ($modifier instanceof RoundDateTime) { + foreach ($query->getParameters() as $parameter) { + /* @var $parameter Parameter */ + if ($parameter->getValue() instanceof \DateTimeInterface) { + // round down so that the results do not include data that should not be there. + $date = clone $parameter->getValue(); + $date = $date->setTimestamp( + floor($date->getTimestamp() / $modifier->getRoundSeconds()) * + $modifier->getRoundSeconds() + ); + + $query->setParameter($parameter->getName(), $date, $parameter->getType()); + } + } + } + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/ComparisonTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/ComparisonTransformer.php new file mode 100644 index 00000000..d7293731 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/ComparisonTransformer.php @@ -0,0 +1,77 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\Query\Expr\Comparison as DoctrineComparison; +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Comparison; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Exception\InvalidArgumentException; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ValueConverter; + +abstract class ComparisonTransformer implements QueryBuilderTransformer +{ + const EQ = DoctrineComparison::EQ; + const NEQ = DoctrineComparison::NEQ; + const LT = DoctrineComparison::LT; + const LTE = DoctrineComparison::LTE; + const GT = DoctrineComparison::GT; + const GTE = DoctrineComparison::GTE; + + /** + * @var array + */ + private static $operators = [ + self::EQ, + self::NEQ, + self::LT, + self::LTE, + self::GT, + self::GTE, + ]; + + /** + * @param Comparison $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * @param string $operator + * + * @return string + */ + protected function getCondition(Comparison $specification, QueryBuilder $qb, $dqlAlias, $operator) + { + if (!in_array($operator, self::$operators)) { + throw InvalidArgumentException::invalidComparisonOperator(self::$operators, $operator); + } + + $paramName = $this->getParameterName($qb); + $value = ValueConverter::convertToDatabaseValue($specification->getValue(), $qb); + + $qb->setParameter($paramName, $value); + + return (string) new DoctrineComparison( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + $operator, + sprintf(':%s', $paramName) + ); + } + + /** + * Get a good unique parameter name. + * + * @param QueryBuilder $qb + * + * @return string + */ + private function getParameterName(QueryBuilder $qb) + { + return sprintf('comparison_%d', $qb->getParameters()->count()); + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/EqualsTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/EqualsTransformer.php new file mode 100644 index 00000000..e8cc74fe --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/EqualsTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Equals; +use Happyr\DoctrineSpecification\Specification; + +class EqualsTransformer extends ComparisonTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Equals) { + return $this->getCondition($specification, $qb, $dqlAlias, self::EQ); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/GreaterOrEqualThanTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/GreaterOrEqualThanTransformer.php new file mode 100644 index 00000000..c3e495db --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/GreaterOrEqualThanTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\GreaterOrEqualThan; +use Happyr\DoctrineSpecification\Specification; + +class GreaterOrEqualThanTransformer extends ComparisonTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof GreaterOrEqualThan) { + return $this->getCondition($specification, $qb, $dqlAlias, self::GTE); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/GreaterThanTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/GreaterThanTransformer.php new file mode 100644 index 00000000..88431bda --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/GreaterThanTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\GreaterThan; +use Happyr\DoctrineSpecification\Specification; + +class GreaterThanTransformer extends ComparisonTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof GreaterThan) { + return $this->getCondition($specification, $qb, $dqlAlias, self::GT); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/InTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/InTransformer.php new file mode 100644 index 00000000..b4be76f2 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/InTransformer.php @@ -0,0 +1,63 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\In; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ValueConverter; + +class InTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof In) { + $paramName = $this->getParameterName($qb); + $value = $specification->getValue(); + + if (is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = ValueConverter::convertToDatabaseValue($v, $qb); + } + } else { + $value = ValueConverter::convertToDatabaseValue($value, $qb); + } + + $qb->setParameter($paramName, $value); + + return (string) $qb->expr()->in( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + sprintf(':%s', $paramName) + ); + } + + return null; + } + + /** + * Get a good unique parameter name. + * + * @param QueryBuilder $qb + * + * @return string + */ + private function getParameterName(QueryBuilder $qb) + { + return sprintf('in_%d', $qb->getParameters()->count()); + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/InstanceOfXTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/InstanceOfXTransformer.php new file mode 100644 index 00000000..5dfa127d --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/InstanceOfXTransformer.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\InstanceOfX; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class InstanceOfXTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof InstanceOfX) { + return sprintf('%s INSTANCE OF %s', $dqlAlias, $specification->getValue()); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/IsNotNullTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/IsNotNullTransformer.php new file mode 100644 index 00000000..dc04a521 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/IsNotNullTransformer.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\IsNotNull; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class IsNotNullTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof IsNotNull) { + return (string) $qb->expr()->isNotNull( + sprintf('%s.%s', $dqlAlias, $specification->getField()) + ); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/IsNullTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/IsNullTransformer.php new file mode 100644 index 00000000..935b62b3 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/IsNullTransformer.php @@ -0,0 +1,36 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\IsNull; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class IsNullTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof IsNull) { + return (string) $qb->expr()->isNull( + sprintf('%s.%s', $dqlAlias, $specification->getField()) + ); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LessOrEqualThanTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LessOrEqualThanTransformer.php new file mode 100644 index 00000000..23039bea --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LessOrEqualThanTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\LessOrEqualThan; +use Happyr\DoctrineSpecification\Specification; + +class LessOrEqualThanTransformer extends ComparisonTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof LessOrEqualThan) { + return $this->getCondition($specification, $qb, $dqlAlias, self::LTE); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LessThanTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LessThanTransformer.php new file mode 100644 index 00000000..e60c0cd8 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LessThanTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\LessThan; +use Happyr\DoctrineSpecification\Specification; + +class LessThanTransformer extends ComparisonTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof LessThan) { + return $this->getCondition($specification, $qb, $dqlAlias, self::LT); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LikeTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LikeTransformer.php new file mode 100644 index 00000000..402761f2 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/LikeTransformer.php @@ -0,0 +1,64 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\Query\Expr\Comparison; +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Like; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ValueConverter; + +class LikeTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Like) { + $paramName = $this->getParameterName($qb); + $value = ValueConverter::convertToDatabaseValue($specification->getValue(), $qb); + + if ($specification->getFormat() | Like::ENDS_WITH) { + $value = '%'.$value; + } + if ($specification->getFormat() | Like::STARTS_WITH) { + $value = $value.'%'; + } + + $qb->setParameter($paramName, $value); + + return (string) new Comparison( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + 'LIKE', + sprintf(':%s', $paramName) + ); + } + + return null; + } + + /** + * Get a good unique parameter name. + * + * @param QueryBuilder $qb + * + * @return string + */ + private function getParameterName(QueryBuilder $qb) + { + return sprintf('like_%d', $qb->getParameters()->count()); + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/AndXTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/AndXTransformer.php new file mode 100644 index 00000000..e9891be9 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/AndXTransformer.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\Logic; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Logic\AndX; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +class AndXTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof AndX && $this->collection instanceof QueryBuilderTransformerCollection) { + $andX = $qb->expr()->andX(); + + foreach ($specification->getFilters() as $filter) { + if ($condition = $this->collection->transform($filter, $qb, $dqlAlias)) { + $andX->add($condition); + } + } + + return $andX->count() ? (string) $andX : null; + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/NotTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/NotTransformer.php new file mode 100644 index 00000000..c577aa7e --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/NotTransformer.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\Logic; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Logic\Not; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +class NotTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Not && + $this->collection instanceof QueryBuilderTransformerCollection && + ($condition = $this->collection->transform($specification->getFilter(), $qb, $dqlAlias)) + ) { + return $qb->expr()->not($condition); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/OrXTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/OrXTransformer.php new file mode 100644 index 00000000..56431c19 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/Logic/OrXTransformer.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\Logic; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Logic\OrX; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +class OrXTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof OrX && $this->collection instanceof QueryBuilderTransformerCollection) { + $orX = $qb->expr()->orX(); + + foreach ($specification->getFilters() as $filter) { + if ($condition = $this->collection->transform($filter, $qb, $dqlAlias)) { + $orX->add($condition); + } + } + + return $orX->count() ? (string) $orX : null; + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/NotEqualsTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/NotEqualsTransformer.php new file mode 100644 index 00000000..d074e75c --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/Filter/NotEqualsTransformer.php @@ -0,0 +1,33 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\NotEquals; +use Happyr\DoctrineSpecification\Specification; + +class NotEqualsTransformer extends ComparisonTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof NotEquals) { + return $this->getCondition($specification, $qb, $dqlAlias, self::NEQ); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformer.php new file mode 100644 index 00000000..86f1a80b --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformer.php @@ -0,0 +1,27 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Specification; + +interface QueryBuilderTransformer +{ + /** + * Return a condition string or NULL if the specification is not a filter. + * + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias); +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollection.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollection.php new file mode 100644 index 00000000..c5490fc9 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollection.php @@ -0,0 +1,61 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Specification; + +class QueryBuilderTransformerCollection implements QueryBuilderTransformer +{ + /** + * @var QueryBuilderTransformer[] + */ + private $transformers = []; + + /** + * @param QueryBuilderTransformer[] $transformers + */ + public function __construct(array $transformers = []) + { + foreach ($transformers as $transformer) { + $this->addTransformer($transformer); + } + } + + /** + * @param QueryBuilderTransformer $transformer + */ + public function addTransformer(QueryBuilderTransformer $transformer) + { + $this->transformers[] = $transformer; + + if ($transformer instanceof QueryBuilderTransformerCollectionAware) { + $transformer->setCollection($this); + } + } + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + foreach ($this->transformers as $transformer) { + if ($condition = $transformer->transform($specification, $qb, $dqlAlias)) { + return $condition; + } + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionAware.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionAware.php new file mode 100644 index 00000000..f4256e64 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionAware.php @@ -0,0 +1,18 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder; + +interface QueryBuilderTransformerCollectionAware extends QueryBuilderTransformer +{ + /** + * @param QueryBuilderTransformerCollection $collection + */ + public function setCollection(QueryBuilderTransformerCollection $collection); +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionAwareTrait.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionAwareTrait.php new file mode 100644 index 00000000..db2fd5d4 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionAwareTrait.php @@ -0,0 +1,26 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder; + +trait QueryBuilderTransformerCollectionAwareTrait +{ + /** + * @var QueryBuilderTransformerCollection + */ + protected $collection; + + /** + * @param QueryBuilderTransformerCollection $collection + */ + public function setCollection(QueryBuilderTransformerCollection $collection) + { + $this->collection = $collection; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/AbstractJoinTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/AbstractJoinTransformer.php new file mode 100644 index 00000000..56e3a71c --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/AbstractJoinTransformer.php @@ -0,0 +1,50 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\Query\Expr\Join; +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Filter\Filter; +use Happyr\DoctrineSpecification\QueryModifier\AbstractJoin; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +abstract class AbstractJoinTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param AbstractJoin $specification + * @param QueryBuilder $qb + * + * @return string|null + */ + protected function getCondition(AbstractJoin $specification, QueryBuilder $qb) + { + if (!($specification->getCondition() instanceof Filter) || + !($this->collection instanceof QueryBuilderTransformerCollection) + ) { + return null; + } + + return $this->collection->transform($specification->getCondition(), $qb, $specification->getAlias()); + } + + /** + * @param AbstractJoin $specification + * + * @return string + */ + protected function getConditionType(AbstractJoin $specification) + { + return $specification->getConditionType() == AbstractJoin::ON ? Join::ON : Join::WITH; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/GroupByTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/GroupByTransformer.php new file mode 100644 index 00000000..74023adf --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/GroupByTransformer.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\QueryModifier\GroupBy; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class GroupByTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof GroupBy) { + $qb->addGroupBy(sprintf('%s.%s', $dqlAlias, $specification->getField())); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/HavingTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/HavingTransformer.php new file mode 100644 index 00000000..0cc03a41 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/HavingTransformer.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\QueryModifier\Having; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +class HavingTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Having && + $this->collection instanceof QueryBuilderTransformerCollection && + ($condition = $this->collection->transform($specification->getFilter(), $qb, $dqlAlias)) + ) { + $qb->having($condition); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/InnerJoinTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/InnerJoinTransformer.php new file mode 100644 index 00000000..9c6cf589 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/InnerJoinTransformer.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\QueryModifier\InnerJoin; +use Happyr\DoctrineSpecification\Specification; + +class InnerJoinTransformer extends AbstractJoinTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof InnerJoin) { + if ($condition = $this->getCondition($specification, $qb)) { + $qb->innerJoin( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + $specification->getAlias(), + $this->getConditionType($specification), + $condition + ); + } else { + $qb->innerJoin(sprintf('%s.%s', $dqlAlias, $specification->getField()), $specification->getAlias()); + } + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/JoinTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/JoinTransformer.php new file mode 100644 index 00000000..b6ffb161 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/JoinTransformer.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\QueryModifier\Join; +use Happyr\DoctrineSpecification\Specification; + +class JoinTransformer extends AbstractJoinTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Join) { + if ($condition = $this->getCondition($specification, $qb)) { + $qb->join( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + $specification->getAlias(), + $this->getConditionType($specification), + $condition + ); + } else { + $qb->join(sprintf('%s.%s', $dqlAlias, $specification->getField()), $specification->getAlias()); + } + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/LeftJoinTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/LeftJoinTransformer.php new file mode 100644 index 00000000..9940e611 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/LeftJoinTransformer.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\QueryModifier\LeftJoin; +use Happyr\DoctrineSpecification\Specification; + +class LeftJoinTransformer extends AbstractJoinTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof LeftJoin) { + if ($condition = $this->getCondition($specification, $qb)) { + $qb->leftJoin( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + $specification->getAlias(), + $this->getConditionType($specification), + $condition + ); + } else { + $qb->leftJoin(sprintf('%s.%s', $dqlAlias, $specification->getField()), $specification->getAlias()); + } + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/QueryModifierCollectionTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/QueryModifierCollectionTransformer.php new file mode 100644 index 00000000..1b872656 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/QueryModifier/QueryModifierCollectionTransformer.php @@ -0,0 +1,43 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\QueryModifier\QueryModifierCollection; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAware; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollectionAwareTrait; + +class QueryModifierCollectionTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof QueryModifierCollection && + $this->collection instanceof QueryBuilderTransformerCollection + ) { + foreach ($specification->getModifiers() as $modifier) { + // query modifiers not return a conditions + $this->collection->transform($modifier, $qb, $dqlAlias); + } + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/CountOfTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/CountOfTransformer.php new file mode 100644 index 00000000..4a90d732 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/CountOfTransformer.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\ResultManagement\CountOf; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class CountOfTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof CountOf) { + $qb->select(sprintf('COUNT(%s)', $dqlAlias)); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/LimitTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/LimitTransformer.php new file mode 100644 index 00000000..be3f1cf2 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/LimitTransformer.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\ResultManagement\Limit; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class LimitTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Limit) { + $qb->setMaxResults($specification->getLimit()); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/OffsetTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/OffsetTransformer.php new file mode 100644 index 00000000..e7e96ccf --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/OffsetTransformer.php @@ -0,0 +1,34 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\ResultManagement\Offset; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class OffsetTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Offset) { + $qb->setFirstResult($specification->getOffset()); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/OrderByTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/OrderByTransformer.php new file mode 100644 index 00000000..2939527f --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/OrderByTransformer.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\ResultManagement\OrderBy; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class OrderByTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof OrderBy) { + $qb->addOrderBy( + sprintf('%s.%s', $dqlAlias, $specification->getField()), + $specification->getOrder() + ); + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/SliceTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/SliceTransformer.php new file mode 100644 index 00000000..0d1db8c2 --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/ResultManagement/SliceTransformer.php @@ -0,0 +1,38 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\ResultManagement\Slice; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer; + +class SliceTransformer implements QueryBuilderTransformer +{ + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof Slice) { + $qb->setMaxResults($specification->getSliceSize()); + + if ($specification->getSliceNumber() > 0) { + $qb->setFirstResult($specification->getSliceNumber() * $specification->getSliceSize()); + } + } + + return null; + } +} diff --git a/src/Transformer/Doctrine/ORM/QueryBuilder/SpecificationCollectionTransformer.php b/src/Transformer/Doctrine/ORM/QueryBuilder/SpecificationCollectionTransformer.php new file mode 100644 index 00000000..9c390fff --- /dev/null +++ b/src/Transformer/Doctrine/ORM/QueryBuilder/SpecificationCollectionTransformer.php @@ -0,0 +1,45 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder; + +use Doctrine\ORM\QueryBuilder; +use Happyr\DoctrineSpecification\Specification; +use Happyr\DoctrineSpecification\SpecificationCollection; + +class SpecificationCollectionTransformer implements QueryBuilderTransformerCollectionAware +{ + use QueryBuilderTransformerCollectionAwareTrait; + + /** + * @param Specification $specification + * @param QueryBuilder $qb + * @param string $dqlAlias + * + * @return string|null + */ + public function transform(Specification $specification, QueryBuilder $qb, $dqlAlias) + { + if ($specification instanceof SpecificationCollection && + $this->collection instanceof QueryBuilderTransformerCollection + ) { + $andX = $qb->expr()->andX(); + + foreach ($specification->getSpecifications() as $specification) { + if ($condition = $this->collection->transform($specification, $qb, $dqlAlias)) { + $andX->add($condition); + } + } + + return $andX->count() ? (string) $andX : null; + } + + return null; + } +} diff --git a/src/RepositoryFactory.php b/src/Transformer/Doctrine/ORM/RepositoryFactory.php similarity index 50% rename from src/RepositoryFactory.php rename to src/Transformer/Doctrine/ORM/RepositoryFactory.php index 670f9892..bdc4b3f8 100644 --- a/src/RepositoryFactory.php +++ b/src/Transformer/Doctrine/ORM/RepositoryFactory.php @@ -1,9 +1,17 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ -namespace Happyr\DoctrineSpecification; +namespace Happyr\DoctrineSpecification\Transformer\Doctrine\ORM; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Repository\RepositoryFactory as DoctrineRepositoryFactory; /** * Factory class for creating EntitySpecificationRepository instances. @@ -11,8 +19,21 @@ * Provides an implementation of RepositoryFactory so that the * default repository type in Doctrine can easily be replaced. */ -class RepositoryFactory implements \Doctrine\ORM\Repository\RepositoryFactory +class RepositoryFactory implements DoctrineRepositoryFactory { + /** + * @var DoctrineORMTransformer + */ + private $transformer; + + /** + * @param DoctrineORMTransformer $transformer + */ + public function __construct(DoctrineORMTransformer $transformer) + { + $this->transformer = $transformer; + } + /** * Gets the repository for an entity class. * @@ -25,7 +46,8 @@ public function getRepository(EntityManagerInterface $entityManager, $entityName { return new EntitySpecificationRepository( $entityManager, - $entityManager->getClassMetadata($entityName) + $entityManager->getClassMetadata($entityName), + $this->transformer ); } } diff --git a/src/ValueConverter.php b/src/Transformer/Doctrine/ValueConverter.php similarity index 68% rename from src/ValueConverter.php rename to src/Transformer/Doctrine/ValueConverter.php index 37502828..d939966a 100644 --- a/src/ValueConverter.php +++ b/src/Transformer/Doctrine/ValueConverter.php @@ -1,6 +1,13 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ -namespace Happyr\DoctrineSpecification; +namespace Happyr\DoctrineSpecification\Transformer\Doctrine; use Doctrine\ORM\QueryBuilder; diff --git a/tests/EntitySpecificationRepositorySpec.php b/tests/EntitySpecificationRepositorySpec.php deleted file mode 100644 index 5da49b5f..00000000 --- a/tests/EntitySpecificationRepositorySpec.php +++ /dev/null @@ -1,255 +0,0 @@ -beConstructedWith($entityManager, $classMetadata); - } - - public function it_should_modify_query( - QueryModifier $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareEntityManagerStub($entityManager, $qb); - $this->prepareQueryBuilderStub($qb, $query); - $query->execute()->willReturn($this->result); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $this->match($specification); - } - - public function it_should_apply_filter( - Filter $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareEntityManagerStub($entityManager, $qb); - $this->prepareQueryBuilderStub($qb, $query); - $specification->getFilter($qb, $this->alias)->willReturn($this->expression); - - $qb->andWhere($this->expression)->willReturn($qb); - $qb->where()->shouldNotBeCalled(); - - $this->match($specification); - } - - public function it_should_skip_apply_empty_specification( - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareEntityManagerStub($entityManager, $qb); - $this->prepareQueryBuilderStub($qb, $query); - - $qb->andWhere()->shouldNotBeCalled(); - $qb->where()->shouldNotBeCalled(); - - $this->match(null); - } - - public function it_should_throw_exception_when_apply_not_specification( - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareEntityManagerStub($entityManager, $qb); - $this->prepareQueryBuilderStub($qb, $query); - - $this->shouldThrow('\InvalidArgumentException')->duringMatch(new \stdClass()); - $this->shouldThrow('\InvalidArgumentException')->duringMatch(['fake', 'array']); - $this->shouldThrow('\InvalidArgumentException')->duringMatch('fake'); - } - - public function it_matches_a_specification_with_empty_filter( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareEntityManagerStub($entityManager, $qb); - $this->prepareQueryBuilderStub($qb, $query); - $query->execute()->willReturn($this->result); - - $qb->andWhere()->shouldNotBeCalled(); - $qb->where()->shouldNotBeCalled(); - - $this->match($specification)->shouldReturn($this->result); - } - - public function it_matches_a_specification_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareStubs($specification, $entityManager, $qb, $query); - $query->execute()->willReturn($this->result); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $this->match($specification)->shouldReturn($this->result); - } - - public function it_matches_a_single_result_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $singleResult = new \stdClass(); - - $this->prepareStubs($specification, $entityManager, $qb, $query); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $query->getSingleResult()->willReturn($singleResult); - - $this->matchSingleResult($specification)->shouldReturn($singleResult); - } - - public function it_throws_exception_when_expecting_single_result_finding_none_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareStubs($specification, $entityManager, $qb, $query); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $query->getSingleResult()->willThrow(new NoResultException()); - - $this->shouldThrow('Happyr\DoctrineSpecification\Exception\NoResultException')->duringMatchSingleResult($specification); - } - - public function it_throws_exception_when_expecting_single_result_finding_multiple_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareStubs($specification, $entityManager, $qb, $query); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $query->getSingleResult()->willThrow(new NonUniqueResultException()); - - $this->shouldThrow('Happyr\DoctrineSpecification\Exception\NonUniqueResultException')->duringMatchSingleResult($specification); - } - - public function it_matches_a_single_result_when_expecting_one_or_null_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $singleResult = new \stdClass(); - - $this->prepareStubs($specification, $entityManager, $qb, $query); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $query->getSingleResult()->willReturn($singleResult); - - $this->matchOneOrNullResult($specification)->shouldReturn($singleResult); - } - - public function it_matches_null_when_expecting_one_or_null_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareStubs($specification, $entityManager, $qb, $query); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $query->getSingleResult()->willThrow(new NonUniqueResultException()); - - $this->shouldThrow('Happyr\DoctrineSpecification\Exception\NonUniqueResultException')->duringMatchOneOrNullResult($specification); - } - - public function it_throws_exception_when_expecting_one_or_null_finding_multiple_without_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query - ) { - $this->prepareStubs($specification, $entityManager, $qb, $query); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - - $query->getSingleResult()->willThrow(new NonUniqueResultException()); - - $this->shouldThrow('Happyr\DoctrineSpecification\Exception\UnexpectedResultException')->duringMatchOneOrNullResult($specification); - } - - public function it_matches_a_specification_with_result_modifier( - Specification $specification, - EntityManager $entityManager, - QueryBuilder $qb, - AbstractQuery $query, - ResultModifier $modifier - ) { - $this->prepareStubs($specification, $entityManager, $qb, $query); - $query->execute()->willReturn($this->result); - - $specification->modify($qb, $this->alias)->shouldBeCalled(); - $modifier->modify($query)->shouldBeCalled(); - - $this->match($specification, $modifier)->shouldReturn($this->result); - } - - private function prepareStubs(Specification $specification, EntityManager $entityManager, QueryBuilder $qb, AbstractQuery $query) - { - $this->prepareEntityManagerStub($entityManager, $qb); - $this->prepareSpecificationStub($specification, $qb); - $this->prepareQueryBuilderStub($qb, $query); - } - - private function prepareEntityManagerStub(EntityManager $entityManager, QueryBuilder $qb) - { - $entityManager->createQueryBuilder()->willReturn($qb); - } - - private function prepareSpecificationStub(Specification $specification, QueryBuilder $qb) - { - $specification->getFilter($qb, $this->alias)->willReturn($this->expression); - } - - private function prepareQueryBuilderStub(QueryBuilder $qb, Query $query) - { - $qb->from(Argument::any(), $this->alias, null)->willReturn($qb); - $qb->select($this->alias)->willReturn($qb); - $qb->andWhere($this->expression)->willReturn($qb); - $qb->getQuery()->willReturn($query); - } -} diff --git a/tests/Filter/ComparisonSpec.php b/tests/Filter/ComparisonSpec.php deleted file mode 100644 index dbb7ad3d..00000000 --- a/tests/Filter/ComparisonSpec.php +++ /dev/null @@ -1,53 +0,0 @@ -beConstructedWith(Comparison::GT, 'age', 18, 'a'); - } - - public function it_is_an_expression() - { - $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); - } - - public function it_returns_comparison_object(QueryBuilder $qb, ArrayCollection $parameters) - { - $qb->getParameters()->willReturn($parameters); - $parameters->count()->willReturn(10); - - $qb->setParameter('comparison_10', 18)->shouldBeCalled(); - - $comparison = $this->getFilter($qb, null); - - $comparison->shouldReturn('a.age > :comparison_10'); - } - - public function it_uses_comparison_specific_dql_alias_if_passed(QueryBuilder $qb, ArrayCollection $parameters) - { - $this->beConstructedWith(Comparison::GT, 'age', 18, null); - - $qb->getParameters()->willReturn($parameters); - $parameters->count()->willReturn(10); - - $qb->setParameter('comparison_10', 18)->shouldBeCalled(); - - $this->getFilter($qb, 'x')->shouldReturn('x.age > :comparison_10'); - } - - public function it_validates_operator() - { - $this->shouldThrow('Happyr\DoctrineSpecification\Exception\InvalidArgumentException')->during('__construct', array('==', 'age', 18, null)); - } -} diff --git a/tests/Filter/InSpec.php b/tests/Filter/InSpec.php index 50e49e2a..0f0a7e28 100644 --- a/tests/Filter/InSpec.php +++ b/tests/Filter/InSpec.php @@ -1,10 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace tests\Happyr\DoctrineSpecification\Filter; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\QueryBuilder; use Happyr\DoctrineSpecification\Filter\In; use PhpSpec\ObjectBehavior; @@ -15,11 +19,16 @@ class InSpec extends ObjectBehavior { private $field = 'foobar'; - private $value = array('bar', 'baz'); + private $value = ['bar', 'baz']; public function let() { - $this->beConstructedWith($this->field, $this->value, 'a'); + $this->beConstructedWith($this->field, $this->value); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\Filter\In'); } public function it_is_an_expression() @@ -27,17 +36,13 @@ public function it_is_an_expression() $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); } - public function it_returns_expression_func_object(QueryBuilder $qb, ArrayCollection $parameters, Expr $expr) + public function it_should_return_field() { - $dqlAlias = 'a'; - $qb->expr()->willReturn($expr); - $expr->in(sprintf('%s.%s', $dqlAlias, $this->field), ':in_10')->shouldBeCalled(); - - $qb->getParameters()->willReturn($parameters); - $parameters->count()->willReturn(10); - - $qb->setParameter('in_10', $this->value)->shouldBeCalled(); + $this->getField()->shouldReturn($this->field); + } - $this->getFilter($qb, null); + public function it_should_return_value() + { + $this->getValue()->shouldReturn($this->value); } } diff --git a/tests/Filter/InstanceOfXSpec.php b/tests/Filter/InstanceOfXSpec.php index 669e8ce7..39808384 100644 --- a/tests/Filter/InstanceOfXSpec.php +++ b/tests/Filter/InstanceOfXSpec.php @@ -1,8 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace tests\Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\QueryBuilder; use Happyr\DoctrineSpecification\Filter\InstanceOfX; use PhpSpec\ObjectBehavior; @@ -11,9 +17,11 @@ */ class InstanceOfXSpec extends ObjectBehavior { + private $value = 'My\Model'; + public function let() { - $this->beConstructedWith('My\Model', 'o'); + $this->beConstructedWith($this->value); } public function it_is_initializable() @@ -26,8 +34,8 @@ public function it_is_an_expression() $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); } - public function it_returns_expression_func_object(QueryBuilder $qb) + public function it_should_return_value() { - $this->getFilter($qb, null)->shouldReturn('o INSTANCE OF My\Model'); + $this->getValue()->shouldReturn($this->value); } } diff --git a/tests/Filter/IsNotNullSpec.php b/tests/Filter/IsNotNullSpec.php index caedd150..b79c4b3e 100644 --- a/tests/Filter/IsNotNullSpec.php +++ b/tests/Filter/IsNotNullSpec.php @@ -1,9 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace tests\Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\QueryBuilder; use Happyr\DoctrineSpecification\Filter\IsNotNull; use PhpSpec\ObjectBehavior; @@ -14,38 +19,23 @@ class IsNotNullSpec extends ObjectBehavior { private $field = 'foobar'; - private $dqlAlias = 'a'; - public function let() { - $this->beConstructedWith($this->field, $this->dqlAlias); + $this->beConstructedWith($this->field); } - public function it_is_an_expression() + public function it_is_initializable() { - $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); + $this->shouldHaveType('Happyr\DoctrineSpecification\Filter\IsNotNull'); } - /** - * returns expression func object. - */ - public function it_calls_not_null(QueryBuilder $qb, Expr $expr) + public function it_is_an_expression() { - $expression = 'a.foobar is not null'; - - $qb->expr()->willReturn($expr); - $expr->isNotNull(sprintf('%s.%s', $this->dqlAlias, $this->field))->willReturn($expression); - - $this->getFilter($qb, null)->shouldReturn($expression); + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); } - public function it_uses_dql_alias_if_passed(QueryBuilder $qb, Expr $expr) + public function it_should_return_field() { - $dqlAlias = 'x'; - $this->beConstructedWith($this->field, null); - $qb->expr()->willReturn($expr); - - $expr->isNotNull(sprintf('%s.%s', $dqlAlias, $this->field))->shouldBeCalled(); - $this->getFilter($qb, $dqlAlias); + $this->getField()->shouldReturn($this->field); } } diff --git a/tests/Filter/IsNullSpec.php b/tests/Filter/IsNullSpec.php index 72174537..3a749334 100644 --- a/tests/Filter/IsNullSpec.php +++ b/tests/Filter/IsNullSpec.php @@ -1,9 +1,14 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ namespace tests\Happyr\DoctrineSpecification\Filter; -use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\QueryBuilder; use Happyr\DoctrineSpecification\Filter\IsNull; use PhpSpec\ObjectBehavior; @@ -14,38 +19,23 @@ class IsNullSpec extends ObjectBehavior { private $field = 'foobar'; - private $dqlAlias = 'a'; - public function let() { - $this->beConstructedWith($this->field, $this->dqlAlias); + $this->beConstructedWith($this->field); } - public function it_is_an_expression() + public function it_is_initializable() { - $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); + $this->shouldHaveType('Happyr\DoctrineSpecification\Filter\IsNull'); } - /** - * returns expression func object. - */ - public function it_calls_null(QueryBuilder $qb, Expr $expr) + public function it_is_an_expression() { - $expression = 'a.foobar is null'; - - $qb->expr()->willReturn($expr); - $expr->isNull(sprintf('%s.%s', $this->dqlAlias, $this->field))->willReturn($expression); - - $this->getFilter($qb, 'b')->shouldReturn($expression); + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); } - public function it_uses_dql_alias_if_passed(QueryBuilder $qb, Expr $expr) + public function it_should_return_field() { - $dqlAlias = 'x'; - $this->beConstructedWith($this->field, null); - $qb->expr()->willReturn($expr); - - $expr->isNull(sprintf('%s.%s', $dqlAlias, $this->field))->shouldBeCalled(); - $this->getFilter($qb, $dqlAlias); + $this->getField()->shouldReturn($this->field); } } diff --git a/tests/Filter/LikeSpec.php b/tests/Filter/LikeSpec.php index f401e5b5..1b734753 100644 --- a/tests/Filter/LikeSpec.php +++ b/tests/Filter/LikeSpec.php @@ -1,58 +1,67 @@ + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ -namespace tests\Happyr\DoctrineSpecification\Spec; +namespace tests\Happyr\DoctrineSpecification\Filter; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\ORM\QueryBuilder; use Happyr\DoctrineSpecification\Filter\Like; use PhpSpec\ObjectBehavior; +/** + * @mixin Like + */ class LikeSpec extends ObjectBehavior { private $field = 'foo'; private $value = 'bar'; + private $format = Like::CONTAINS; + public function let() { - $this->beConstructedWith($this->field, $this->value, Like::CONTAINS, 'dqlAlias'); + $this->beConstructedWith($this->field, $this->value, $this->format); } - public function it_is_a_specification() + public function it_is_initializable() { - $this->shouldHaveType('Happyr\DoctrineSpecification\Specification\Specification'); + $this->shouldHaveType('Happyr\DoctrineSpecification\Filter\Like'); } - public function it_surrounds_with_wildcards_when_using_contains(QueryBuilder $qb, ArrayCollection $parameters) + public function it_is_a_specification() { - $this->beConstructedWith($this->field, $this->value, Like::CONTAINS, 'dqlAlias'); - $qb->getParameters()->willReturn($parameters); - $parameters->count()->willReturn(1); - - $qb->setParameter('comparison_1', '%bar%')->shouldBeCalled(); - - $this->match($qb, null); + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Filter\Filter'); } - public function it_starts_with_wildcard_when_using_ends_with(QueryBuilder $qb, ArrayCollection $parameters) + public function it_should_return_field() { - $this->beConstructedWith($this->field, $this->value, Like::ENDS_WITH, 'dqlAlias'); - $qb->getParameters()->willReturn($parameters); - $parameters->count()->willReturn(1); - - $qb->setParameter('comparison_1', '%bar')->shouldBeCalled(); + $this->getField()->shouldReturn($this->field); + } - $this->match($qb, null); + public function it_should_return_value() + { + $this->getValue()->shouldReturn($this->value); } - public function it_ends_with_wildcard_when_using_starts_with(QueryBuilder $qb, ArrayCollection $parameters) + public function it_should_return_format() { - $this->beConstructedWith($this->field, $this->value, Like::STARTS_WITH, 'dqlAlias'); - $qb->getParameters()->willReturn($parameters); - $parameters->count()->willReturn(1); + $this->getFormat()->shouldReturn($this->format); + } - $qb->setParameter('comparison_1', 'bar%')->shouldBeCalled(); + public function it_should_return_format_ends_with() + { + $this->beConstructedWith($this->field, $this->value, Like::ENDS_WITH); + $this->getFormat()->shouldReturn(Like::ENDS_WITH); + } - $this->match($qb, null); + public function it_should_return_format_starts_with() + { + $this->beConstructedWith($this->field, $this->value, Like::STARTS_WITH); + $this->getFormat()->shouldReturn(Like::STARTS_WITH); } } diff --git a/tests/Filter/SliceSpec.php b/tests/Filter/SliceSpec.php deleted file mode 100644 index c0636aad..00000000 --- a/tests/Filter/SliceSpec.php +++ /dev/null @@ -1,49 +0,0 @@ -beConstructedWith($this->sliceSize, 0); - } - - public function it_is_a_specification() - { - $this->shouldHaveType('Happyr\DoctrineSpecification\Query\QueryModifier'); - } - - public function it_slice_with_zero_index(QueryBuilder $qb) - { - $this->beConstructedWith($this->sliceSize, 0); - - $qb->setMaxResults($this->sliceSize)->shouldBeCalled(); - - $this->modify($qb, 'a'); - } - - public function it_slice_with_second_index(QueryBuilder $qb) - { - $sliceNumber = 1; - - $this->beConstructedWith($this->sliceSize, $sliceNumber); - - $qb->setMaxResults($this->sliceSize)->shouldBeCalled(); - $qb->setFirstResult($this->sliceSize * $sliceNumber)->shouldBeCalled(); - - $this->modify($qb, 'a'); - } -} diff --git a/tests/Logic/LogicXSpec.php b/tests/Logic/LogicXSpec.php deleted file mode 100644 index 3d4daf34..00000000 --- a/tests/Logic/LogicXSpec.php +++ /dev/null @@ -1,66 +0,0 @@ -beConstructedWith(self::EXPRESSION, array($specificationA, $specificationB)); - } - - public function it_is_a_specification() - { - $this->shouldHaveType('Happyr\DoctrineSpecification\Specification\Specification'); - } - - public function it_modifies_all_child_queries(QueryBuilder $queryBuilder, Specification $specificationA, Specification $specificationB) - { - $dqlAlias = 'a'; - - $specificationA->modify($queryBuilder, $dqlAlias)->shouldBeCalled(); - $specificationB->modify($queryBuilder, $dqlAlias)->shouldBeCalled(); - - $this->modify($queryBuilder, $dqlAlias); - } - - public function it_composes_and_child_with_expression(QueryBuilder $qb, Expr $expression, Specification $specificationA, Specification $specificationB, $x, $y) - { - $dqlAlias = 'a'; - - $specificationA->getFilter($qb, $dqlAlias)->willReturn($x); - $specificationB->getFilter($qb, $dqlAlias)->willReturn($y); - $qb->expr()->willReturn($expression); - - $expression->{self::EXPRESSION}($x, $y)->shouldBeCalled(); - - $this->getFilter($qb, $dqlAlias); - } - - public function it_supports_expressions(QueryBuilder $qb, Expr $expression, Filter $exprA, Filter $exprB, $x, $y) - { - $this->beConstructedWith(self::EXPRESSION, array($exprA, $exprB)); - - $dqlAlias = 'a'; - - $exprA->getFilter($qb, $dqlAlias)->willReturn($x); - $exprB->getFilter($qb, $dqlAlias)->willReturn($y); - $qb->expr()->willReturn($expression); - - $expression->{self::EXPRESSION}($x, $y)->shouldBeCalled(); - - $this->getFilter($qb, $dqlAlias); - } -} diff --git a/tests/Logic/NotSpec.php b/tests/Logic/NotSpec.php deleted file mode 100644 index 5fb3a0d0..00000000 --- a/tests/Logic/NotSpec.php +++ /dev/null @@ -1,54 +0,0 @@ -beConstructedWith($filterExpr, null); - } - - /** - * calls parent. - */ - public function it_calls_parent_match(QueryBuilder $qb, Expr $expr, Filter $filterExpr) - { - $dqlAlias = 'a'; - $expression = 'expression'; - $parentExpression = 'foobar'; - - $qb->expr()->willReturn($expr); - $filterExpr->getFilter($qb, $dqlAlias)->willReturn($parentExpression); - - $expr->not($parentExpression)->willReturn($expression); - - $this->getFilter($qb, $dqlAlias)->shouldReturn($expression); - } - - /** - * modifies parent query. - */ - public function it_modifies_parent_query(QueryBuilder $qb, Specification $spec) - { - $this->beConstructedWith($spec, null); - - $spec->modify($qb, 'a')->shouldBeCalled(); - $this->modify($qb, 'a'); - } - - public function it_does_not_modify_parent_query(QueryBuilder $qb) - { - $this->modify($qb, 'a'); - } -} diff --git a/tests/Query/InnerJoinSpec.php b/tests/Query/InnerJoinSpec.php deleted file mode 100644 index 3091bf54..00000000 --- a/tests/Query/InnerJoinSpec.php +++ /dev/null @@ -1,36 +0,0 @@ -beConstructedWith('user', 'authUser', 'a'); - } - - public function it_is_a_specification() - { - $this->shouldHaveType('Happyr\DoctrineSpecification\Query\QueryModifier'); - } - - public function it_joins_with_default_dql_alias(QueryBuilder $qb) - { - $qb->innerJoin('a.user', 'authUser')->shouldBeCalled(); - $this->modify($qb, 'a'); - } - - public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $qb) - { - $this->beConstructedWith('user', 'authUser'); - $qb->innerJoin('b.user', 'authUser')->shouldBeCalled(); - $this->modify($qb, 'b'); - } -} diff --git a/tests/Query/JoinSpec.php b/tests/Query/JoinSpec.php deleted file mode 100644 index a5114dd8..00000000 --- a/tests/Query/JoinSpec.php +++ /dev/null @@ -1,36 +0,0 @@ -beConstructedWith('user', 'authUser', 'a'); - } - - public function it_is_a_specification() - { - $this->shouldHaveType('Happyr\DoctrineSpecification\Query\QueryModifier'); - } - - public function it_joins_with_default_dql_alias(QueryBuilder $qb) - { - $qb->join('a.user', 'authUser')->shouldBeCalled(); - $this->modify($qb, 'a'); - } - - public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $qb) - { - $this->beConstructedWith('user', 'authUser'); - $qb->join('b.user', 'authUser')->shouldBeCalled(); - $this->modify($qb, 'b'); - } -} diff --git a/tests/Query/LeftJoinSpec.php b/tests/Query/LeftJoinSpec.php deleted file mode 100644 index 5b4cb1ba..00000000 --- a/tests/Query/LeftJoinSpec.php +++ /dev/null @@ -1,36 +0,0 @@ -beConstructedWith('user', 'authUser', 'a'); - } - - public function it_is_a_specification() - { - $this->shouldHaveType('Happyr\DoctrineSpecification\Query\QueryModifier'); - } - - public function it_joins_with_default_dql_alias(QueryBuilder $qb) - { - $qb->leftJoin('a.user', 'authUser')->shouldBeCalled(); - $this->modify($qb, 'a'); - } - - public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $qb) - { - $this->beConstructedWith('user', 'authUser'); - $qb->leftJoin('b.user', 'authUser')->shouldBeCalled(); - $this->modify($qb, 'b'); - } -} diff --git a/tests/QueryModifier/InnerJoinSpec.php b/tests/QueryModifier/InnerJoinSpec.php new file mode 100644 index 00000000..c0830b67 --- /dev/null +++ b/tests/QueryModifier/InnerJoinSpec.php @@ -0,0 +1,73 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\QueryModifier; + +use Happyr\DoctrineSpecification\Filter\Filter; +use Happyr\DoctrineSpecification\QueryModifier\InnerJoin; +use PhpSpec\ObjectBehavior; + +/** + * @mixin InnerJoin + */ +class InnerJoinSpec extends ObjectBehavior +{ + private $field = 'user'; + + private $alias = 'authUser'; + + public function let() + { + $this->beConstructedWith($this->field, $this->alias, null, null); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\QueryModifier\InnerJoin'); + } + + public function it_is_a_specification() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\QueryModifier\QueryModifier'); + } + + public function it_should_return_field() + { + $this->getField()->shouldReturn($this->field); + } + + public function it_should_return_alias() + { + $this->getAlias()->shouldReturn($this->alias); + } + + public function it_should_return_empty_condition() + { + $this->getCondition()->shouldReturn(null); + } + + public function it_should_return_empty_condition_type() + { + $this->getConditionType()->shouldReturn(null); + } + + public function it_should_on_condition(Filter $condition) + { + $this->beConstructedWith($this->field, $this->alias, InnerJoin::ON, $condition); + $this->getCondition()->shouldReturn($condition); + $this->getConditionType()->shouldReturn(InnerJoin::ON); + } + + public function it_should_with_condition(Filter $condition) + { + $this->beConstructedWith($this->field, $this->alias, InnerJoin::WITH, $condition); + $this->getCondition()->shouldReturn($condition); + $this->getConditionType()->shouldReturn(InnerJoin::WITH); + } +} diff --git a/tests/QueryModifier/JoinSpec.php b/tests/QueryModifier/JoinSpec.php new file mode 100644 index 00000000..22e164da --- /dev/null +++ b/tests/QueryModifier/JoinSpec.php @@ -0,0 +1,73 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\QueryModifier; + +use Happyr\DoctrineSpecification\Filter\Filter; +use Happyr\DoctrineSpecification\QueryModifier\Join; +use PhpSpec\ObjectBehavior; + +/** + * @mixin Join + */ +class JoinSpec extends ObjectBehavior +{ + private $field = 'user'; + + private $alias = 'authUser'; + + public function let() + { + $this->beConstructedWith($this->field, $this->alias, null, null); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\QueryModifier\Join'); + } + + public function it_is_a_specification() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\QueryModifier\QueryModifier'); + } + + public function it_should_return_field() + { + $this->getField()->shouldReturn($this->field); + } + + public function it_should_return_alias() + { + $this->getAlias()->shouldReturn($this->alias); + } + + public function it_should_return_empty_condition() + { + $this->getCondition()->shouldReturn(null); + } + + public function it_should_return_empty_condition_type() + { + $this->getConditionType()->shouldReturn(null); + } + + public function it_should_on_condition(Filter $condition) + { + $this->beConstructedWith($this->field, $this->alias, Join::ON, $condition); + $this->getCondition()->shouldReturn($condition); + $this->getConditionType()->shouldReturn(Join::ON); + } + + public function it_should_with_condition(Filter $condition) + { + $this->beConstructedWith($this->field, $this->alias, Join::WITH, $condition); + $this->getCondition()->shouldReturn($condition); + $this->getConditionType()->shouldReturn(Join::WITH); + } +} diff --git a/tests/QueryModifier/LeftJoinSpec.php b/tests/QueryModifier/LeftJoinSpec.php new file mode 100644 index 00000000..a0d84850 --- /dev/null +++ b/tests/QueryModifier/LeftJoinSpec.php @@ -0,0 +1,73 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\QueryModifier; + +use Happyr\DoctrineSpecification\Filter\Filter; +use Happyr\DoctrineSpecification\QueryModifier\LeftJoin; +use PhpSpec\ObjectBehavior; + +/** + * @mixin LeftJoin + */ +class LeftJoinSpec extends ObjectBehavior +{ + private $field = 'user'; + + private $alias = 'authUser'; + + public function let() + { + $this->beConstructedWith($this->field, $this->alias, null, null); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\QueryModifier\LeftJoin'); + } + + public function it_is_a_specification() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\QueryModifier\QueryModifier'); + } + + public function it_should_return_field() + { + $this->getField()->shouldReturn($this->field); + } + + public function it_should_return_alias() + { + $this->getAlias()->shouldReturn($this->alias); + } + + public function it_should_return_empty_condition() + { + $this->getCondition()->shouldReturn(null); + } + + public function it_should_return_empty_condition_type() + { + $this->getConditionType()->shouldReturn(null); + } + + public function it_should_on_condition(Filter $condition) + { + $this->beConstructedWith($this->field, $this->alias, LeftJoin::ON, $condition); + $this->getCondition()->shouldReturn($condition); + $this->getConditionType()->shouldReturn(LeftJoin::ON); + } + + public function it_should_with_condition(Filter $condition) + { + $this->beConstructedWith($this->field, $this->alias, LeftJoin::WITH, $condition); + $this->getCondition()->shouldReturn($condition); + $this->getConditionType()->shouldReturn(LeftJoin::WITH); + } +} diff --git a/tests/Result/AsArraySpec.php b/tests/Result/AsArraySpec.php deleted file mode 100644 index e9d3f793..00000000 --- a/tests/Result/AsArraySpec.php +++ /dev/null @@ -1,26 +0,0 @@ -shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Result\ResultModifier'); - } - - public function it_sets_hydration_mode_to_array(AbstractQuery $query) - { - $query->setHydrationMode(Query::HYDRATE_ARRAY)->shouldBeCalled(); - - $this->modify($query); - } -} diff --git a/tests/Result/AsSingleScalarSpec.php b/tests/Result/AsSingleScalarSpec.php deleted file mode 100644 index 149e0bef..00000000 --- a/tests/Result/AsSingleScalarSpec.php +++ /dev/null @@ -1,26 +0,0 @@ -shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Result\ResultModifier'); - } - - public function it_sets_hydration_mode_to_object(AbstractQuery $query) - { - $query->setHydrationMode(Query::HYDRATE_SINGLE_SCALAR)->shouldBeCalled(); - - $this->modify($query); - } -} diff --git a/tests/Result/CacheSpec.php b/tests/Result/CacheSpec.php deleted file mode 100644 index 454daed4..00000000 --- a/tests/Result/CacheSpec.php +++ /dev/null @@ -1,32 +0,0 @@ -beConstructedWith($this->lifetime); - } - - public function it_is_a_specification() - { - $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Result\ResultModifier'); - } - - public function it_caches_query_for_given_time(AbstractQuery $query) - { - $query->setResultCacheLifetime($this->lifetime)->shouldBeCalled(); - - $this->modify($query); - } -} diff --git a/tests/Result/RoundDateTimeSpec.php b/tests/Result/RoundDateTimeSpec.php deleted file mode 100644 index 1f833c94..00000000 --- a/tests/Result/RoundDateTimeSpec.php +++ /dev/null @@ -1,47 +0,0 @@ -beConstructedWith($this->roundSeconds); - } - - public function it_is_a_specification() - { - $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Result\RoundDateTime'); - } - - public function it_round_date_time_in_query_parameters_for_given_time( - AbstractQuery $query, - Parameter $scalarParam, - Parameter $datetimeParam - ) { - $name = 'now'; - $type = 'datetime'; - $date = new \DateTime('15:55:34'); - - $scalarParam->getValue()->willReturn('foo'); - - $datetimeParam->getValue()->willReturn($date); - $datetimeParam->getName()->willReturn($name); - $datetimeParam->getType()->willReturn($type); - - $query->getParameters()->willReturn(new ArrayCollection([$scalarParam, $datetimeParam])); - $query->setParameter($name, new \DateTime('15:00:00'), $type)->shouldBeCalled(); - - $this->modify($query); - } -} diff --git a/tests/ResultManagement/SliceSpec.php b/tests/ResultManagement/SliceSpec.php new file mode 100644 index 00000000..95c1d53f --- /dev/null +++ b/tests/ResultManagement/SliceSpec.php @@ -0,0 +1,39 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\ResultManagement; + +use Happyr\DoctrineSpecification\ResultManagement\Slice; +use PhpSpec\ObjectBehavior; + +/** + * @mixin Slice + */ +class SliceSpec extends ObjectBehavior +{ + /** + * @var int + */ + private $sliceSize = 25; + + public function let() + { + $this->beConstructedWith($this->sliceSize, 0); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\ResultManagement\Slice'); + } + + public function it_is_a_specification() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\Specification'); + } +} diff --git a/tests/ResultModifier/AsArraySpec.php b/tests/ResultModifier/AsArraySpec.php new file mode 100644 index 00000000..c58a7d0f --- /dev/null +++ b/tests/ResultModifier/AsArraySpec.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\ResultModifier; + +use Happyr\DoctrineSpecification\ResultModifier\AsArray; +use PhpSpec\ObjectBehavior; + +/** + * @mixin AsArray + */ +class AsArraySpec extends ObjectBehavior +{ + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\ResultModifier\AsArray'); + } + + public function it_is_a_result_modifier() + { + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\ResultModifier\ResultModifier'); + } +} diff --git a/tests/ResultModifier/AsSingleScalarSpec.php b/tests/ResultModifier/AsSingleScalarSpec.php new file mode 100644 index 00000000..767416ff --- /dev/null +++ b/tests/ResultModifier/AsSingleScalarSpec.php @@ -0,0 +1,29 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\ResultModifier; + +use Happyr\DoctrineSpecification\ResultModifier\AsSingleScalar; +use PhpSpec\ObjectBehavior; + +/** + * @mixin AsSingleScalar + */ +class AsSingleScalarSpec extends ObjectBehavior +{ + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\ResultModifier\AsSingleScalar'); + } + + public function it_is_a_result_modifier() + { + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\ResultModifier\ResultModifier'); + } +} diff --git a/tests/ResultModifier/CacheSpec.php b/tests/ResultModifier/CacheSpec.php new file mode 100644 index 00000000..a6215e26 --- /dev/null +++ b/tests/ResultModifier/CacheSpec.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\ResultModifier; + +use Happyr\DoctrineSpecification\ResultModifier\Cache; +use PhpSpec\ObjectBehavior; + +/** + * @mixin Cache + */ +class CacheSpec extends ObjectBehavior +{ + private $lifetime = 3600; + + public function let() + { + $this->beConstructedWith($this->lifetime); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\ResultModifier\Cache'); + } + + public function it_is_a_specification() + { + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\ResultModifier\ResultModifier'); + } + + public function it_should_return_field() + { + $this->getCacheLifetime()->shouldReturn($this->lifetime); + } +} diff --git a/tests/ResultModifier/RoundDateTimeSpec.php b/tests/ResultModifier/RoundDateTimeSpec.php new file mode 100644 index 00000000..85bf70d5 --- /dev/null +++ b/tests/ResultModifier/RoundDateTimeSpec.php @@ -0,0 +1,35 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\ResultModifier; + +use Happyr\DoctrineSpecification\ResultModifier\RoundDateTime; + +/** + * @mixin RoundDateTime + */ +class RoundDateTimeSpec +{ + private $roundSeconds = 3600; + + public function let() + { + $this->beConstructedWith($this->roundSeconds); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\ResultModifier\RoundDateTime'); + } + + public function it_is_a_specification() + { + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Result\RoundDateTime'); + } +} diff --git a/tests/SpecSpec.php b/tests/SpecSpec.php deleted file mode 100644 index 4e8cae26..00000000 --- a/tests/SpecSpec.php +++ /dev/null @@ -1,17 +0,0 @@ -andX()->shouldReturnAnInstanceOf('Happyr\DoctrineSpecification\Logic\LogicX'); - } -} diff --git a/tests/Transformer/Doctrine/ORM/Query/QueryTransformerCollectionSpec.php b/tests/Transformer/Doctrine/ORM/Query/QueryTransformerCollectionSpec.php new file mode 100644 index 00000000..c2103f8e --- /dev/null +++ b/tests/Transformer/Doctrine/ORM/Query/QueryTransformerCollectionSpec.php @@ -0,0 +1,56 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query; + +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier\AsArrayTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier\AsSingleScalarTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier\CacheTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\ResultModifier\RoundDateTimeTransformer; +use PhpSpec\ObjectBehavior; + +/** + * @mixin QueryTransformerCollection + */ +class QueryTransformerCollectionSpec extends ObjectBehavior +{ + public function let() + { + $this->beConstructedWith([]); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformerCollection'); + } + + public function it_is_a_specification() + { + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\Query\QueryTransformer'); + } + + public function it_should_available_set_transformer() + { + $this->beConstructedWith([ + new AsArrayTransformer(), + new AsSingleScalarTransformer(), + new CacheTransformer(), + new RoundDateTimeTransformer(), + ]); + } + + public function it_should_available_add_transformers() + { + $this->addTransformer(new AsArrayTransformer()); + $this->addTransformer(new AsSingleScalarTransformer()); + $this->addTransformer(new CacheTransformer()); + $this->addTransformer(new RoundDateTimeTransformer()); + } +} diff --git a/tests/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionSpec.php b/tests/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionSpec.php new file mode 100644 index 00000000..5428af05 --- /dev/null +++ b/tests/Transformer/Doctrine/ORM/QueryBuilder/QueryBuilderTransformerCollectionSpec.php @@ -0,0 +1,119 @@ + + * @copyright Copyright (c) 2014, Tobias Nyholm + * @license http://opensource.org/licenses/MIT + */ + +namespace tests\Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder; + +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\EqualsTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\GreaterOrEqualThanTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\GreaterThanTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\InstanceOfXTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\InTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\IsNotNullTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\IsNullTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\LessOrEqualThanTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\LessThanTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\LikeTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\Logic\AndXTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\Logic\NotTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\Logic\OrXTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\Filter\NotEqualsTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier\GroupByTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier\HavingTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier\InnerJoinTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier\JoinTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier\LeftJoinTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryModifier\QueryModifierCollectionTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement\CountOfTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement\LimitTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement\OffsetTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement\OrderByTransformer; +use Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\ResultManagement\SliceTransformer; +use PhpSpec\ObjectBehavior; + +/** + * @mixin QueryBuilderTransformerCollection + */ +class QueryBuilderTransformerCollectionSpec extends ObjectBehavior +{ + public function let() + { + $this->beConstructedWith([]); + } + + public function it_is_initializable() + { + $this->shouldHaveType('Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformerCollection'); + } + + public function it_is_a_specification() + { + $this->shouldBeAnInstanceOf('Happyr\DoctrineSpecification\Transformer\Doctrine\ORM\QueryBuilder\QueryBuilderTransformer'); + } + + public function it_should_available_set_transformer() + { + $this->beConstructedWith([ + new EqualsTransformer(), + new GreaterOrEqualThanTransformer(), + new GreaterThanTransformer(), + new InstanceOfXTransformer(), + new InTransformer(), + new IsNotNullTransformer(), + new IsNullTransformer(), + new LessOrEqualThanTransformer(), + new LessThanTransformer(), + new LikeTransformer(), + new NotEqualsTransformer(), + new AndXTransformer(), + new NotTransformer(), + new OrXTransformer(), + new GroupByTransformer(), + new HavingTransformer(), + new InnerJoinTransformer(), + new JoinTransformer(), + new LeftJoinTransformer(), + new QueryModifierCollectionTransformer(), + new CountOfTransformer(), + new LimitTransformer(), + new OffsetTransformer(), + new OrderByTransformer(), + new SliceTransformer(), + ]); + } + + public function it_should_available_add_transformers() + { + $this->addTransformer(new EqualsTransformer()); + $this->addTransformer(new GreaterOrEqualThanTransformer()); + $this->addTransformer(new GreaterThanTransformer()); + $this->addTransformer(new InstanceOfXTransformer()); + $this->addTransformer(new InTransformer()); + $this->addTransformer(new IsNotNullTransformer()); + $this->addTransformer(new IsNullTransformer()); + $this->addTransformer(new LessOrEqualThanTransformer()); + $this->addTransformer(new LessThanTransformer()); + $this->addTransformer(new LikeTransformer()); + $this->addTransformer(new NotEqualsTransformer()); + $this->addTransformer(new AndXTransformer()); + $this->addTransformer(new NotTransformer()); + $this->addTransformer(new OrXTransformer()); + $this->addTransformer(new GroupByTransformer()); + $this->addTransformer(new HavingTransformer()); + $this->addTransformer(new InnerJoinTransformer()); + $this->addTransformer(new JoinTransformer()); + $this->addTransformer(new LeftJoinTransformer()); + $this->addTransformer(new QueryModifierCollectionTransformer()); + $this->addTransformer(new CountOfTransformer()); + $this->addTransformer(new LimitTransformer()); + $this->addTransformer(new OffsetTransformer()); + $this->addTransformer(new OrderByTransformer()); + $this->addTransformer(new SliceTransformer()); + } +}