More biz to help simfiles make it. Better error reporting service.
[rock.divinelegy.git] / DataAccess / DataMapper / DataMapper.php
index b5217ad..3900522 100644 (file)
@@ -2,6 +2,7 @@
 \r
 namespace DataAccess\DataMapper;\r
 \r
+use Exception;\r
 use Domain\Entities\IDivineEntity;\r
 use DataAccess\IDatabaseFactory;\r
 use DataAccess\DataMapper\IDataMapper;\r
@@ -64,146 +65,160 @@ class DataMapper implements IDataMapper
         \r
     public function save(IDivineEntity $entity)\r
     {\r
-        $queries = AbstractPopulationHelper::generateUpdateSaveQuery($this->_maps, $entity, $entity->getId(), $this->_db);\r
-        $mergeMap = array();\r
-        $flattened = array();\r
+        try {\r
+            $this->_db->beginTransaction();\r
+            $queries = AbstractPopulationHelper::generateUpdateSaveQuery($this->_maps, $entity, $entity->getId(), $this->_db);\r
+            $mergeMap = array();\r
+            $flattened = array();\r
 \r
-        foreach($queries as $index => $query)\r
-        {\r
-            $this_table = $query['table'];\r
-            $this_columns = $query['columns'];\r
-\r
-            \r
-            for($i = $index+1; $i<count($queries); $i++)\r
+            foreach($queries as $index => $query)\r
             {\r
-                if(\r
-                    $queries[$i]['table'] == $this_table &&\r
-                    !array_key_exists($i, $mergeMap) &&\r
-                    !isset($query['id'])) //only merge create queries, updates are fine to run multiple times\r
+                $this_table = $query['table'];\r
+                $this_columns = $query['columns'];\r
+\r
+\r
+                for($i = $index+1; $i<count($queries); $i++)\r
                 {\r
-                    //XXX: This whole biz is tricky. Basically the problem is that when creating a new simfile,\r
-                    //the datamapper spews out a bunch of create queries. When parsing a simfile for example, there can\r
-                    //be huge redundency - it may produce 5 queries that all create the same step artist, for example.\r
-                    //We attempt to flatten equivalent queries. Originally I was basing it purely on the table name or something,\r
-                    //but that is not enough. In the case of steps, it ends up mergin all the steps together, so we need to\r
-                    //check if the arrays are equal as well, which is what this does.\r
-                    if($this_columns === $queries[$i]['columns'])\r
-                    {\r
-                        //need to keep track of what we merged as future queries might reference the old ids.\r
-                        $mergeMap[$i] = $index;\r
-                    }\r
-                    \r
-                    //XXX: Another thing that might happen is we have to create queries running on the same table, but with unique columns.\r
-                    //In this case, we can take the columns of one and put it into the other. Otherwise we create two records when we really\r
-                    //should have only one. An example of this is when a user is created, a query to add the country to users_meta is run,\r
-                    //and then _another_ to add firstname, lastname and user_id. It should really all be done in one query.\r
-                    \r
-                    //Make sure both queries are for the same table, and the both relate back to the main query\r
-                    if($this_table == $queries[$i]['table'] && in_array('%MAIN_QUERY_ID%', $this_columns) && in_array('%MAIN_QUERY_ID%', $queries[$i]['columns']))\r
+                    if(\r
+                        $queries[$i]['table'] == $this_table &&\r
+                        !array_key_exists($i, $mergeMap) &&\r
+                        !isset($query['id'])) //only merge create queries, updates are fine to run multiple times\r
                     {\r
-                        $this_column_names = array_keys($this_columns);\r
-                        $other_column_names = array_keys($queries[$i]['columns']);\r
-                        $combine = true;\r
-                        foreach($this_column_names as $column_name)\r
+                        //XXX: This whole biz is tricky. Basically the problem is that when creating a new simfile,\r
+                        //the datamapper spews out a bunch of create queries. When parsing a simfile for example, there can\r
+                        //be huge redundency - it may produce 5 queries that all create the same step artist, for example.\r
+                        //We attempt to flatten equivalent queries. Originally I was basing it purely on the table name or something,\r
+                        //but that is not enough. In the case of steps, it ends up mergin all the steps together, so we need to\r
+                        //check if the arrays are equal as well, which is what this does.\r
+                        if($this_columns === $queries[$i]['columns'])\r
                         {\r
-                            if($this_columns[$column_name] != '%MAIN_QUERY_ID%' && in_array($column_name, $other_column_names))\r
-                            {\r
-                                $combine = false;\r
-                            }\r
+                            //need to keep track of what we merged as future queries might reference the old ids.\r
+                            $mergeMap[$i] = $index;\r
                         }\r
-                        \r
-                        if($combine)\r
+\r
+                        //XXX: Another thing that might happen is we have to create queries running on the same table, but with unique columns.\r
+                        //In this case, we can take the columns of one and put it into the other. Otherwise we create two records when we really\r
+                        //should have only one. An example of this is when a user is created, a query to add the country to users_meta is run,\r
+                        //and then _another_ to add firstname, lastname and user_id. It should really all be done in one query.\r
+\r
+                        //Make sure both queries are for the same table, and the both relate back to the main query\r
+                        if($this_table == $queries[$i]['table'] && in_array('%MAIN_QUERY_ID%', $this_columns) && in_array('%MAIN_QUERY_ID%', $queries[$i]['columns']))\r
                         {\r
-                            $this_columns = array_merge($this_columns, $queries[$i]['columns']);\r
-                            $mergeMap[$i] = $index;\r
+                            $this_column_names = array_keys($this_columns);\r
+                            $other_column_names = array_keys($queries[$i]['columns']);\r
+                            $combine = true;\r
+                            foreach($this_column_names as $column_name)\r
+                            {\r
+                                if($this_columns[$column_name] != '%MAIN_QUERY_ID%' && in_array($column_name, $other_column_names))\r
+                                {\r
+                                    $combine = false;\r
+                                }\r
+                            }\r
+\r
+                            if($combine)\r
+                            {\r
+                                $this_columns = array_merge($this_columns, $queries[$i]['columns']);\r
+                                $mergeMap[$i] = $index;\r
+                            }\r
                         }\r
                     }\r
                 }\r
+\r
+                if(!array_key_exists($index, $mergeMap)) {\r
+                    $prepared = isset($query['prepared']) ? $query['prepared'] : null;\r
+                    $id = isset($query['id']) ? $query['id'] : null;\r
+\r
+                    $flattened[$index] = array(\r
+                        'columns' => $this_columns,\r
+                        'table' => $this_table,\r
+                        'prepared' => $prepared,\r
+                        'id' => $id\r
+                    );\r
+                }\r
             }\r
-            \r
-            if(!array_key_exists($index, $mergeMap)) {\r
-                $prepared = isset($query['prepared']) ? $query['prepared'] : null;\r
-                $id = isset($query['id']) ? $query['id'] : null;\r
-\r
-                $flattened[$index] = array(\r
-                    'columns' => $this_columns,\r
-                    'table' => $this_table,\r
-                    'prepared' => $prepared,\r
-                    'id' => $id\r
-                );\r
-            }\r
-        }\r
-        \r
-        $queries = array();\r
-                \r
-        foreach($flattened as $index => $info)\r
-        {\r
-            if(isset($info['id']))\r
+\r
+            $queries = array();\r
+\r
+            foreach($flattened as $index => $info)\r
             {\r
-                $query = $info['prepared'];\r
-                $query = substr($query, 0, -2);\r
-                $query .= sprintf(' WHERE id=%u', $info['id']);\r
-            } else {\r
-                $query = sprintf('INSERT INTO %s (%s) VALUES (%s)',\r
-                $info['table'],\r
-                implode(', ', array_keys($info['columns'])),\r
-                implode(', ', $info['columns']));\r
+                if(isset($info['id']))\r
+                {\r
+                    $query = $info['prepared'];\r
+                    $query = substr($query, 0, -2);\r
+                    $query .= sprintf(' WHERE id=%u', $info['id']);\r
+                } else {\r
+                    $query = sprintf('INSERT INTO %s (%s) VALUES (%s)',\r
+                    $info['table'],\r
+                    implode(', ', array_keys($info['columns'])),\r
+                    implode(', ', $info['columns']));\r
+                }\r
+\r
+                $queries[$index] = $query;\r
             }\r
             \r
-            $queries[$index] = $query;\r
-        }\r
+           // if($queries['TYPE'] == AbstractPopulationHelper::QUERY_TYPE_CREATE)\r
+           // {\r
+                $idMap = [];\r
+                foreach($queries as $index => $query)\r
+                {                                \r
+                    $runQuery = true;\r
+                    //originally was preg_quote('%').'(.*?)'.preg_quote('%') but that failed with things like:\r
+                    //...VALUES ('Voyager Full 50%', %INDEX_REF_0%\r
+                    //it picked up ', \r
+                    //so now only find ones with INDEX_REF and double check that MAIN QUERY isn't there.\r
+                    if (preg_match_all('/'.preg_quote('%INDEX_REF_').'(.*?)'.preg_quote('%').'/s', $query, $matches)) {\r
+                        foreach($matches[1] as $index_ref)\r
+                        {\r
+                            //if($index_ref != 'MAIN_QUERY_ID')\r
+                            //if(strpos($query, '%MAIN_QUERY_ID%') === false)\r
+                            //{\r
+                                $index_id = str_replace('INDEX_REF_', '', $index_ref);\r
+                                $query = str_replace('%INDEX_REF_' . $index_id . '%', $idMap['INDEX_REF_' . $index_id], $query);\r
+                            //} else {\r
+                            //    $runQuery = false;\r
+                            //}\r
+                        }\r
+                    }\r
 \r
-       // if($queries['TYPE'] == AbstractPopulationHelper::QUERY_TYPE_CREATE)\r
-       // {\r
-            $idMap = [];\r
-            foreach($queries as $index => $query)\r
-            {                                \r
-                $runQuery = true;\r
-                if (preg_match_all('/'.preg_quote('%').'(.*?)'.preg_quote('%').'/s', $query, $matches)) {\r
-                    foreach($matches[1] as $index_ref)\r
+                    //if we don't need the main query we can run this\r
+                    if(strpos($query, '%MAIN_QUERY_ID%') === false)\r
                     {\r
-                        if($index_ref != 'MAIN_QUERY_ID')\r
-                        {\r
-                            $index_id = str_replace('INDEX_REF_', '', $index_ref);\r
-                            $query = str_replace('%INDEX_REF_' . $index_id . '%', $idMap['INDEX_REF_' . $index_id], $query);\r
-                        } else {\r
-                            $runQuery = false;\r
+                        $statement = $this->_db->prepare($query);\r
+                        $statement->execute();\r
+                        //$refIndex = $index+1; This was being used as the index for idMap below. I have nfi why I was adding 1.\r
+                        $idMap['INDEX_REF_' . $index] = $this->_db->lastInsertId();\r
+\r
+                        foreach($mergeMap as $oldIndex => $mergedIndex) {\r
+                            if($mergedIndex == $index) {\r
+                                $idMap['INDEX_REF_' . $oldIndex] = $idMap['INDEX_REF_' . $index];\r
+                            }\r
                         }\r
+\r
+                        unset($queries[$index]);\r
+                    } else {\r
+                        //update query so that other references are resolved.\r
+                        $queries[$index] = $query;\r
                     }\r
                 }\r
 \r
-                if($runQuery)\r
+                //at this point we have queries left that depend on the main query id\r
+                foreach($queries as $query)\r
                 {\r
+                    $query = str_replace('%MAIN_QUERY_ID%', end($idMap), $query);\r
                     $statement = $this->_db->prepare($query);\r
                     $statement->execute();\r
-                    //$refIndex = $index+1; This was being used as the index for idMap below. I have nfi why I was adding 1.\r
-                    $idMap['INDEX_REF_' . $index] = $this->_db->lastInsertId();\r
-                    \r
-                    foreach($mergeMap as $oldIndex => $mergedIndex) {\r
-                        if($mergedIndex == $index) {\r
-                            $idMap['INDEX_REF_' . $oldIndex] = $idMap['INDEX_REF_' . $index];\r
-                        }\r
-                    }\r
-                    \r
-                    unset($queries[$index]);\r
-                } else {\r
-                    //update query so that other references are resolved.\r
-                    $queries[$index] = $query;\r
                 }\r
-            }\r
+            //}\r
+\r
+            if(!$entity->getId()) $entity->setId(end($idMap));\r
             \r
-            //at this point we have queries left that depend on the main query id\r
-            foreach($queries as $query)\r
-            {\r
-                $query = str_replace('%MAIN_QUERY_ID%', end($idMap), $query);\r
-                $statement = $this->_db->prepare($query);\r
-                $statement->execute();\r
-            }\r
-        //}\r
-        \r
-        if(!$entity->getId()) $entity->setId(end($idMap));\r
-        \r
-        return $entity;\r
+            $this->_db->commit();\r
+            \r
+            return $entity;\r
+        } catch (Exception $e) {\r
+            $this->_db->rollBack();\r
+            throw $e;\r
+        }\r
     }\r
     \r
     //TODO: Implement\r