Nicer implementation of query builder and query constraints.
authorCameron Ball <c.ball1729@gmail.com>
Fri, 19 Sep 2014 12:32:32 +0000 (20:32 +0800)
committerCameron Ball <c.ball1729@gmail.com>
Fri, 19 Sep 2014 12:32:32 +0000 (20:32 +0800)
Controllers/IndexController.php
DataAccess/DataMapper/DataMapper.php
DataAccess/DataMapper/IDataMapper.php
DataAccess/Queries/IQueryBuilder.php [new file with mode: 0644]
DataAccess/Queries/IQueryBuilderFactory.php [new file with mode: 0644]
DataAccess/Queries/IQueryConstraints.php
DataAccess/Queries/QueryBuilder.php [new file with mode: 0644]
DataAccess/Queries/QueryBuilderFactory.php [new file with mode: 0644]
DataAccess/Queries/StepMania/SimfileQueryConstraints.php
DataAccess/StepMania/SimfileRepository.php
config/DI.php

index 8b4ba2a..101d71b 100644 (file)
@@ -28,7 +28,7 @@ class IndexController implements IDivineController
         $queryConstraints = new SimfileQueryConstraints();
         $queryConstraints->stepsHaveRating(15);
 
-        $simfiles = $this->_simfileRepository->findByTitle('a', $queryConstraints);
+        $simfiles = $this->_simfileRepository->findByTitle('a');
 
         foreach($simfiles as $simfile)
         {
index 9d1611b..b33923c 100644 (file)
@@ -4,6 +4,7 @@ namespace DataAccess\DataMapper;
 \r
 use Domain\Entities\IDivineEntity;\r
 use DataAccess\DataMapper\IDataMapper;\r
+use DataAccess\Queries\IQueryBuilder;\r
 use DataAccess\DataMapper\Helpers\AbstractPopulationHelper;\r
 use ReflectionClass;\r
 use PDO;\r
@@ -22,12 +23,14 @@ class DataMapper implements IDataMapper
         $options = array(PDO::ATTR_EMULATE_PREPARES => false,\r
                          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);\r
         \r
-        $this->_db = new PDO($dsn, $username, null, $options);        \r
+        $this->_db = new PDO($dsn, $username, $password, $options);        \r
         $this->_maps = include $maps;\r
     }\r
     \r
