3 namespace DataAccess\DataMapper
;
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 DataAccess\DataMapper\LazyLoadedEntities
;
13 class DataMapper
implements IDataMapper
18 public function __construct($maps, IDatabaseFactory
$databaseFactory)
20 $this->_db
= $databaseFactory->createInstance();
21 $this->_maps
= include $maps;
24 public function map($entityName, IQueryBuilder
$queryBuilder)
26 $queryString = $queryBuilder->buildQuery();
27 $statement = $this->_db
->prepare(sprintf($queryString,
28 $this->_maps
[$entityName]['table']
31 $statement->execute();
32 $rows = $statement->fetchAll();
39 return new LazyLoadedEntities($rows, $entityName, $this->_maps
, $this->_db
);
42 foreach($rows as $row)
44 $className = $this->_maps
[$entityName]['class']; //the entity to instantiate and return
45 $constructors = AbstractPopulationHelper
::getConstrutorArray($this->_maps
, $entityName, $row, $this->_db
);
47 if(count($constructors) == 0)
49 $class = new $className;
51 $r = new ReflectionClass($className);
52 $class = $r->newInstanceArgs($constructors);
55 $class->setId($row['id']);
56 $entities[$row['id']] = $class;
62 public function save(IDivineEntity
$entity)
64 $queries = AbstractPopulationHelper
::generateUpdateSaveQuery($this->_maps
, $entity, $entity->getId(), $this->_db
);
68 foreach($queries as $index => $query)
70 $this_table = $query['table'];
71 $this_columns = $query['columns'];
74 for($i = $index+
1; $i<count($queries); $i++
)
77 $queries[$i]['table'] == $this_table &&
78 !array_key_exists($i, $mergeMap) &&
79 !isset($query['id'])) //only merge create queries, updates are fine to run multiple times
81 //XXX: This whole biz is tricky. Basically the problem is that when creating a new simfile,
82 //the datamapper spews out a bunch of create queries. When parsing a simfile for example, there can
83 //be huge redundency - it may produce 5 queries that all create the same step artist, for example.
84 //We attempt to flatten equivalent queries. Originally I was basing it purely on the table name or something,
85 //but that is not enough. In the case of steps, it ends up mergin all the steps together, so we need to
86 //check if the arrays are equal as well, which is what this does.
87 if($this_columns === $queries[$i]['columns'])
89 //need to keep track of what we merged as future queries might reference the old ids.
90 $mergeMap[$i] = $index;
93 //XXX: Another thing that might happen is we have to create queries running on the same table, but with unique columns.
94 //In this case, we can take the columns of one and put it into the other. Otherwise we create two records when we really
95 //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,
96 //and then _another_ to add firstname, lastname and user_id. It should really all be done in one query.
98 //Make sure both queries are for the same table, and the both relate back to the main query
99 if($this_table == $queries[$i]['table'] && in_array('%MAIN_QUERY_ID%', $this_columns) && in_array('%MAIN_QUERY_ID%', $queries[$i]['columns']))
101 $this_column_names = array_keys($this_columns);
102 $other_column_names = array_keys($queries[$i]['columns']);
104 foreach($this_column_names as $column_name)
106 if($this_columns[$column_name] != '%MAIN_QUERY_ID%' && in_array($column_name, $other_column_names))
114 $this_columns = array_merge($this_columns, $queries[$i]['columns']);
115 $mergeMap[$i] = $index;
121 if(!array_key_exists($index, $mergeMap)) {
122 $prepared = isset($query['prepared']) ?
$query['prepared'] : null
;
123 $id = isset($query['id']) ?
$query['id'] : null
;
125 $flattened[$index] = array(
126 'columns' => $this_columns,
127 'table' => $this_table,
128 'prepared' => $prepared,
136 foreach($flattened as $index => $info)
138 if(isset($info['id']))
140 $query = $info['prepared'];
141 $query = substr($query, 0, -2);
142 $query .= sprintf(' WHERE id=%u', $info['id']);
144 $query = sprintf('INSERT INTO %s (%s) VALUES (%s)',
146 implode(', ', array_keys($info['columns'])),
147 implode(', ', $info['columns']));
150 $queries[$index] = $query;
153 // if($queries['TYPE'] == AbstractPopulationHelper::QUERY_TYPE_CREATE)
156 foreach($queries as $index => $query)
159 if (preg_match_all('/'.preg_quote('%').'(.*?)'.preg_quote('%').'/s', $query, $matches)) {
160 foreach($matches[1] as $index_ref)
162 if($index_ref != 'MAIN_QUERY_ID')
164 $index_id = str_replace('INDEX_REF_', '', $index_ref);
165 $query = str_replace('%INDEX_REF_' . $index_id . '%', $idMap['INDEX_REF_' . $index_id], $query);
174 $statement = $this->_db
->prepare($query);
175 $statement->execute();
176 //$refIndex = $index+1; This was being used as the index for idMap below. I have nfi why I was adding 1.
177 $idMap['INDEX_REF_' . $index] = $this->_db
->lastInsertId();
179 foreach($mergeMap as $oldIndex => $mergedIndex) {
180 if($mergedIndex == $index) {
181 $idMap['INDEX_REF_' . $oldIndex] = $idMap['INDEX_REF_' . $index];
185 unset($queries[$index]);
187 //update query so that other references are resolved.
188 $queries[$index] = $query;
192 //at this point we have queries left that depend on the main query id
193 foreach($queries as $query)
195 $query = str_replace('%MAIN_QUERY_ID%', end($idMap), $query);
196 $statement = $this->_db
->prepare($query);
197 $statement->execute();
201 if(!$entity->getId()) $entity->setId(end($idMap));
207 public function remove(IDivineEntity
$entity) {