~patrix-sbs/oraculum/git

« back to all changes in this revision

Viewing changes to library/components/doctrine/lib/Doctrine/Collection.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: Collection.php 5049 2008-10-04 20:51:17Z jwage $
 
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_Collection
 
24
 * Collection of Doctrine_Record objects.
 
25
 *
 
26
 * @package     Doctrine
 
27
 * @subpackage  Collection
 
28
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 
29
 * @link        www.phpdoctrine.org
 
30
 * @since       1.0
 
31
 * @version     $Revision: 5049 $
 
32
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 
33
 */
 
34
class Doctrine_Collection extends Doctrine_Access implements Countable, IteratorAggregate, Serializable
 
35
{
 
36
    /**
 
37
     * @var array $data                     an array containing the records of this collection
 
38
     */
 
39
    protected $data = array();
 
40
 
 
41
    /**
 
42
     * @var Doctrine_Table $table           each collection has only records of specified table
 
43
     */
 
44
    protected $_table;
 
45
 
 
46
    /**
 
47
     * @var array $_snapshot                a snapshot of the fetched data
 
48
     */
 
49
    protected $_snapshot = array();
 
50
 
 
51
    /**
 
52
     * @var Doctrine_Record $reference      collection can belong to a record
 
53
     */
 
54
    protected $reference;
 
55
 
 
56
    /**
 
57
     * @var string $referenceField         the reference field of the collection
 
58
     */
 
59
    protected $referenceField;
 
60
 
 
61
    /**
 
62
     * @var Doctrine_Relation               the record this collection is related to, if any
 
63
     */
 
64
    protected $relation;
 
65
 
 
66
    /**
 
67
     * @var string $keyColumn               the name of the column that is used for collection key mapping
 
68
     */
 
69
    protected $keyColumn;
 
70
 
 
71
    /**
 
72
     * @var Doctrine_Null $null             used for extremely fast null value testing
 
73
     */
 
74
    protected static $null;
 
75
 
 
76
    /**
 
77
     * constructor
 
78
     *
 
79
     * @param Doctrine_Table|string $table
 
80
     */
 
81
    public function __construct($table, $keyColumn = null)
 
82
    {
 
83
        if ( ! ($table instanceof Doctrine_Table)) {
 
84
            $table = Doctrine::getTable($table);
 
85
        }
 
86
 
 
87
        $this->_table = $table;
 
88
 
 
89
        if ($keyColumn === null) {
 
90
            $keyColumn = $table->getBoundQueryPart('indexBy');
 
91
        }
 
92
 
 
93
        if ($keyColumn === null) {
 
94
                $keyColumn = $table->getAttribute(Doctrine::ATTR_COLL_KEY);
 
95
        }
 
96
 
 
97
        if ($keyColumn !== null) {
 
98
            $this->keyColumn = $keyColumn;
 
99
        }
 
100
    }
 
101
 
 
102
    /**
 
103
     * Initializes the null object for this collection
 
104
     *
 
105
     * @return void
 
106
     */
 
107
    public static function initNullObject(Doctrine_Null $null)
 
108
    {
 
109
        self::$null = $null;
 
110
    }
 
111
 
 
112
    /**
 
113
     * Get the table this collection belongs to
 
114
     *
 
115
     * @return Doctrine_Table
 
116
     */
 
117
    public function getTable()
 
118
    {
 
119
        return $this->_table;
 
120
    }
 
121
 
 
122
    /**
 
123
     * Set the data for the Doctrin_Collection instance
 
124
     *
 
125
     * @param array $data
 
126
     * @return Doctrine_Collection
 
127
     */
 
128
    public function setData(array $data) 
 
129
    {
 
130
        $this->data = $data;
 
131
    }
 
132
 
 
133
    /**
 
134
     * This method is automatically called when this Doctrine_Collection is serialized
 
135
     *
 
136
     * @return array
 
137
     */
 
138
    public function serialize()
 
139
    {
 
140
        $vars = get_object_vars($this);
 
141
 
 
142
        unset($vars['reference']);
 
143
        unset($vars['reference_field']);
 
144
        unset($vars['relation']);
 
145
        unset($vars['expandable']);
 
146
        unset($vars['expanded']);
 
147
        unset($vars['generator']);
 
148
 
 
149
        $vars['_table'] = $vars['_table']->getComponentName();
 
150
 
 
151
        return serialize($vars);
 
152
    }
 
153
 
 
154
    /**
 
155
     * This method is automatically called everytime a Doctrine_Collection object is unserialized
 
156
     *
 
157
     * @return void
 
158
     */
 
159
    public function unserialize($serialized)
 
160
    {
 
161
        $manager    = Doctrine_Manager::getInstance();
 
162
        $connection    = $manager->getCurrentConnection();
 
163
 
 
164
        $array = unserialize($serialized);
 
165
 
 
166
        foreach ($array as $name => $values) {
 
167
            $this->$name = $values;
 
168
        }
 
169
 
 
170
        $this->_table = $connection->getTable($this->_table);
 
171
 
 
172
        $keyColumn = isset($array['keyColumn']) ? $array['keyColumn'] : null;
 
173
        if ($keyColumn === null) {
 
174
            $keyColumn = $this->_table->getBoundQueryPart('indexBy');
 
175
        }
 
176
 
 
177
        if ($keyColumn !== null) {
 
178
            $this->keyColumn = $keyColumn;
 
179
        }
 
180
    }
 
181
 
 
182
    /**
 
183
     * Sets the key column for this collection
 
184
     *
 
185
     * @param string $column
 
186
     * @return Doctrine_Collection $this
 
187
     */
 
188
    public function setKeyColumn($column)
 
189
    {
 
190
        $this->keyColumn = $column;
 
191
        
 
192
        return $this;
 
193
    }
 
194
 
 
195
    /**
 
196
     * Get the name of the key column
 
197
     *
 
198
     * @return string
 
199
     */
 
200
    public function getKeyColumn()
 
201
    {
 
202
        return $this->keyColumn;
 
203
    }
 
204
 
 
205
    /**
 
206
     * Get all the records as an array
 
207
     *
 
208
     * @return array
 
209
     */
 
210
    public function getData()
 
211
    {
 
212
        return $this->data;
 
213
    }
 
214
 
 
215
    /**
 
216
     * Get the first record in the collection
 
217
     *
 
218
     * @return mixed
 
219
     */
 
220
    public function getFirst()
 
221
    {
 
222
        return reset($this->data);
 
223
    }
 
224
 
 
225
    /**
 
226
     * Get the last record in the collection
 
227
     *
 
228
     * @return mixed
 
229
     */
 
230
    public function getLast()
 
231
    {
 
232
        return end($this->data);
 
233
    }
 
234
 
 
235
    /**
 
236
     * Get the last record in the collection
 
237
     *
 
238
     * @return mixed
 
239
     */
 
240
    public function end()
 
241
    {
 
242
        return end($this->data);
 
243
    }
 
244
 
 
245
    /**
 
246
     * Get the current key
 
247
     *
 
248
     * @return mixed
 
249
     */
 
250
    public function key()
 
251
    {
 
252
        return key($this->data);
 
253
    }
 
254
 
 
255
    /**
 
256
     * Sets a reference pointer
 
257
     *
 
258
     * @return void
 
259
     */
 
260
    public function setReference(Doctrine_Record $record, Doctrine_Relation $relation)
 
261
    {
 
262
        $this->reference = $record;
 
263
        $this->relation  = $relation;
 
264
 
 
265
        if ($relation instanceof Doctrine_Relation_ForeignKey || 
 
266
                $relation instanceof Doctrine_Relation_LocalKey) {
 
267
            $this->referenceField = $relation->getForeignFieldName();
 
268
 
 
269
            $value = $record->get($relation->getLocalFieldName());
 
270
 
 
271
            foreach ($this->data as $record) {
 
272
                if ($value !== null) {
 
273
                    $record->set($this->referenceField, $value, false);
 
274
                } else {
 
275
                    $record->set($this->referenceField, $this->reference, false);
 
276
                }
 
277
            }
 
278
        } elseif ($relation instanceof Doctrine_Relation_Association) {
 
279
 
 
280
        }
 
281
    }
 
282
 
 
283
    /**
 
284
     * Get reference to Doctrine_Record instance
 
285
     *
 
286
     * @return Doctrine_Record $reference
 
287
     */
 
288
    public function getReference()
 
289
    {
 
290
        return $this->reference;
 
291
    }
 
292
 
 
293
    /**
 
294
     * Removes a specified collection element
 
295
     *
 
296
     * @param mixed $key
 
297
     * @return boolean
 
298
     */
 
299
    public function remove($key)
 
300
    {
 
301
        $removed = $this->data[$key];
 
302
 
 
303
        unset($this->data[$key]);
 
304
        return $removed;
 
305
    }
 
306
 
 
307
    /**
 
308
     * Whether or not this collection contains a specified element
 
309
     *
 
310
     * @param mixed $key                    the key of the element
 
311
     * @return boolean
 
312
     */
 
313
    public function contains($key)
 
314
    {
 
315
        return isset($this->data[$key]);
 
316
    }
 
317
 
 
318
    /**
 
319
     * Search a Doctrine_Record instance
 
320
     *
 
321
     * @param string $Doctrine_Record 
 
322
     * @return void
 
323
     */
 
324
    public function search(Doctrine_Record $record)
 
325
    {
 
326
        return array_search($record, $this->data, true);
 
327
    }
 
328
 
 
329
    /**
 
330
     * Gets a record for given key
 
331
     *
 
332
     * There are two special cases:
 
333
     *
 
334
     * 1. if null is given as a key a new record is created and attached
 
335
     * at the end of the collection
 
336
     *
 
337
     * 2. if given key does not exist, then a new record is create and attached
 
338
     * to the given key
 
339
     *
 
340
     * Collection also maps referential information to newly created records
 
341
     *
 
342
     * @param mixed $key                    the key of the element
 
343
     * @return Doctrine_Record              return a specified record
 
344
     */
 
345
    public function get($key)
 
346
    {
 
347
        if ( ! isset($this->data[$key])) {
 
348
            $record = $this->_table->create();
 
349
 
 
350
            if (isset($this->referenceField)) {
 
351
                $value = $this->reference->get($this->relation->getLocalFieldName());
 
352
 
 
353
                if ($value !== null) {
 
354
                    $record->set($this->referenceField, $value, false);
 
355
                } else {
 
356
                    $record->set($this->referenceField, $this->reference, false);
 
357
                }
 
358
            }
 
359
            if ($key === null) {
 
360
                $this->data[] = $record;
 
361
            } else {
 
362
                $this->data[$key] = $record;            
 
363
            }
 
364
 
 
365
            if (isset($this->keyColumn)) {
 
366
                $record->set($this->keyColumn, $key);
 
367
            }
 
368
 
 
369
            return $record;
 
370
        }
 
371
 
 
372
        return $this->data[$key];
 
373
    }
 
374
 
 
375
    /**
 
376
     * Get array of primary keys for all the records in the collection
 
377
     *
 
378
     * @return array                an array containing all primary keys
 
379
     */
 
380
    public function getPrimaryKeys()
 
381
    {
 
382
        $list = array();
 
383
        $name = $this->_table->getIdentifier();
 
384
 
 
385
        foreach ($this->data as $record) {
 
386
            if (is_array($record) && isset($record[$name])) {
 
387
                $list[] = $record[$name];
 
388
            } else {
 
389
                $list[] = $record->getIncremented();
 
390
            }
 
391
        }
 
392
        return $list;
 
393
    }
 
394
 
 
395
    /**
 
396
     * Get all keys of the data in the collection
 
397
     *
 
398
     * @return array
 
399
     */
 
400
    public function getKeys()
 
401
    {
 
402
        return array_keys($this->data);
 
403
    }
 
404
 
 
405
    /**
 
406
     * Gets the number of records in this collection
 
407
     * This class implements interface countable
 
408
     *
 
409
     * @return integer
 
410
     */
 
411
    public function count()
 
412
    {
 
413
        return count($this->data);
 
414
    }
 
415
 
 
416
    /**
 
417
     * Set a Doctrine_Record instance to the collection
 
418
     *
 
419
     * @param integer $key
 
420
     * @param Doctrine_Record $record
 
421
     * @return void
 
422
     */
 
423
    public function set($key, $record)
 
424
    {
 
425
        if (isset($this->referenceField)) {
 
426
            $record->set($this->referenceField, $this->reference, false);
 
427
        }
 
428
 
 
429
        $this->data[$key] = $record;
 
430
    }
 
431
 
 
432
    /**
 
433
     * Adds a record to collection
 
434
     *
 
435
     * @param Doctrine_Record $record              record to be added
 
436
     * @param string $key                          optional key for the record
 
437
     * @return boolean
 
438
     */
 
439
    public function add($record, $key = null)
 
440
    {
 
441
        if (isset($this->referenceField)) {
 
442
            $value = $this->reference->get($this->relation->getLocalFieldName());
 
443
            if ($value !== null) {
 
444
                $record->set($this->referenceField, $value, false);
 
445
            } else {
 
446
                $record->set($this->referenceField, $this->reference, false);
 
447
            }
 
448
            $relations = $this->relation['table']->getRelations();
 
449
            foreach ($relations as $relation) {
 
450
                if ($this->relation['class'] == $relation['localTable']->getOption('name') && $relation->getLocal() == $this->relation->getForeignFieldName()) {
 
451
                    $record->$relation['alias'] = $this->reference;
 
452
                    break;
 
453
                }
 
454
            }
 
455
        }
 
456
        /**
 
457
         * for some weird reason in_array cannot be used here (php bug ?)
 
458
         *
 
459
         * if used it results in fatal error : [ nesting level too deep ]
 
460
         */
 
461
        foreach ($this->data as $val) {
 
462
            if ($val === $record) {
 
463
                return false;
 
464
            }
 
465
        }
 
466
 
 
467
        if (isset($key)) {
 
468
            if (isset($this->data[$key])) {
 
469
                return false;
 
470
            }
 
471
            $this->data[$key] = $record;
 
472
            return true;
 
473
        }
 
474
 
 
475
        if (isset($this->keyColumn)) {
 
476
            $value = $record->get($this->keyColumn);
 
477
            if ($value === null) {
 
478
                throw new Doctrine_Collection_Exception("Couldn't create collection index. Record field '".$this->keyColumn."' was null.");
 
479
            }
 
480
            $this->data[$value] = $record;
 
481
        } else {
 
482
            $this->data[] = $record;
 
483
        }
 
484
 
 
485
        return true;
 
486
    }
 
487
    
 
488
    /**
 
489
     * Merges collection into $this and returns merged collection
 
490
     * 
 
491
     * @param Doctrine_Collection $coll
 
492
     * @return Doctrine_Collection
 
493
     */
 
494
    public function merge(Doctrine_Collection $coll)
 
495
    {
 
496
        $localBase = $this->getTable()->getComponentName();
 
497
        $otherBase = $coll->getTable()->getComponentName();
 
498
        
 
499
        if ($otherBase != $localBase && !is_subclass_of($otherBase, $localBase) ) {
 
500
            throw new Doctrine_Collection_Exception("Can't merge collections with incompatible record types");
 
501
        }
 
502
        
 
503
        foreach ($coll->getData() as $record) {
 
504
            $this->add($record);
 
505
        }
 
506
        
 
507
        return $this;
 
508
    }
 
509
 
 
510
    /**
 
511
     * Load all relationships or the named relationship passed
 
512
     *
 
513
     * @param mixed $name
 
514
     * @return boolean
 
515
     */
 
516
    public function loadRelated($name = null)
 
517
    {
 
518
        $list = array();
 
519
        $query   = new Doctrine_Query($this->_table->getConnection());
 
520
 
 
521
        if ( ! isset($name)) {
 
522
            foreach ($this->data as $record) {
 
523
                $value = $record->getIncremented();
 
524
                if ($value !== null) {
 
525
                    $list[] = $value;
 
526
                }
 
527
            }
 
528
            $query->from($this->_table->getComponentName());
 
529
            $query->where($this->_table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')');
 
530
 
 
531
            return $query;
 
532
        }
 
533
 
 
534
        $rel     = $this->_table->getRelation($name);
 
535
 
 
536
        if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) {
 
537
            foreach ($this->data as $record) {
 
538
                $list[] = $record[$rel->getLocal()];
 
539
            }
 
540
        } else {
 
541
            foreach ($this->data as $record) {
 
542
                $value = $record->getIncremented();
 
543
                if ($value !== null) {
 
544
                    $list[] = $value;
 
545
                }
 
546
            }
 
547
        }
 
548
 
 
549
        $dql     = $rel->getRelationDql(count($list), 'collection');
 
550
 
 
551
        $coll    = $query->query($dql, $list);
 
552
 
 
553
        $this->populateRelated($name, $coll);
 
554
    }
 
