~patrix-sbs/oraculum/git

« back to all changes in this revision

Viewing changes to library/components/doctrine/lib/Doctrine/Data/Import.php

  • Committer: Patrick Kaminski
  • Date: 2009-09-02 02:33:07 UTC
  • Revision ID: git-v1:943803254fca67bfb4c0374422b1b836b14dc518
Tags: v0.1a
Sending Oraculum Framework v0.1 alpha

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/*
 
3
 *  $Id: Import.php 2552 2007-09-19 19:33:00Z Jonathan.Wage $
 
4
 *
 
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
16
 *
 
17
 * This software consists of voluntary contributions made by many individuals
 
18
 * and is licensed under the LGPL. For more information, see
 
19
 * <http://www.phpdoctrine.org>.
 
20
 */
 
21
 
 
22
/**
 
23
 * Doctrine_Data_Import
 
24
 *
 
25
 * @package     Doctrine
 
26
 * @package     Data
 
27
 * @author      Jonathan H. Wage <jwage@mac.com>
 
28
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 
29
 * @link        www.phpdoctrine.org
 
30
 * @since       1.0
 
31
 * @version     $Revision: 2552 $
 
32
 */
 
33
class Doctrine_Data_Import extends Doctrine_Data
 
34
{
 
35
    /**
 
36
     * Array of imported objects for processing and saving
 
37
     *
 
38
     * @var array
 
39
     */
 
40
    protected $_importedObjects = array();
 
41
 
 
42
    /**
 
43
     * Array of the raw data parsed from yaml
 
44
     *
 
45
     * @var array
 
46
     */
 
47
    protected $_rows = array();
 
48
 
 
49
    /**
 
50
     * Optionally pass the directory/path to the yaml for importing
 
51
     *
 
52
     * @param string $directory
 
53
     * @return void
 
54
     */
 
55
    public function __construct($directory = null)
 
56
    {
 
57
        if ($directory !== null) {
 
58
            $this->setDirectory($directory);
 
59
        }
 
60
    }
 
61
 
 
62
    /**
 
63
     * Do the parsing of the yaml files and return the final parsed array
 
64
     *
 
65
     * @return array $array
 
66
     */
 
67
    public function doParsing()
 
68
    {
 
69
        $recursiveMerge = Doctrine_Manager::getInstance()->getAttribute('recursive_merge_fixtures');
 
70
        $mergeFunction = $recursiveMerge === true ? 'array_merge_recursive':'array_merge';
 
71
        $directory = $this->getDirectory();
 
72
 
 
73
        $array = array();
 
74
 
 
75
        if ($directory !== null) {
 
76
            foreach ((array) $directory as $dir) {
 
77
                $e = explode('.', $dir);
 
78
 
 
79
                // If they specified a specific yml file
 
80
                if (end($e) == 'yml') {
 
81
                    $array = $mergeFunction($array, Doctrine_Parser::load($dir, $this->getFormat()));
 
82
                // If they specified a directory
 
83
                } else if(is_dir($dir)) {
 
84
                    $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir),
 
85
                                                            RecursiveIteratorIterator::LEAVES_ONLY);
 
86
 
 
87
                    foreach ($it as $file) {
 
88
                        $e = explode('.', $file->getFileName());
 
89
                        if (in_array(end($e), $this->getFormats())) {
 
90
                            $array = $mergeFunction($array, Doctrine_Parser::load($file->getPathName(), $this->getFormat()));
 
91
                        }
 
92
                    }
 
93
                }
 
94
            }
 
95
        }
 
96
 
 
97
        return $array;
 
98
    }
 
99
 
 
100
    /**
 
101
     * Do the importing of the data parsed from the fixtures
 
102
     *
 
103
     * @return void
 
104
     */
 
105
    public function doImport($append = false)
 
106
    {
 
107
        $array = $this->doParsing();
 
108
 
 
109
        if ( ! $append) {
 
110
            $this->purge(array_reverse(array_keys($array)));
 
111
        }
 
112
 
 
113
        $this->_loadData($array);
 
114
    }
 
115
 
 
116
    /**
 
117
     * Recursively loop over all data fixtures and build the array of className rows
 
118
     *
 
119
     * @return void
 
120
     */
 
121
    protected function _buildRows($className, $data)
 
122
    {
 
123
        foreach ($data as $rowKey => $row) {
 
124
            // do the same for the row information
 
125
            $this->_rows[$className][$rowKey] = $row;
 
126
 
 
127
            foreach ((array) $row as $key => $value) {
 
128
                if (Doctrine::getTable($className)->hasRelation($key) && is_array($value)) {
 
129
                    $keys = array_keys($value);
 
130
 
 
131
                    // Skip associative arrays defining keys to relationships
 
132
                    if ( ! isset($keys[0])) {
 
133
                        $this->_buildRows(Doctrine::getTable($className)->getRelation($key)->getTable()->getOption('name'), $value);
 
134
                    }
 
135
                }
 
136
            }
 
137
        }
 
138
    }
 
