Development

/trunk/lib/addon/propel/sfPropelData.class.php

You must first sign up to be able to contribute.

root/trunk/lib/addon/propel/sfPropelData.class.php

Revision 4905, 10.1 kB (checked in by fabien, 3 years ago)

fixed dumping Propel data to multiple files

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Rev Date
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * This class is the Propel implementation of sfData.  It interacts with the data source
13  * and loads data.
14  *
15  * @package    symfony
16  * @subpackage addon
17  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18  * @version    SVN: $Id$
19  */
20 class sfPropelData extends sfData
21 {
22   protected
23     $maps = array();
24
25   // symfony load-data (file|dir)
26   /**
27    * Loads data from a file or directory into a Propel data source
28    *
29    * @param mixed A file or directory path
30    * @param string The Propel connection name, default 'propel'
31    *
32    * @throws Exception If the database throws an error, rollback transaction and rethrows exception
33    */
34   public function loadData($directory_or_file = null, $connectionName = 'propel')
35   {
36     $fixture_files = $this->getFiles($directory_or_file);
37
38     // wrap all database operations in a single transaction
39     $con = Propel::getConnection($connectionName);
40     try
41     {
42       $con->begin();
43
44       $this->doDeleteCurrentData($fixture_files);
45
46       $this->doLoadData($fixture_files);
47
48       $con->commit();
49     }
50     catch (Exception $e)
51     {
52       $con->rollback();
53       throw $e;
54     }
55   }
56
57   /**
58    * Implements the abstract loadDataFromArray method and loads the data using the generated data model.
59    *
60    * @param array The data to be loaded into the data source
61    *
62    * @throws Exception If data is unnamed.
63    * @throws sfException If an object defined in the model does not exist in the data
64    * @throws sfException If a column that does not exist is referenced
65    */
66   public function loadDataFromArray($data)
67   {
68     if ($data === null)
69     {
70       // no data
71       return;
72     }
73
74     foreach ($data as $class => $datas)
75     {
76       $class = trim($class);
77
78       $peer_class = $class.'Peer';
79
80       // load map class
81       $this->loadMapBuilder($class);
82
83       $tableMap = $this->maps[$class]->getDatabaseMap()->getTable(constant($peer_class.'::TABLE_NAME'));
84
85       $column_names = call_user_func_array(array($peer_class, 'getFieldNames'), array(BasePeer::TYPE_FIELDNAME));
86
87       // iterate through datas for this class
88       // might have been empty just for force a table to be emptied on import
89       if (!is_array($datas))
90       {
91         continue;
92       }
93
94       foreach ($datas as $key => $data)
95       {
96         // create a new entry in the database
97         $obj = new $class();
98
99         if (!is_array($data))
100         {
101           throw new Exception('You must give a name for each fixture data entry');
102         }
103
104         foreach ($data as $name => $value)
105         {
106           // foreign key?
107           try
108           {
109             $column = $tableMap->getColumn($name);
110             if ($column->isForeignKey() && !is_null($value))
111             {
112               $relatedTable = $this->maps[$class]->getDatabaseMap()->getTable($column->getRelatedTableName());
113               if (!isset($this->object_references[$relatedTable->getPhpName().'_'.$value]))
114               {
115                 throw new sfException(sprintf('The object "%s" from class "%s" is not defined in your data file.', $value, $relatedTable->getPhpName()));
116               }
117               $value = $this->object_references[$relatedTable->getPhpName().'_'.$value];
118             }
119           }
120           catch (PropelException $e)
121           {
122           }
123
124           $pos = array_search($name, $column_names);
125           $method = 'set'.sfInflector::camelize($name);
126           if ($pos)
127           {
128             $obj->setByPosition($pos, $value);
129           }
130           else if (is_callable(array($obj, $method)))
131           {
132             $obj->$method($value);
133           }
134           else
135           {
136             throw new sfException(sprintf('Column "%s" does not exist for class "%s"', $name, $class));
137           }
138         }
139         $obj->save();
140
141         // save the id for future reference
142         if (method_exists($obj, 'getPrimaryKey'))
143         {
144           $this->object_references[$class.'_'.$key] = $obj->getPrimaryKey();
145         }
146       }
147     }
148   }
149
150   /**
151    * Clears existing data from the data source by reading the fixture files
152    * and deleting the existing data for only those classes that are mentioned
153    * in the fixtures.
154    *
155    * @param array The list of YAML files.
156    *
157    * @throws sfException If a class mentioned in a fixture can not be found
158    */
159   protected function doDeleteCurrentData($fixture_files)
160   {
161     // delete all current datas in database
162     if (!$this->deleteCurrentData)
163     {
164       return;
165     }
166
167     rsort($fixture_files);
168     foreach ($fixture_files as $fixture_file)
169     {
170       $data = sfYaml::load($fixture_file);
171
172       if ($data === null)
173       {
174         // no data
175         continue;
176       }
177
178       $classes = array_keys($data);
179       krsort($classes);
180       foreach ($classes as $class)
181       {
182         $peer_class = trim($class.'Peer');
183
184         if (!$classPath = sfAutoload::getClassPath($peer_class))
185         {
186           throw new sfException(sprintf('Unable to find path for class "%s".', $peer_class));
187         }
188
189         require_once($classPath);
190
191         call_user_func(array($peer_class, 'doDeleteAll'));
192       }
193     }
194   }
195
196   /**
197    * Loads the mappings for the classes
198    *
199    * @param string The model class name
200    *
201    * @throws sfException If the class cannot be found
202    */
203   protected function loadMapBuilder($class)
204   {
205     $mapBuilderClass = $class.'MapBuilder';
206     if (!isset($this->maps[$class]))
207     {
208       if (!$classPath = sfAutoload::getClassPath($mapBuilderClass))
209       {
210         throw new sfException(sprintf('Unable to find path for class "%s".', $mapBuilderClass));
211       }
212
213       require_once($classPath);
214       $this->maps[$class] = new $mapBuilderClass();
215       $this->maps[$class]->doBuild();
216     }
217   }
218
219   /**
220    * Dumps data to fixture from one or more tables.
221    *
222    * @param string directory or file to dump to
223    * @param mixed  name or names of tables to dump (or all to dump all tables)
224    * @param string connection name
225    */
226   public function dumpData($directory_or_file = null, $tables = 'all', $connectionName = 'propel')
227   {
228     $sameFile = true;
229     if (is_dir($directory_or_file))
230     {
231       // multi files
232       $sameFile = false;
233     }
234     else
235     {
236       // same file
237       // delete file
238     }
239
240     $con = Propel::getConnection($connectionName);
241
242     // get tables
243     if ('all' === $tables || is_null($tables))
244     {
245       // load all map builder classes
246       $files = sfFinder::type('file')->name('*MapBuilder.php')->in(sfLoader::getModelDirs());
247       foreach ($files as $file)
248       {
249         $mapBuilderClass = basename($file, '.php');
250         $map = new $mapBuilderClass();
251         $map->doBuild();
252       }
253
254       $dbMap = Propel::getDatabaseMap($connectionName);
255       $tables = array();
256       foreach ($dbMap->getTables() as $table)
257       {
258         $tables[] = $table->getPhpName();
259       }
260     }
261     else if (!is_array($tables))
262     {
263       $tables = array($tables);
264     }
265
266     $dumpData = array();
267
268     // load map classes
269     array_walk($tables, array($this, 'loadMapBuilder'));
270
271     $tables = $this->fixOrderingOfForeignKeyData($tables);
272
273     foreach ($tables as $tableName)
274     {
275       $tableMap = $this->maps[$tableName]->getDatabaseMap()->getTable(constant($tableName.'Peer::TABLE_NAME'));
276
277       // get db info
278       $rs = $con->executeQuery('SELECT * FROM '.constant($tableName.'Peer::TABLE_NAME'));
279
280       while ($rs->next())
281       {
282         $pk = $tableName;
283         $values = array();
284         foreach ($tableMap->getColumns() as $column)
285         {
286           $col = strtolower($column->getColumnName());
287           if ($column->isPrimaryKey())
288           {
289             $pk .= '_'.$rs->get($col);
290           }
291           else if ($column->isForeignKey())
292           {
293             $relatedTable = $this->maps[$tableName]->getDatabaseMap()->getTable($column->getRelatedTableName());
294
295             $values[$col] = $relatedTable->getPhpName().'_'.$rs->get($col);
296           }
297           else
298           {
299             $values[$col] = $rs->get($col);
300           }
301         }
302
303         if (!isset($dumpData[$tableName]))
304         {
305           $dumpData[$tableName] = array();
306         }
307
308         $dumpData[$tableName][$pk] = $values;
309       }
310     }
311
312     // save to file(s)
313     if ($sameFile)
314     {
315       file_put_contents($directory_or_file, Spyc::YAMLDump($dumpData));
316     }
317     else
318     {
319       $i = 0;
320       foreach ($tables as $tableName)
321       {
322         if (!isset($dumpData[$tableName]))
323         {
324           continue;
325         }
326
327         file_put_contents(sprintf("%s/%03d-%s.yml", $directory_or_file, ++$i, $tableName), Spyc::YAMLDump(array($tableName => $dumpData[$tableName]));
328       }
329     }
330   }
331
332   /**
333    * Fixes the ordering of foreign key data, by outputting data a foreign key depends on before the table with the foreign key.
334    *
335    * @param array The array with the class names.
336    */
337   public function fixOrderingOfForeignKeyData($classes)
338   {
339     // reordering classes to take foreign keys into account
340     for ($i = 0, $count = count($classes); $i < $count; $i++)
341     {
342       $class = $classes[$i];
343       $tableMap = $this->maps[$class]->getDatabaseMap()->getTable(constant($class.'Peer::TABLE_NAME'));
344       foreach ($tableMap->getColumns() as $column)
345       {
346         if ($column->isForeignKey())
347         {
348           $relatedTable = $this->maps[$class]->getDatabaseMap()->getTable($column->getRelatedTableName());
349           $relatedTablePos = array_search($relatedTable->getPhpName(), $classes);
350
351           // check if relatedTable is after the current table
352           if ($relatedTablePos > $i)
353           {
354             // move related table 1 position before current table
355             $classes = array_merge(
356               array_slice($classes, 0, $i),
357               array($classes[$relatedTablePos]),
358               array_slice($classes, $i, $relatedTablePos - $i),
359               array_slice($classes, $relatedTablePos + 1)
360             );
361
362             // we have moved a table, so let's see if we are done
363             return $this->fixOrderingOfForeignKeyData($classes);
364           }
365         }
366       }
367     }
368
369     return $classes;
370   }
371 }
372
Note: See TracBrowser for help on using the browser.

The Sensio Labs Network

Since 1998, Sensio Labs has been promoting the Open-Source software movement by providing quality web application development, training, consulting.
Sensio Labs also supports several large Open-Source projects.