555
 
 
556
    /**
 
557
     * Populate the relationship $name for all records in the passed collection
 
558
     *
 
559
     * @param string $name
 
560
     * @param Doctrine_Collection $coll
 
561
     * @return void
 
562
     */
 
563
    public function populateRelated($name, Doctrine_Collection $coll)
 
564
    {
 
565
        $rel     = $this->_table->getRelation($name);
 
566
        $table   = $rel->getTable();
 
567
        $foreign = $rel->getForeign();
 
568
        $local   = $rel->getLocal();
 
569
 
 
570
        if ($rel instanceof Doctrine_Relation_LocalKey) {
 
571
            foreach ($this->data as $key => $record) {
 
572
                foreach ($coll as $k => $related) {
 
573
                    if ($related[$foreign] == $record[$local]) {
 
574
                        $this->data[$key]->setRelated($name, $related);
 
575
                    }
 
576
                }
 
577
            }
 
578
        } elseif ($rel instanceof Doctrine_Relation_ForeignKey) {
 
579
            foreach ($this->data as $key => $record) {
 
580
                if ( ! $record->exists()) {
 
581
                    continue;
 
582
                }
 
583
                $sub = new Doctrine_Collection($table);
 
584
 
 
585
                foreach ($coll as $k => $related) {
 
586
                    if ($related[$foreign] == $record[$local]) {
 
587
                        $sub->add($related);
 
588
                        $coll->remove($k);
 
589
                    }
 
590
                }
 
591
 
 
592
                $this->data[$key]->setRelated($name, $sub);
 
593
            }
 
594
        } elseif ($rel instanceof Doctrine_Relation_Association) {
 
595
            $identifier = $this->_table->getIdentifier();
 
596
            $asf        = $rel->getAssociationFactory();
 
597
            $name       = $table->getComponentName();
 
598
 
 
599
            foreach ($this->data as $key => $record) {
 
600
                if ( ! $record->exists()) {
 
601
                    continue;
 
602
                }
 
603
                $sub = new Doctrine_Collection($table);
 
604
                foreach ($coll as $k => $related) {
 
605
                    if ($related->get($local) == $record[$identifier]) {
 
606
                        $sub->add($related->get($name));
 
607
                    }
 
608
                }
 
609
                $this->data[$key]->setRelated($name, $sub);
 
610
 
 
611
            }
 
612
        }
 
613
    }
 