139
 
 
140
    /**
 
141
     * Build the rows for nested set models
 
142
     *
 
143
     * @return void
 
144
     */
 
145
    protected function _buildNestedSetRows($className, $data)
 
146
    {
 
147
        foreach ($data as $rowKey => $row) {
 
148
            $children = isset($row['children']) ? $row['children']:array();
 
149
            unset($row['children']);
 
150
            $this->_rows[$className][$rowKey] = $row;
 
151
 
 
152
            $this->_buildNestedSetRows($className, $children);
 
153
        }
 
154
    }
 
155
 
 
156
    /**
 
157
     * Get the unsaved object for a specified row key and validate that it is the valid object class
 
158
     * for the passed record and relation name
 
159
     *
 
160
     * @param  string $rowKey
 
161
     * @param  Doctrine_Record $record
 
162
     * @param  string $relationName
 
163
     * @param  string $referringRowKey
 
164
     * @return Doctrine_Record
 
165
     * @throws Doctrine_Data_Exception
 
166
     */
 
167
    protected function _getImportedObject($rowKey, Doctrine_Record $record, $relationName, $referringRowKey)
 
168
    {
 
169
        if ( ! isset($this->_importedObjects[$rowKey])) {
 
170
            throw new Doctrine_Data_Exception(
 
171
                sprintf('Invalid row key specified: %s, referred to in %s', $rowKey, $referringRowKey)
 
172
            );
 
173
        }
 
174
 
 
175
        $relatedRowKeyObject = $this->_importedObjects[$rowKey];
 
176
 
 
177
        $relation = $record->getTable()->getRelation($relationName);
 
178
        if ($relation->getClass() !== get_class($relatedRowKeyObject)) {
 
179
            if ( ! is_subclass_of($relatedRowKeyObject, $relation->getClass())) {
 
180
                throw new Doctrine_Data_Exception(sprintf(
 
181
                    'Class referred to in "%s" is expected to be "%s" and "%s" was given',
 
182
                    $referringRowKey, $relation->getClass(), get_class($relatedRowKeyObject)));
 
183
            }
 
184
        }
 
185
 
 
186
        return $relatedRowKeyObject;
 
187
    }
 
188
 
 
189
    /**
 
190
     * Process a row and make all the appropriate relations between the imported data
 
191
     *
 
192
     * @param string $rowKey
 
193
     * @param string $row
 
194
     * @return void
 
195
     */
 
196
    protected function _processRow($rowKey, $row)
 
197
    {
 
198
        $obj = $this->_importedObjects[$rowKey];
 
199
 
 
200
        foreach ((array) $row as $key => $value) {
 
201
            if (method_exists($obj, 'set' . Doctrine_Inflector::classify($key))) {
 
202
                $func = 'set' . Doctrine_Inflector::classify($key);
 
203
                $obj->$func($value);
 
204
            } else if ($obj->getTable()->hasField($key)) {
 
205
                if ($obj->getTable()->getTypeOf($key) == 'object') {
 
206
                    $value = unserialize($value);
 
207
                }
 
208
                $obj->set($key, $value);
 
209
            } else if ($obj->getTable()->hasRelation($key)) {
 
210
                if (is_array($value)) {
 
211
                    if (isset($value[0]) && ! is_array($value[0])) {
 
212
                        foreach ($value as $link) {
 
213
                            if ($obj->getTable()->getRelation($key)->getType() === Doctrine_Relation::ONE) {
 
214
                                $obj->set($key, $this->_getImportedObject($link, $obj, $key, $rowKey));
 
215
                            } else if ($obj->getTable()->getRelation($key)->getType() === Doctrine_Relation::MANY) {
 
216
                                $relation = $obj->$key;
 
217
 
 
218
                                $relation[] = $this->_getImportedObject($link, $obj, $key, $rowKey);
 
219
                            }
 
220
                        }
 
221
                    } else {
 
222
                        $obj->$key->fromArray($value);
 
223
                    }
 
224
                } else {
 
225
                    $obj->set($key, $this->_getImportedObject($value, $obj, $key, $rowKey));
 
226
                }
 
227
            } else {
 
228
                try {
 
229
                    $obj->$key = $value;
 
230
                } catch (Exception $e) {
 
231
                    // used for Doctrine plugin methods (Doctrine_Template)
 
232
                    if (is_callable(array($obj, 'set' . Doctrine_Inflector::classify($key)))) {
 
233
                        $func = 'set' . Doctrine_Inflector::classify($key);
 
234
                        $obj->$func($value);
 
235
                    } else {
 
236
                        throw new Doctrine_Data_Exception('Invalid fixture element "'. $key . '" under "' . $rowKey . '"');
 
237
                    }
 
238
                }
 
239
            }
 
240
        }
 
241
    }
 
