\r
namespace DataAccess\DataMapper;\r
\r
+use Exception;\r
use Domain\Entities\IDivineEntity;\r
use DataAccess\IDatabaseFactory;\r
use DataAccess\DataMapper\IDataMapper;\r
\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