614
 
 
615
    /**
 
616
     * Get normal iterator - an iterator that will not expand this collection
 
617
     *
 
618
     * @return Doctrine_Iterator_Normal $iterator
 
619
     */
 
620
    public function getNormalIterator()
 
621
    {
 
622
        return new Doctrine_Collection_Iterator_Normal($this);
 
623
    }
 
624
 
 
625
    /**
 
626
     * Takes a snapshot from this collection
 
627
     *
 
628
     * snapshots are used for diff processing, for example
 
629
     * when a fetched collection has three elements, then two of those
 
630
     * are being removed the diff would contain one element
 
631
     *
 
632
     * Doctrine_Collection::save() attaches the diff with the help of last
 
633
     * snapshot.
 
634
     *
 
635
     * @return Doctrine_Collection
 
636
     */
 
637
    public function takeSnapshot()
 
638
    {
 
639
        $this->_snapshot = $this->data;
 
640
        
 
641
        return $this;
 
642
    }
 
643
 
 
644
    /**
 
645
     * Gets the data of the last snapshot
 
646
     *
 
647
     * @return array    returns the data in last snapshot
 
648
     */
 
649
    public function getSnapshot()
 
650
    {
 
651
        return $this->_snapshot;
 
652
    }
 
653
 
 
654
    /**
 
655
     * Processes the difference of the last snapshot and the current data
 
656
     *
 
657
     * an example:
 
658
     * Snapshot with the objects 1, 2 and 4
 
659
     * Current data with objects 2, 3 and 5
 
660
     *
 
661
     * The process would remove object 4
 
662
     *
 
663
     * @return Doctrine_Collection
 
664
     */
 
