From 05733302b20dd632f2f5ca1b38e91e8ea3615dd1 Mon Sep 17 00:00:00 2001 From: Gerben Oolbekkink Date: Wed, 22 Nov 2017 16:36:05 +0100 Subject: [PATCH] Revert changes to PersistenceModel->select and move to ->query --- src/Common/Enum.php | 45 ++++++++++++ src/PersistenceModel.php | 19 ++++- src/Query/OrderDirection.php | 38 ++++++++++ src/Query/SelectMode.php | 84 +++++++++++++++++++++++ src/Query/SelectQuery.php | 80 ++++++++------------- tests/PersistenceModelIntegrationTest.php | 27 +++++++- 6 files changed, 238 insertions(+), 55 deletions(-) create mode 100644 src/Common/Enum.php create mode 100644 src/Query/OrderDirection.php create mode 100644 src/Query/SelectMode.php diff --git a/src/Common/Enum.php b/src/Common/Enum.php new file mode 100644 index 0000000..700baa2 --- /dev/null +++ b/src/Common/Enum.php @@ -0,0 +1,45 @@ + + * @date 29/10/2017 + */ +abstract class Enum { + /** + * Error constants. + */ + const ERROR_CHOICE_NOT_SUPPORTED = 'Choice "%s" not supported.'; + + /** + * @var string[] + */ + protected static $supportedChoices = []; + + /** + * @var string Current choice + */ + protected $choice; + + /** + * Enum constructor. + * @param string $choice + * @throws OrmException + */ + public function __construct($choice) { + + if (isset(static::$supportedChoices[$choice])) { + $this->choice = $choice; + } else { + throw new OrmException(sprintf(self::ERROR_CHOICE_NOT_SUPPORTED, $choice)); + } + } + + /** + * @return string + */ + public function getChoice() { + return $this->choice; + } +} diff --git a/src/PersistenceModel.php b/src/PersistenceModel.php index 9632109..940a542 100644 --- a/src/PersistenceModel.php +++ b/src/PersistenceModel.php @@ -161,10 +161,25 @@ public function count($criteria = null, array $criteria_params = []) { * * @return SelectQuery */ - public function select() { - return new SelectQuery($this, $this->database);//$this->database->sqlSelect($columns, $this->getTableName(), $criteria, $criteria_params); + public function query() { + return new SelectQuery($this, $this->database); } + /** + * Select existing entities with optional criteria. + * + * Allows for selecting specific sums, averages and counts + * + * @param array $columns SELECT + * @param string $criteria WHERE + * @param array $criteria_params optional named parameters + * @return PDOStatement + */ + public function select(array $columns, $criteria = null, array $criteria_params = []) { + return $this->database->sqlSelect($columns, $this->getTableName(), $criteria, $criteria_params); + } + + /** * Check if entity exists. * diff --git a/src/Query/OrderDirection.php b/src/Query/OrderDirection.php new file mode 100644 index 0000000..235ee74 --- /dev/null +++ b/src/Query/OrderDirection.php @@ -0,0 +1,38 @@ + + * @date 02/11/2017 + */ +class OrderDirection extends Enum { + /** + * Possible choices. + */ + const CHOICE_ASC = 'ASC'; + const CHOICE_DESC = 'DESC'; + + /** + * @var string[] + */ + protected static $supportedChoices = [ + self::CHOICE_ASC => self::CHOICE_ASC, + self::CHOICE_DESC => self::CHOICE_DESC, + ]; + + /** + * @return OrderDirection + */ + public static function ASC() { + return new static(self::CHOICE_ASC); + } + + /** + * @return OrderDirection + */ + public static function DESC() { + return new static(self::CHOICE_DESC); + } +} diff --git a/src/Query/SelectMode.php b/src/Query/SelectMode.php new file mode 100644 index 0000000..de35802 --- /dev/null +++ b/src/Query/SelectMode.php @@ -0,0 +1,84 @@ + + * @date 29/10/2017 + */ +class SelectMode extends Enum { + /** + * Select type constants. + */ + const TYPE_EXACT = 'exact'; + const TYPE_IEXACT = 'iexact'; + const TYPE_CONTAINS = 'contains'; + const TYPE_ICONTAINS = 'icontains'; + const TYPE_STARTSWITH = 'startswith'; + const TYPE_ISTARTSWITH = 'istartswith'; + const TYPE_ENDSWITH = 'endswith'; + const TYPE_IENDSWITH = 'iendswith'; + const TYPE_LT = 'lt'; + const TYPE_LTE = 'lte'; + const TYPE_GT = 'gt'; + const TYPE_GTE = 'gte'; + + /** + * Sql operator constants. + */ + const COLLATE_CASE_SENSITIVE = 'COLLATE \'utf8_bin\' '; + const OPERATOR_ENDSWITH = 'LIKE \'%\''; + const OPERATOR_STARTSWITH = 'LIKE ? || \'%\''; + const OPERATOR_CONTAINS = 'LIKE \'%\' || ? || \'%\''; + const OPERATOR_EXACT = '= ?'; + const OPERATOR_LT = '< ?'; + const OPERATOR_LTE = '<= ?'; + const OPERATOR_GT = '> ?'; + const OPETAROR_GTE = '>= ?'; + const SQL_AND = ' AND '; + + /** + * @var string[] + */ + protected static $supportedChoices = [ + self::TYPE_EXACT => self::TYPE_EXACT, + self::TYPE_IEXACT => self::TYPE_IEXACT, + self::TYPE_CONTAINS => self::TYPE_CONTAINS, + self::TYPE_ICONTAINS => self::TYPE_ICONTAINS, + self::TYPE_STARTSWITH => self::TYPE_STARTSWITH, + self::TYPE_ISTARTSWITH => self::TYPE_ISTARTSWITH, + self::TYPE_ENDSWITH => self::TYPE_ENDSWITH, + self::TYPE_IENDSWITH => self::TYPE_IENDSWITH, + self::TYPE_LT => self::TYPE_LT, + self::TYPE_LTE => self::TYPE_LTE, + self::TYPE_GT => self::TYPE_GT, + self::TYPE_GTE => self::TYPE_GTE, + ]; + + /** + * @var string[] + */ + protected static $typeToOperator = [ + self::TYPE_EXACT => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_EXACT, + self::TYPE_IEXACT => self::OPERATOR_EXACT, + self::TYPE_CONTAINS => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_CONTAINS, + self::TYPE_ICONTAINS => self::OPERATOR_CONTAINS, + self::TYPE_STARTSWITH => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_STARTSWITH, + self::TYPE_ISTARTSWITH => self::OPERATOR_STARTSWITH, + self::TYPE_ENDSWITH => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_ENDSWITH, + self::TYPE_IENDSWITH => self::OPERATOR_ENDSWITH, + self::TYPE_LT => self::OPERATOR_LT, + self::TYPE_LTE => self::OPERATOR_LTE, + self::TYPE_GT => self::OPERATOR_GT, + self::TYPE_GTE => self::OPETAROR_GTE, + ]; + + /** + * @return string + */ + public function getOperator() { + return static::$typeToOperator[$this->choice]; + } +} diff --git a/src/Query/SelectQuery.php b/src/Query/SelectQuery.php index 0c59256..9fcef1a 100644 --- a/src/Query/SelectQuery.php +++ b/src/Query/SelectQuery.php @@ -20,34 +20,9 @@ class SelectQuery { const ERROR_NO_RESULT = 'Expected at least one result'; const ERROR_SELECT_NOT_POSSIBLE = 'Selecting field "%s" in model "%s" not possible.'; - /** - * Select type constants. - */ - const TYPE_EXACT = 'exact'; - const TYPE_IEXACT = 'iexact'; - const TYPE_CONTAINS = 'contains'; - const TYPE_ICONTAINS = 'icontains'; - const TYPE_STARTSWITH = 'startswith'; - const TYPE_ISTARTSWITH = 'istartswith'; - const TYPE_ENDSWITH = 'endswith'; - const TYPE_IENDSWITH = 'iendswith'; - const TYPE_LT = 'lt'; - const TYPE_LTE = 'lte'; - const TYPE_GT = 'gt'; - const TYPE_GTE = 'gte'; - /** * Sql operator constants. */ - const COLLATE_CASE_SENSITIVE = 'COLLATE \'utf8_bin\' '; - const OPERATOR_ENDSWITH = 'LIKE \'%\''; - const OPERATOR_STARTSWITH = 'LIKE ? || \'%\''; - const OPERATOR_CONTAINS = 'LIKE \'%\' || ? || \'%\''; - const OPERATOR_EXACT = '= ?'; - const OPERATOR_LT = '< ?'; - const OPERATOR_LTE = '<= ?'; - const OPERATOR_GT = '> ?'; - const OPETAROR_GTE = '>= ?'; const SQL_AND = ' AND '; /** @var PersistenceModel */ @@ -68,6 +43,9 @@ class SelectQuery { /** @var Database */ protected $database; + /** @var string[] */ + protected $order; + /** @var int */ protected $limit; @@ -87,6 +65,7 @@ public function __construct(PersistenceModel $model, Database $database) { $this->limit = -1; $this->criteria = []; $this->criteria_params = []; + $this->order = []; } /** @@ -143,7 +122,7 @@ public function filterBy($field_type, $value) { $matches = explode('__', $field_type); $field = $matches[0]; - $type = isset($matches[1]) ? $matches[1] : self::TYPE_IEXACT; + $type = isset($matches[1]) ? new SelectMode($matches[1]) : new SelectMode(SelectMode::TYPE_IEXACT); if (in_array($field, $attributes)) { $this->criteria[] = $this->getCriteriaSql($field, $type); @@ -167,6 +146,8 @@ public function getAll() { * @throws OrmException */ public function getOne() { + $this->limit(1); + $entity = $this->execute()->fetch(); if ($entity === false) { @@ -216,7 +197,7 @@ public function execute() { $this->getCriteriaString(), $this->criteria_params, null, - null, + $this->getOrderString(), $this->limit, $this->offset ); @@ -245,34 +226,21 @@ public function copy() { } /** - * @param $field - * @param string $type + * @param string $column + * @param OrderDirection $direction + */ + public function addOrderBy($column, OrderDirection $direction) { + $this->order[] = $column . ' ' . $direction->getChoice(); + } + + /** + * @param string $field + * @param SelectMode $type * @return string * @throws OrmException */ - private function getCriteriaSql($field, $type) { - $mapTypeToOperator = [ - self::TYPE_EXACT => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_EXACT, - self::TYPE_IEXACT => self::OPERATOR_EXACT, - self::TYPE_CONTAINS => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_CONTAINS, - self::TYPE_ICONTAINS => self::OPERATOR_CONTAINS, - self::TYPE_STARTSWITH => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_STARTSWITH, - self::TYPE_ISTARTSWITH => self::OPERATOR_STARTSWITH, - self::TYPE_ENDSWITH => self::COLLATE_CASE_SENSITIVE . self::OPERATOR_ENDSWITH, - self::TYPE_IENDSWITH => self::OPERATOR_ENDSWITH, - self::TYPE_LT => self::OPERATOR_LT, - self::TYPE_LTE => self::OPERATOR_LTE, - self::TYPE_GT => self::OPERATOR_GT, - self::TYPE_GTE => self::OPETAROR_GTE, - ]; - - if (isset($mapTypeToOperator[$type])) { - $operator = $mapTypeToOperator[$type]; - } else { - throw new OrmException(sprintf(self::ERROR_OPERATOR_DOES_NOT_EXIST, $type)); - } - - return sprintf('%s %s', $field, $operator); + private function getCriteriaSql($field, SelectMode $type) { + return sprintf('%s %s', $field, $type->getOperator()); } /** @@ -285,4 +253,12 @@ private function getCriteriaString() { return implode(self::SQL_AND, $this->criteria); } } + + private function getOrderString() { + if (count($this->order) === 0) { + return null; + } else { + return implode(', ', $this->order); + } + } } diff --git a/tests/PersistenceModelIntegrationTest.php b/tests/PersistenceModelIntegrationTest.php index 67e8e0c..d662e86 100644 --- a/tests/PersistenceModelIntegrationTest.php +++ b/tests/PersistenceModelIntegrationTest.php @@ -65,7 +65,8 @@ public function testCreate() { $this->model->create($car); - $car = $this->model->select() + /** @var Car $car */ + $car = $this->model->query() ->filterBy('num_wheels', 2) ->getOne(); @@ -74,6 +75,30 @@ public function testCreate() { $this->assertEquals(3, $this->model->count()); } + public function testCountUnfiltered() { + + $query = $this->model->query()->filterBy('num_wheels', 5); + + $countBefore = $query->countUnfiltered(); + + + $car = new Car(); + $car->num_wheels = 5; + $car->brand = "Opel"; + + $this->model->create($car); + + $car2 = new Car(); + $car->num_wheels = 4; + $car->brand = "Toyota"; + + $this->model->create($car2); + + + $this->assertEquals($countBefore + 2, $query->countUnfiltered()); + $this->assertEquals(1, $query->count()); + } + public function testRetrieve() { $car = new Car(); $car->id = 1;