I think uploading packs is more or less working. I nutted out a few bugs in a lot...
[rock.divinelegy.git] / DataAccess / DataMapper / DataMapper.php
1 <?php
2
3 namespace DataAccess\DataMapper;
4
5 use Domain\Entities\IDivineEntity;
6 use DataAccess\IDatabaseFactory;
7 use DataAccess\DataMapper\IDataMapper;
8 use DataAccess\Queries\IQueryBuilder;
9 use DataAccess\DataMapper\Helpers\AbstractPopulationHelper;
10 use ReflectionClass;
11
12 class DataMapper implements IDataMapper
13 {
14 private $_db;
15 private $_maps;
16
17 public function __construct($maps, IDatabaseFactory $databaseFactory)
18 {
19 $this->_db = $databaseFactory->createInstance();
20 $this->_maps = include $maps;
21 }
22
23 public function map($entityName, IQueryBuilder $queryBuilder)
24 {
25 $queryString = $queryBuilder->buildQuery();
26
27 $statement = $this->_db->prepare(sprintf($queryString,
28 $this->_maps[$entityName]['table']
29 ));
30
31 $statement->execute();
32 $rows = $statement->fetchAll();
33
34 $entities = array();
35
36 foreach($rows as $row)
37 {
38 $className = $this->_maps[$entityName]['class']; //the entity to instantiate and return
39 $constructors = AbstractPopulationHelper::getConstrutorArray($this->_maps, $entityName, $row, $this->_db);
40
41 if(count($constructors) == 0)
42 {
43 $class = new $className;
44 } else {
45 $r = new ReflectionClass($className);
46 $class = $r->newInstanceArgs($constructors);
47 }
48
49 $class->setId($row['id']);
50 $entities[$row['id']] = $class;
51 }
52
53 return $entities;
54 }
55
56 public function save(IDivineEntity $entity)
57 {
58 $queries = AbstractPopulationHelper::generateUpdateSaveQuery($this->_maps, $entity, $entity->getId(), $this->_db);
59 $mergeMap = array();
60
61 echo 'pre flattened: <br />';
62 echo '<pre>';
63 print_r($queries);
64 echo '</pre>';
65
66 $flattened = array();
67 $flattened_tables = array();
68 foreach($queries as $index => $query)
69 {
70 $this_table = $query['table'];
71 $this_columns = $query['columns'];
72 $flatten = true;
73 for($i = $index+1; $i<count($queries); $i++)
74 {
75 if($queries[$i]['table'] == $this_table && !in_array($queries[$i]['table'], $flattened_tables) && !isset($query['id'])) //only merge create queries, updates are fine to run multiple times
76 {
77 //XXX: This whole biz is tricky. Basically the problem is that when creating a new simfile,
78 //the datamapper spews out a bunch of create queries. When parsing a simfile for example, there can
79 //be huge redundency - it may produce 5 queries that all create the same step artist, for example.
80 //We attempt to flatten equivalent queries. Originally I was basing it purely on the table name or something,
81 //but that is not enough. In the case of steps, it ends up mergin all the steps together, so we need to
82 //check if the arrays are equal as well, which is what this does.
83 if($this_columns === $queries[$i]['columns'])
84 {
85 $this_columns = array_merge($this_columns, $queries[$i]['columns']);
86 //need to keep track of what we merged as future queries might reference the old ids.
87 $mergeMap[$i] = $index;
88 } else {
89 //we need to add these unmerged ones here as further down we record that anything to
90 //do with this table has been sorted out.
91 // $prepared = isset($queries[$i]['prepared']) ? $queries[$i]['prepared'] : null;
92 // $id = isset($queries[$i]['id']) ? $queries[$i]['id'] : null;
93 // $flattened[] = array('columns' => $queries[$i]['columns'], 'table' => $queries[$i]['table'], 'prepared' => $prepared, 'id' => $id);
94 $flatten = false;
95 }
96 }
97 }
98
99 if(!in_array($this_table, $flattened_tables))
100 {
101 if($flatten) $flattened_tables[] = $this_table;
102 $prepared = isset($query['prepared']) ? $query['prepared'] : null;
103 $id = isset($query['id']) ? $query['id'] : null;
104 $flattened[] = array('columns' => $this_columns, 'table' => $this_table, 'prepared' => $prepared, 'id' => $id);
105 }
106 }
107
108 echo 'flattened: <br />';
109 echo '<pre>';
110 print_r($flattened);
111 echo '</pre>';
112
113 $queries = array();
114
115 foreach($flattened as $info)
116 {
117 if(isset($info['id']))
118 {
119 $query = $info['prepared'];
120 $query = substr($query, 0, -2);
121 $query .= sprintf(' WHERE id=%u', $info['id']);
122 } else {
123 $query = sprintf('INSERT INTO %s (%s) VALUES (%s)',
124 $info['table'],
125 implode(', ', array_keys($info['columns'])),
126 implode(', ', $info['columns']));
127 }
128
129 $queries[] = $query;
130 }
131
132 // if($queries['TYPE'] == AbstractPopulationHelper::QUERY_TYPE_CREATE)
133 // {
134 $idMap = [];
135 foreach($queries as $index => $query)
136 {
137 $runQuery = true;
138 if (preg_match_all('/'.preg_quote('%').'(.*?)'.preg_quote('%').'/s', $query, $matches)) {
139 foreach($matches[1] as $index_ref)
140 {
141 if($index_ref != 'MAIN_QUERY_ID')
142 {
143 $index_id = str_replace('INDEX_REF_', '', $index_ref);
144 $query = str_replace('%INDEX_REF_' . $index_id . '%', $idMap['INDEX_REF_' . $index_id], $query);
145 } else {
146 $runQuery = false;
147 }
148 }
149 }
150
151 if($runQuery)
152 {
153 $statement = $this->_db->prepare($query);
154 $statement->execute();
155 //$refIndex = $index+1; This was being used as the index for idMap below. I have nfi why I was adding 1.
156 $idMap['INDEX_REF_' . $index] = $this->_db->lastInsertId();
157
158 foreach($mergeMap as $oldIndex => $mergedIndex) {
159 if($mergedIndex == $index) {
160 $idMap['INDEX_REF_' . $oldIndex] = $idMap['INDEX_REF_' . $index];
161 }
162 }
163
164 unset($queries[$index]);
165 } else {
166 //update query so that other references are resolved.
167 $queries[$index] = $query;
168 }
169 }
170
171 //at this point we have queries left that depend on the main query id
172 foreach($queries as $query)
173 {
174 $query = str_replace('%MAIN_QUERY_ID%', end($idMap), $query);
175 $statement = $this->_db->prepare($query);
176 $statement->execute();
177 }
178 //}
179
180 if(!$entity->getId()) $entity->setId(end($idMap));
181
182 return $entity;
183 }
184
185 //TODO: Implement
186 public function remove(IDivineEntity $entity) {
187 ;
188 }
189 }