665
    public function processDiff() 
 
666
    {
 
667
        foreach (array_udiff($this->_snapshot, $this->data, array($this, "compareRecords")) as $record) {
 
668
            $record->delete();
 
669
        }
 
670
 
 
671
        return $this;
 
672
    }
 
673
 
 
674
    /**
 
675
     * Mimics the result of a $query->execute(array(), Doctrine::HYDRATE_ARRAY);
 
676
     *
 
677
     * @param boolean $deep
 
678
     */
 
679
    public function toArray($deep = false, $prefixKey = false)
 
680
    {
 
681
        $data = array();
 
682
        foreach ($this as $key => $record) {
 
683
            
 
684
            $key = $prefixKey ? get_class($record) . '_' .$key:$key;
 
685
            
 
686
            $data[$key] = $record->toArray($deep, $prefixKey);
 
687
        }
 
688
        
 
689
        return $data;
 
690
    }
 
691
 
 
692
    /**
 
693
     * Populate a Doctrine_Collection from an array of data
 
694
     *
 
695
     * @param string $array 
 
696
     * @return void
 
697
     */
 
698
    public function fromArray($array, $deep = true)
 
699
    {
 
700
        $data = array();
 
701
        foreach ($array as $rowKey => $row) {
 
702
            $this[$rowKey]->fromArray($row, $deep);
 
703
        }
 
704
    }
 