242
 
 
243
   /**
 
244
    * NestedSet fixtures may come in a 'natural' format with nested children listed under a 'children'
 
245
    * key or in a raw, non-nested format with lft/rgt values.
 
246
    *
 
247
    * This method returns true if the given $data is a nested set in 'natural' form.
 
248
    *
 
249
    * @param $className
 
250
    * @param $data
 
251
    * @return boolean
 
252
    */
 
253
    protected function _hasNaturalNestedSetFormat($className, array &$data)
 
254
    {
 
255
        if (Doctrine::getTable($className)->isTree()) {
 
256
            if (isset($data['NestedSet']) && $data['NestedSet'] == true) {
 
257
                unset($data['NestedSet']);
 
258
                return true;
 
259
            } else {
 
260
                $first = current($data);
 
261
                return array_key_exists('children', $first);
 
262
            }
 
263
        } else {
 
264
            return false;
 
265
        }
 
266
    }
 
267
 
 
268
    /**
 
269
     * Perform the loading of the data from the passed array
 
270
     *
 
271
     * @param string $array
 
272
     * @return void
 
273
     */
 
274
    protected function _loadData(array $array)
 
275
    {
 
276
        $nestedSets = array();
 
277
 
 
278
        $specifiedModels = $this->getModels();
 
279
        $rows = array();
 
280
 
 
281
        foreach ($array as $className => $data) {
 
282
            if ( ! empty($specifiedModels) && !in_array($className, $specifiedModels)) {
 
283
                continue;
 
284
            }
 
285
 
 
286
            // if loaded data is a nested set in natural format, process through _buildNestedSetRows.
 
287
            // 'raw' nested sets and all other models are processed through _buildRows.
 
288
            if ($this->_hasNaturalNestedSetFormat($className, $data)) {
 
289
                $nestedSets[$className][] = $data;
 
290
                $this->_buildNestedSetRows($className, $data);
 
291
            } else {
 
292
                $this->_buildRows($className, $data);
 
293
            }
 
294
        }
 
295
 
 
296
        $buildRows = array();
 
297
        foreach ($this->_rows as $className => $classRows) {
 
298
            foreach ($classRows as $rowKey => $row) {
 
299
                $buildRows[$rowKey] = $row;
 
300
                $this->_importedObjects[$rowKey] = new $className();
 
301
                $this->_importedObjects[$rowKey]->state('TDIRTY');
 
302
            }
 
303
        }
 
304
 
 
305
        foreach($buildRows as $rowKey => $row) {
 
306
            $this->_processRow($rowKey, $row);
 
307
        }
 
308
 
 
309
        // save natural nested set fixture data and unset from _importedObjects
 
310
        foreach ($nestedSets as $className => $sets) {
 
311
            foreach ($sets as $data) {
 
312
                $this->_loadNestedSetData($className, $data);
 
313
            }
 
314
        }
 
315
 
 
316
        $objects = array();
 
317
        foreach ($this->_importedObjects as $object) {
 
318
            $className = get_class($object);
 
319
            $objects[$className] = $className;
 
320
        }
 
321
 
 
322
        $manager = Doctrine_Manager::getInstance();
 
323
        foreach ($manager as $connection) {
 
324
            $tree = $connection->unitOfWork->buildFlushTree($objects);
 
325
 
 
326
            foreach ($tree as $model) {
 
327
                foreach ($this->_importedObjects as $obj) {
 
328
 
 
329
                    if ($obj instanceof $model) {
 
330
                        $obj->save();
 
331
                    }
 
332
                }
 
333
            }
 
334
        }
 
335
 
 
336
    }
 
337
 
 
338
    /**
 
339
     * Load nested set data for models with nested set enabled
 
340
     *
 
341
     * @param string $model
 
342
     * @param string $nestedSetData
 
343
     * @param string $parent
 
344
     * @return void
 
345
     */
 
346
    protected function _loadNestedSetData($model, $nestedSetData, $parent = null)
 
347
    {
 
348
        foreach($nestedSetData AS $rowKey => $nestedSet) {
 
349
            $children = array();
 
350
            $data  = array();
 
351
 
 
352
            if (array_key_exists('children', $nestedSet)) {
 
353
                $children = (array) $nestedSet['children'];
 
354
                $children = array_reverse($children, true);
 
355
                unset($nestedSet['children']);
 
356
            }
 
357
 
 
358
            $record = $this->_importedObjects[$rowKey];
 
359
            // remove this nested set from _importedObjects so it's not processed in the save routine for normal objects
 
360
            unset($this->_importedObjects[$rowKey]);
 
361
 
 
362
            if( ! $parent) {
 
363
                $record->save(); // save, so that createRoot can do: root id = id
 
364
                Doctrine::getTable($model)->getTree()->createRoot($record);
 
365
            } else {
 
366
                $parent->getNode()->addChild($record);
 
367
            }
 
368
 
 
369
            if (is_array($children) AND !empty($children)) {
 
370
                $this->_loadNestedSetData($model, $children, $record);
 
371
            }
 
372
        }
 
373
    }
 
374
}