-    public function map($entityName, $queryString = 'SELECT * FROM %s')\r
+    public function map($entityName, IQueryBuilder $queryBuilder)\r
     {\r
+        $queryString = $queryBuilder->buildQuery();\r
+        \r
         $statement = $this->_db->prepare(sprintf($queryString,\r
             $this->_maps[$entityName]['table']\r
         ));\r
index 001db54..dec2895 100644 (file)
@@ -2,7 +2,7 @@
 \r
 namespace DataAccess\DataMapper;\r
 \r
-use DataAccess\Queries\IQueryConstraints;\r
+use DataAccess\Queries\IQueryBuilder;\r
 use Domain\Entities\IDivineEntity;\r
 \r
 interface IDataMapper\r
@@ -10,7 +10,7 @@ interface IDataMapper
     //TODO: Table is the wrong name. We actually give the implementation the entity name and it finds the table from the maps.\r
     \r
     //find in table based on constraints and return it as entity\r
-    public function map($entityName, $queryString);\r
+    public function map($entityName, IQueryBuilder $queryBuilder);\r
     //insert/update entity in table\r
     public function save(IDivineEntity $entity);\r
     //remove entity from table\r
diff --git a/DataAccess/Queries/IQueryBuilder.php b/DataAccess/Queries/IQueryBuilder.php
new file mode 100644 (file)
index 0000000..777cf15
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/* 
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+namespace DataAccess\Queries;
+
+interface IQueryBuilder
+{
+    public function setBaseQuery($query);
+    public function limit($start, $end);
+    public function where($column, $operator, $value);
+    public function join($type, $tableA, $columnA, $tableB, $columnB);
+    public function buildQuery();
+}
\ No newline at end of file
diff --git a/DataAccess/Queries/IQueryBuilderFactory.php b/DataAccess/Queries/IQueryBuilderFactory.php
new file mode 100644 (file)
index 0000000..53583e9
--- /dev/null
@@ -0,0 +1,8 @@
+<?php\r
+\r
+namespace DataAccess\Queries;\r
+\r
+interface IQueryBuilderFactory\r
+{\r
+    public function createInstance();\r
+}\r
index 38128d6..ffa396f 100644 (file)
@@ -1,17 +1,10 @@
-<?php
-
-/* 
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-
-namespace DataAccess\Queries;
-
-interface IQueryConstraints
-{
-    public function applyTo($queryString);
-    public function limit($start, $end);
-    public function where($column, $operator, $value);
-    public function join($type, $tableA, $columnA, $tableB, $columnB);
+<?php\r
+\r
+namespace DataAccess\Queries;\r
+\r
+use DataAccess\Queries\IQueryBuilder;\r
+\r
+interface IQueryConstraints\r
+{\r
+    public function applyTo(IQueryBuilder $queryBuilder);\r
 }
\ No newline at end of file
diff --git a/DataAccess/Queries/QueryBuilder.php b/DataAccess/Queries/QueryBuilder.php
new file mode 100644 (file)
index 0000000..9cfa58f
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+namespace DataAccess\Queries;
+
+use DataAccess\Queries\IQueryBuilder;
+
+class QueryBuilder implements IQueryBuilder
+{
+    private $_queryString = 'SELECT * FROM %s';
+    
+    protected $_whereClauses = array();
+    protected $_limitClause;
+    protected $_joinClause;
+    
+    public function buildQuery()
+    {        
+        $this->applyJoinClause()
+             ->applyWhereClauses()
+             ->applyLimitClause();
+        
+        return $this->_queryString;
+    }
+    
+    public function setBaseQuery($baseQuery)
+    {
+        $this->_queryString = $baseQuery;
+    }
+    
+    public function where($columnName, $operator, $value)
+    {
+        $this->_whereClauses[$columnName] = array('operator' => $operator, 'value' => $value);
+        return $this;
+    }
+    
+    public function limit($start, $end = null)
+    {
+        if($end) 
+        {
+            $this->_limitClause = sprintf(' LIMIT %u,%u', $start, $end);
+            return $this;
+        }
+        
+        $this->_limitClause = sprintf(' LIMIT %u', $start);
+        
+        return $this;
+    }
+    
+    public function join($type, $tableA, $columnA, $tableB, $columnB)
+    {
+        $this->_joinClause = sprintf(' %s JOIN %s ON %s.%s = %s.%s', $type, $tableB, $tableA, $columnA, $tableB, $columnB);
+        return $this;
+    }
+    
+    private function applyJoinClause()
+    {
+        $this->_queryString .= $this->_joinClause;
+        return $this;
+    }
+    
+    private function applyWhereClauses()
+    {
+        $this->_queryString .= ' WHERE ';
+        
+        foreach($this->_whereClauses as $columnName => $columnValue)
+        {
+            switch(gettype($columnValue['value']))
+            {
+                case 'integer':
+                    $this->_queryString .= sprintf("%s%s%u", $columnName, $columnValue['operator'], $columnValue['value']) . ' AND ';
+                    break;
+                case 'string':
+                    $this->_queryString .= sprintf("%s %s '%s'", $columnName, $columnValue['operator'], $columnValue['value']) . ' AND ';
+                    break;
+            }
+            
+        }
+        
+        $this->_queryString = rtrim($this->_queryString, ' AND ');
+        return $this;
+    }
+    
+    private function applyLimitClause()
+    {
+        $this->_queryString .= $this->_limitClause;
+        return $this;
+    }
+    
+}
\ No newline at end of file
diff --git a/DataAccess/Queries/QueryBuilderFactory.php b/DataAccess/Queries/QueryBuilderFactory.php
new file mode 100644 (file)
index 0000000..189a341
--- /dev/null
@@ -0,0 +1,14 @@
+<?php\r
+\r
+namespace DataAccess\Queries;\r
+\r
+use DataAccess\Queries\QueryBuilder;\r
+use DataAccess\Queries\IQueryBuilderFactory;\r
+\r
+class QueryBuilderFactory implements IQueryBuilderFactory\r
+{\r
+    public function createInstance()\r
+    {\r
+        return new QueryBuilder();\r
+    }\r
+}
\ No newline at end of file
index 1876634..cbb82cd 100644 (file)
@@ -2,24 +2,41 @@
 
 namespace DataAccess\Queries\StepMania;
 
-use DataAccess\Queries\QueryConstraints;
+use DataAccess\Queries\IQueryBuilder;
 use DataAccess\Queries\StepMania\ISimfileQueryConstraints;
 
-class SimfileQueryConstraints extends QueryConstraints implements ISimfileQueryConstraints
+class SimfileQueryConstraints implements ISimfileQueryConstraints
 {        
+    
+    private $_queryBuilder;
+    private $_fgChanges;
+    private $_bgChanges;
+    private $_stepRating;
+    
+    public function applyTo(IQueryBuilder $queryBuilder)
+    {
+        $this->_queryBuilder = $queryBuilder;
+        
+        $this->applyStepsRating()
+             ->applyBgChanges()
+             ->applyFgChanges();
+    }
+    
     public function hasFgChanges($bool)
     {
-        return $this->where('fg_changes', '=', (int)$bool);
+        $this->_fgChanges = (int)$bool;
+        return $this;
     }
     public function hasBgChanges($bool)
     {
-        return $this->where('bg_changes', '=', (int)$bool);
+        $this->_bgChanges = (int)$bool;
+        return $this;
     }
     
     public function stepsHaveRating($rating)
     {
-        return $this->join('INNER', 'simfiles', 'id', 'steps', 'simfile_id')
-                    ->where('steps.rating', '=', $rating);
+        $this->_stepRating = $rating;
+        return $this;
     }
     public function hasDifficulty($difficulty){}
     
@@ -27,4 +44,33 @@ class SimfileQueryConstraints extends QueryConstraints implements ISimfileQueryC
     {
         return;
     }
+    
+    private function applyFgChanges()
+    {
+        if($this->_fgChanges) {
+            $this->_queryBuilder->where('fg_changes', '=', $this->_fgChanges);
+        }
+        
+        return $this;
+    }
+    
+    private function applyBgChanges()
+    {
+        if($this->_bgChanges) {
+            $this->_queryBuilder->where('bg_changes', '=', $this->_bgChanges);
+        }
+        
+        return $this;
+    }
+    
+    private function applyStepsRating()
+    {
+        if($this->_stepRating)
+        {
+            $this->_queryBuilder->join('INNER', 'simfiles', 'id', 'steps', 'simfile_id')
+                                ->where('steps.rating', '=', $this->_stepRating);
+        }
+        
+        return $this;
+    }
 }
index 02d7c29..7cd8522 100644 (file)
@@ -4,6 +4,7 @@ namespace DataAccess\StepMania;
 \r
 use DataAccess\StepMania\ISimfileRepository;\r
 use DataAccess\DataMapper\IDataMapper;\r
+use DataAccess\Queries\IQueryBuilderFactory;\r
 use DataAccess\Queries\StepMania\ISimfileQueryConstraints;\r
 use Domain\Entities\StepMania\ISimfile;\r
 \r
@@ -11,9 +12,11 @@ use Domain\Entities\StepMania\ISimfile;
 class SimfileRepository implements ISimfileRepository\r
 {\r
     private $_dataMapper;\r
+    private $_queryBuilderFactory;\r
     \r
-    public function __construct(IDataMapper $dataMapper) {\r
+    public function __construct(IDataMapper $dataMapper, IQueryBuilderFactory $queryBuilderFactory) {\r
         $this->_dataMapper = $dataMapper;\r
+        $this->_queryBuilderFactory = $queryBuilderFactory;\r
     }\r
     \r
     public function findById($id) {\r
@@ -42,26 +45,15 @@ class SimfileRepository implements ISimfileRepository
     \r
     public function findByTitle($title, ISimfileQueryConstraints $constraints = NULL)\r
     {\r
-        //TODO: Should I inject a factory, and then make $constraints if it isn't given?\r
+        $queryBuilder = $this->_queryBuilderFactory->createInstance();\r
+        $queryBuilder->where('title', 'LIKE', "%%$title%%");\r
+        \r
         if($constraints)\r
         {\r
-            $queryString = $constraints->where('title', 'LIKE', "%%$title%%") //TODO: Should I make a like method that handles adding the %% ?\r
-                                       ->applyTo('SELECT * from %s');\r
-        } else {\r
-            //It would avoid this, or rather I could put this in the constraints class\r
-            $queryString = "SELECT * FROM %s WHERE title LIKE '%$title%'";\r
+            $constraints->applyTo($queryBuilder);\r
         }\r
-        \r
-        //is it better to pass in constraints object?\r
-        //could have a default "select * from %s" in the constraints object which could be overwritten via a method.\r
-        //-no more need for applyTo, just go $constratints->getQuery\r
-        //maybe it should no longer be constraints but instead queryBuilder\r
-        \r
-        /**\r
-         * have this class contain a queryBuilderFactory and then have constraintsClass \r
-         * go in through methods which act on the query, adding in constraints.\r
-         */\r
-        return $this->_dataMapper->map('Simfile', $queryString);\r
+\r
+        return $this->_dataMapper->map('Simfile', $queryBuilder);\r
     }\r
     \r
     public function findByArtist($artist){}\r
index 3f12b93..b1dbecc 100644 (file)
@@ -18,5 +18,5 @@ return [
     'DataAccess\StepMania\ISimfileRepository' => DI\object('DataAccess\StepMania\SimfileRepository'),\r
     'DataAccess\DataMapper\IDataMapper' => DI\object('DataAccess\DataMapper\DataMapper')\r
         ->constructor(DI\link('datamapper.maps')),   \r
-   \r
+    'DataAccess\Queries\IQueryBuilderFactory' => DI\object('DataAccess\Queries\QueryBuilderFactory')\r
 ];\r