705
 
 
706
    /**
 
707
     * synchronizes a Doctrine_Collection with data from an array
 
708
     *
 
709
     * it expects an array representation of a Doctrine_Collection similar to the return
 
710
     * value of the toArray() method. It will create Dectrine_Records that don't exist
 
711
     * on the collection, update the ones that do and remove the ones missing in the $array
 
712
     *
 
713
     * @param array $array representation of a Doctrine_Collection
 
714
     */
 
715
    public function synchronizeWithArray(array $array)
 
716
    {
 
717
        foreach ($this as $key => $record) {
 
718
            if (isset($array[$key])) {
 
719
                $record->synchronizeWithArray($array[$key]);
 
720
                unset($array[$key]);
 
721
            } else {
 
722
                // remove records that don't exist in the array
 
723
                $this->remove($key);
 
724
            }
 
725
        }
 
726
        // create new records for each new row in the array
 
727
        foreach ($array as $rowKey => $row) {
 
728
            $this[$rowKey]->fromArray($row);
 
729
        }
 
730
    }
 
731
    public function synchronizeFromArray(array $array)
 
732
    {
 
733
        return $this->synchronizeWithArray($array);
 
734
    }
 
735
 
 
736
    /**
 
737
     * Export a Doctrine_Collection to one of the supported Doctrine_Parser formats
 
738
     *
 
739
     * @param string $type 
 
740
     * @param string $deep 
 
741
     * @return void
 
742
     */
 
743
    public function exportTo($type, $deep = false)
 
744
    {
 
745
        if ($type == 'array') {
 
746
            return $this->toArray($deep);
 
747
        } else {
 
748
            return Doctrine_Parser::dump($this->toArray($deep, true), $type);
 
749
        }
 
750
    }
 
751
 
 
752
    /**
 
753
     * Import data to a Doctrine_Collection from one of the supported Doctrine_Parser formats
 
754
     *
 
755
     * @param string $type 
 
756
     * @param string $data 
 
757
     * @return void
 
758
     */
 
759
    public function importFrom($type, $data)
 
760
    {
 
761
        if ($type == 'array') {
 
762
            return $this->fromArray($data);
 
763
        } else {
 
764
            return $this->fromArray(Doctrine_Parser::load($data, $type));
 
765
        }
 
766
    }
 
767
 
 
768
    /**
 
769
     * Perform a delete diff between the last snapshot and the current data
 
770
     *
 
771
     * @return array $diff
 
772
     */
 
773
    public function getDeleteDiff()
 
774
    {
 
775
        return array_udiff($this->_snapshot, $this->data, array($this, 'compareRecords'));
 
776
    }
 
777
 
 
778
    /**
 
779
     * Perform a insert diff between the last snapshot and the current data
 
780
     *
 
781
     * @return array $diff
 
782
     */
 
783
    public function getInsertDiff()
 
784
    {
 
785
        return array_udiff($this->data, $this->_snapshot, array($this, "compareRecords"));
 
786
    }
 
787
 
 
788
    /**
 
789
     * Compares two records. To be used on _snapshot diffs using array_udiff
 
790
     *
 
791
     * @param Doctrine_Record $a 
 
792
     * @param Doctrine_Record $b 
 
793
     * @return integer
 
794
     */
 
795
    protected function compareRecords($a, $b)
 
796
    {
 
797
        if ($a->getOid() == $b->getOid()) {
 
798
            return 0;
 
799
        }
 
800
        
 
801
        return ($a->getOid() > $b->getOid()) ? 1 : -1;
 
802
    }
 
803
 
 
804
    /**
 
805
     * Saves all records of this collection and processes the 
 
806
     * difference of the last snapshot and the current data
 
807
     *
 
808
     * @param Doctrine_Connection $conn     optional connection parameter
 
809
     * @return Doctrine_Collection
 
810
     */
 
811
    public function save(Doctrine_Connection $conn = null, $processDiff = true)
 
812
    {
 
813
        if ($conn == null) {
 
814
            $conn = $this->_table->getConnection();
 
815
        }
 
816
        
 
817
        try {
 
818
            $conn->beginInternalTransaction();
 
819
 
 
820
            $conn->transaction->addCollection($this);
 
821
 
 
822
            if ($processDiff) {
 
823
                $this->processDiff();
 
824
            }
 
825
 
 
826
            foreach ($this->getData() as $key => $record) {
 
827
                $record->save($conn);
 
828
            }
 
829
 
 
830
            $conn->commit();
 
831
        } catch (Exception $e) {
 
832
            $conn->rollback();
 
833
            throw $e;
 
834
        }
 
835
 
 
836
        return $this;
 
837
    }
 
838
 
 
839
    /**
 
840
     * Deletes all records from this collection
 
841
     *
 
842
     * @return Doctrine_Collection
 
843
     */
 
844
    public function delete(Doctrine_Connection $conn = null, $clearColl = true)
 
845
    {
 
846
        if ($conn == null) {
 
847
            $conn = $this->_table->getConnection();
 
848
        }
 
849
        
 
850
        try {
 
851
            $conn->beginInternalTransaction();
 
852
            $conn->transaction->addCollection($this);
 
853
 
 
854
            foreach ($this as $key => $record) {
 
855
                $record->delete($conn);
 
856
            }
 
857
 
 
858
            $conn->commit();
 
859
        } catch (Exception $e) {
 
860
            $conn->rollback();
 
861
            throw $e;
 
862
        }
 
863
        
 
864
        if ($clearColl) {
 
865
            $this->clear();
 
866
        }
 
867
        
 
868
        return $this;
 
869
    }
 
870
    
 
871
    /**
 
872
     * Clears the collection.
 
873
     *
 
874
     * @return void
 
875
     */
 
876
    public function clear()
 
877
    {
 
878
        $this->data = array();
 
879
    }
 
880
 
 
881
    /**
 
882
     * Frees the resources used by the collection.
 
883
     * WARNING: After invoking free() the collection is no longer considered to
 
884
     * be in a useable state. Subsequent usage may result in unexpected behavior.
 
885
     *
 
886
     * @return void
 
887
     */
 
888
    public function free($deep = false)
 
889
    {
 
890
        foreach ($this->getData() as $key => $record) {
 
891
            if ( ! ($record instanceof Doctrine_Null)) {
 
892
                $record->free($deep);
 
893
            }
 
894
        }
 
895
 
 
896
        $this->data = array();
 
897
 
 
898
        if ($this->reference) {
 
899
            $this->reference->free($deep);
 
900
            $this->reference = null;
 
901
        }
 
902
    }
 
903
 
 
904
    /**
 
905
     * Get collection data iterator
 
906
     *
 
907
     * @return object ArrayIterator
 
908
     */
 
909
    public function getIterator()
 
910
    {
 
911
        $data = $this->data;
 
912
        return new ArrayIterator($data);
 
913
    }
 
914
 
 
915
    /**
 
916
     * Returns a string representation of this object
 
917
     *
 
918
     * @return string $string
 
919
     */
 
920
    public function __toString()
 
921
    {
 
922
        return Doctrine_Lib::getCollectionAsString($this);
 
923
    }
 
924
    
 
925
    /**
 
926
     * Returns the relation object
 
927
     *
 
928
     * @return object Doctrine_Relation
 
929
     */
 
930
    public function getRelation()
 
931
    {
 
932
        return $this->relation;
 
933
    }
 
934
}