Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: Collection.php 7686 2010-08-24 16:54:40Z 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.doctrine-project.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.doctrine-project.org
30
 * @since       1.0
31
 * @version     $Revision: 7686 $
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_Core::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_Core::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
    public static function create($table, $keyColumn = null, $class = null)
113
    {
114
        if (is_null($class)) {
115
            if ( ! $table instanceof Doctrine_Table) {
116
                $table = Doctrine_Core::getTable($table);
117
            }
118
            $class = $table->getAttribute(Doctrine_Core::ATTR_COLLECTION_CLASS);
119
        }
120
 
121
        return new $class($table, $keyColumn);
122
    }
123
 
124
    /**
125
     * Get the table this collection belongs to
126
     *
127
     * @return Doctrine_Table
128
     */
129
    public function getTable()
130
    {
131
        return $this->_table;
132
    }
133
 
134
    /**
135
     * Set the data for the Doctrin_Collection instance
136
     *
137
     * @param array $data
138
     * @return Doctrine_Collection
139
     */
140
    public function setData(array $data)
141
    {
142
        $this->data = $data;
143
    }
144
 
145
    /**
146
     * This method is automatically called when this Doctrine_Collection is serialized
147
     *
148
     * @return array
149
     */
150
    public function serialize()
151
    {
152
        $vars = get_object_vars($this);
153
 
154
        unset($vars['reference']);
155
        unset($vars['referenceField']);
156
        unset($vars['relation']);
157
        unset($vars['expandable']);
158
        unset($vars['expanded']);
159
        unset($vars['generator']);
160
 
161
        $vars['_table'] = $vars['_table']->getComponentName();
162
 
163
        return serialize($vars);
164
    }
165
 
166
    /**
167
     * This method is automatically called everytime a Doctrine_Collection object is unserialized
168
     *
169
     * @return void
170
     */
171
    public function unserialize($serialized)
172
    {
173
        $manager    = Doctrine_Manager::getInstance();
174
        $connection    = $manager->getCurrentConnection();
175
 
176
        $array = unserialize($serialized);
177
 
178
        foreach ($array as $name => $values) {
179
            $this->$name = $values;
180
        }
181
 
182
        $this->_table = $connection->getTable($this->_table);
183
 
184
        $keyColumn = isset($array['keyColumn']) ? $array['keyColumn'] : null;
185
        if ($keyColumn === null) {
186
            $keyColumn = $this->_table->getBoundQueryPart('indexBy');
187
        }
188
 
189
        if ($keyColumn !== null) {
190
            $this->keyColumn = $keyColumn;
191
        }
192
    }
193
 
194
    /**
195
     * Sets the key column for this collection
196
     *
197
     * @param string $column
198
     * @return Doctrine_Collection $this
199
     */
200
    public function setKeyColumn($column)
201
    {
202
        $this->keyColumn = $column;
203
 
204
        return $this;
205
    }
206
 
207
    /**
208
     * Get the name of the key column
209
     *
210
     * @return string
211
     */
212
    public function getKeyColumn()
213
    {
214
        return $this->keyColumn;
215
    }
216
 
217
    /**
218
     * Get all the records as an array
219
     *
220
     * @return array
221
     */
222
    public function getData()
223
    {
224
        return $this->data;
225
    }
226
 
227
    /**
228
     * Get the first record in the collection
229
     *
230
     * @return Doctrine_Record
231
     */
232
    public function getFirst()
233
    {
234
        return reset($this->data);
235
    }
236
 
237
    /**
238
     * Get the last record in the collection
239
     *
240
     * @return Doctrine_Record
241
     */
242
    public function getLast()
243
    {
244
        return end($this->data);
245
    }
246
 
247
    /**
248
     * Get the last record in the collection
249
     *
250
     * @return Doctrine_Record
251
     */
252
    public function end()
253
    {
254
        return end($this->data);
255
    }
256
 
257
    /**
258
     * Get the current key
259
     *
260
     * @return Doctrine_Record
261
     */
262
    public function key()
263
    {
264
        return key($this->data);
265
    }
266
 
267
    /**
268
     * Sets a reference pointer
269
     *
270
     * @return void
271
     */
272
    public function setReference(Doctrine_Record $record, Doctrine_Relation $relation)
273
    {
274
        $this->reference = $record;
275
        $this->relation  = $relation;
276
 
277
        if ($relation instanceof Doctrine_Relation_ForeignKey ||
278
                $relation instanceof Doctrine_Relation_LocalKey) {
279
            $this->referenceField = $relation->getForeignFieldName();
280
 
281
            $value = $record->get($relation->getLocalFieldName());
282
 
283
            foreach ($this->data as $record) {
284
                if ($value !== null) {
285
                    $record->set($this->referenceField, $value, false);
286
                } else {
287
                    $record->set($this->referenceField, $this->reference, false);
288
                }
289
            }
290
        } elseif ($relation instanceof Doctrine_Relation_Association) {
291
 
292
        }
293
    }
294
 
295
    /**
296
     * Get reference to Doctrine_Record instance
297
     *
298
     * @return Doctrine_Record $reference
299
     */
300
    public function getReference()
301
    {
302
        return $this->reference;
303
    }
304
 
305
    /**
306
     * Removes a specified collection element
307
     *
308
     * @param mixed $key
309
     * @return boolean
310
     */
311
    public function remove($key)
312
    {
313
        $removed = $this->data[$key];
314
 
315
        unset($this->data[$key]);
316
        return $removed;
317
    }
318
 
319
    /**
320
     * Whether or not this collection contains a specified element
321
     *
322
     * @param mixed $key                    the key of the element
323
     * @return boolean
324
     */
325
    public function contains($key)
326
    {
327
        return isset($this->data[$key]);
328
    }
329
 
330
    /**
331
     * Search a Doctrine_Record instance
332
     *
333
     * @param string $Doctrine_Record
334
     * @return void
335
     */
336
    public function search(Doctrine_Record $record)
337
    {
338
        return array_search($record, $this->data, true);
339
    }
340
 
341
    /**
342
     * Gets a record for given key
343
     *
344
     * There are two special cases:
345
     *
346
     * 1. if null is given as a key a new record is created and attached
347
     * at the end of the collection
348
     *
349
     * 2. if given key does not exist, then a new record is create and attached
350
     * to the given key
351
     *
352
     * Collection also maps referential information to newly created records
353
     *
354
     * @param mixed $key                    the key of the element
355
     * @return Doctrine_Record              return a specified record
356
     */
357
    public function get($key)
358
    {
359
        if ( ! isset($this->data[$key])) {
360
            $record = $this->_table->create();
361
 
362
            if (isset($this->referenceField)) {
363
                $value = $this->reference->get($this->relation->getLocalFieldName());
364
 
365
                if ($value !== null) {
366
                    $record->set($this->referenceField, $value, false);
367
                } else {
368
                    $record->set($this->referenceField, $this->reference, false);
369
                }
370
            }
371
            if ($key === null) {
372
                $this->data[] = $record;
373
            } else {
374
                $this->data[$key] = $record;
375
            }
376
 
377
            if (isset($this->keyColumn)) {
378
                $record->set($this->keyColumn, $key);
379
            }
380
 
381
            return $record;
382
        }
383
 
384
        return $this->data[$key];
385
    }
386
 
387
    /**
388
     * Get array of primary keys for all the records in the collection
389
     *
390
     * @return array                an array containing all primary keys
391
     */
392
    public function getPrimaryKeys()
393
    {
394
        $list = array();
395
        $name = $this->_table->getIdentifier();
396
 
397
        foreach ($this->data as $record) {
398
            if (is_array($record) && isset($record[$name])) {
399
                $list[] = $record[$name];
400
            } else {
401
                $list[] = $record->getIncremented();
402
            }
403
        }
404
        return $list;
405
    }
406
 
407
    /**
408
     * Get all keys of the data in the collection
409
     *
410
     * @return array
411
     */
412
    public function getKeys()
413
    {
414
        return array_keys($this->data);
415
    }
416
 
417
    /**
418
     * Gets the number of records in this collection
419
     * This class implements interface countable
420
     *
421
     * @return integer
422
     */
423
    public function count()
424
    {
425
        return count($this->data);
426
    }
427
 
428
    /**
429
     * Set a Doctrine_Record instance to the collection
430
     *
431
     * @param integer $key
432
     * @param Doctrine_Record $record
433
     * @return void
434
     */
435
    public function set($key, $record)
436
    {
437
        if (isset($this->referenceField)) {
438
            $record->set($this->referenceField, $this->reference, false);
439
        }
440
 
441
        $this->data[$key] = $record;
442
    }
443
 
444
    /**
445
     * Adds a record to collection
446
     *
447
     * @param Doctrine_Record $record              record to be added
448
     * @param string $key                          optional key for the record
449
     * @return boolean
450
     */
451
    public function add($record, $key = null)
452
    {
453
        if (isset($this->referenceField)) {
454
            $value = $this->reference->get($this->relation->getLocalFieldName());
455
            if ($value !== null) {
456
                $record->set($this->referenceField, $value, false);
457
            } else {
458
                $record->set($this->referenceField, $this->reference, false);
459
            }
460
            $relations = $this->relation['table']->getRelations();
461
            foreach ($relations as $relation) {
462
                if ($this->relation['class'] == $relation['localTable']->getOption('name') && $relation->getLocal() == $this->relation->getForeignFieldName()) {
463
                    $record->$relation['alias'] = $this->reference;
464
                    break;
465
                }
466
            }
467
        }
468
        /**
469
         * for some weird reason in_array cannot be used here (php bug ?)
470
         *
471
         * if used it results in fatal error : [ nesting level too deep ]
472
         */
473
        foreach ($this->data as $val) {
474
            if ($val === $record) {
475
                return false;
476
            }
477
        }
478
 
479
        if (isset($key)) {
480
            if (isset($this->data[$key])) {
481
                return false;
482
            }
483
            $this->data[$key] = $record;
484
            return true;
485
        }
486
 
487
        if (isset($this->keyColumn)) {
488
            $value = $record->get($this->keyColumn);
489
            if ($value === null) {
490
                throw new Doctrine_Collection_Exception("Couldn't create collection index. Record field '".$this->keyColumn."' was null.");
491
            }
492
            $this->data[$value] = $record;
493
        } else {
494
            $this->data[] = $record;
495
        }
496
 
497
        return true;
498
    }
499
 
500
    /**
501
     * Merges collection into $this and returns merged collection
502
     *
503
     * @param Doctrine_Collection $coll
504
     * @return Doctrine_Collection
505
     */
506
    public function merge(Doctrine_Collection $coll)
507
    {
508
        $localBase = $this->getTable()->getComponentName();
509
        $otherBase = $coll->getTable()->getComponentName();
510
 
511
        if ($otherBase != $localBase && !is_subclass_of($otherBase, $localBase) ) {
512
            throw new Doctrine_Collection_Exception("Can't merge collections with incompatible record types");
513
        }
514
 
515
        foreach ($coll->getData() as $record) {
516
            $this->add($record);
517
        }
518
 
519
        return $this;
520
    }
521
 
522
    /**
523
     * Load all relationships or the named relationship passed
524
     *
525
     * @param mixed $name
526
     * @return boolean
527
     */
528
    public function loadRelated($name = null)
529
    {
530
        $list = array();
531
        $query = $this->_table->createQuery();
532
 
533
        if ( ! isset($name)) {
534
            foreach ($this->data as $record) {
535
                $value = $record->getIncremented();
536
                if ($value !== null) {
537
                    $list[] = $value;
538
                }
539
            }
540
            $query->where($this->_table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')');
541
            if ( ! $list) {
542
                $query->where($this->_table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')', $list);
543
            }
544
 
545
            return $query;
546
        }
547
 
548
        $rel     = $this->_table->getRelation($name);
549
 
550
        if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) {
551
            foreach ($this->data as $record) {
552
                $list[] = $record[$rel->getLocal()];
553
            }
554
        } else {
555
            foreach ($this->data as $record) {
556
                $value = $record->getIncremented();
557
                if ($value !== null) {
558
                    $list[] = $value;
559
                }
560
            }
561
        }
562
 
563
        if ( ! $list) {
564
            return;
565
        }
566
 
567
        $dql     = $rel->getRelationDql(count($list), 'collection');
568
 
569
        $coll    = $query->query($dql, $list);
570
 
571
        $this->populateRelated($name, $coll);
572
    }
573
 
574
    /**
575
     * Populate the relationship $name for all records in the passed collection
576
     *
577
     * @param string $name
578
     * @param Doctrine_Collection $coll
579
     * @return void
580
     */
581
    public function populateRelated($name, Doctrine_Collection $coll)
582
    {
583
        $rel     = $this->_table->getRelation($name);
584
        $table   = $rel->getTable();
585
        $foreign = $rel->getForeign();
586
        $local   = $rel->getLocal();
587
 
588
        if ($rel instanceof Doctrine_Relation_LocalKey) {
589
            foreach ($this->data as $key => $record) {
590
                foreach ($coll as $k => $related) {
591
                    if ($related[$foreign] == $record[$local]) {
592
                        $this->data[$key]->setRelated($name, $related);
593
                    }
594
                }
595
            }
596
        } elseif ($rel instanceof Doctrine_Relation_ForeignKey) {
597
            foreach ($this->data as $key => $record) {
598
                if ( ! $record->exists()) {
599
                    continue;
600
                }
601
                $sub = Doctrine_Collection::create($table);
602
 
603
                foreach ($coll as $k => $related) {
604
                    if ($related[$foreign] == $record[$local]) {
605
                        $sub->add($related);
606
                        $coll->remove($k);
607
                    }
608
                }
609
 
610
                $this->data[$key]->setRelated($name, $sub);
611
            }
612
        } elseif ($rel instanceof Doctrine_Relation_Association) {
613
            $identifier = $this->_table->getIdentifier();
614
            $asf        = $rel->getAssociationFactory();
615
            $name       = $table->getComponentName();
616
 
617
            foreach ($this->data as $key => $record) {
618
                if ( ! $record->exists()) {
619
                    continue;
620
                }
621
                $sub = Doctrine_Collection::create($table);
622
                foreach ($coll as $k => $related) {
623
                    if ($related->get($local) == $record[$identifier]) {
624
                        $sub->add($related->get($name));
625
                    }
626
                }
627
                $this->data[$key]->setRelated($name, $sub);
628
 
629
            }
630
        }
631
    }
632
 
633
    /**
634
     * Get normal iterator - an iterator that will not expand this collection
635
     *
636
     * @return Doctrine_Iterator_Normal $iterator
637
     */
638
    public function getNormalIterator()
639
    {
640
        return new Doctrine_Collection_Iterator_Normal($this);
641
    }
642
 
643
    /**
644
     * Takes a snapshot from this collection
645
     *
646
     * snapshots are used for diff processing, for example
647
     * when a fetched collection has three elements, then two of those
648
     * are being removed the diff would contain one element
649
     *
650
     * Doctrine_Collection::save() attaches the diff with the help of last
651
     * snapshot.
652
     *
653
     * @return Doctrine_Collection
654
     */
655
    public function takeSnapshot()
656
    {
657
        $this->_snapshot = $this->data;
658
 
659
        return $this;
660
    }
661
 
662
    /**
663
     * Gets the data of the last snapshot
664
     *
665
     * @return array    returns the data in last snapshot
666
     */
667
    public function getSnapshot()
668
    {
669
        return $this->_snapshot;
670
    }
671
 
672
    /**
673
     * Processes the difference of the last snapshot and the current data
674
     *
675
     * an example:
676
     * Snapshot with the objects 1, 2 and 4
677
     * Current data with objects 2, 3 and 5
678
     *
679
     * The process would remove object 4
680
     *
681
     * @return Doctrine_Collection
682
     */
683
    public function processDiff()
684
    {
685
        foreach (array_udiff($this->_snapshot, $this->data, array($this, "compareRecords")) as $record) {
686
            $record->delete();
687
        }
688
 
689
        return $this;
690
    }
691
 
692
    /**
693
     * Mimics the result of a $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY);
694
     *
695
     * @param boolean $deep
696
     */
697
    public function toArray($deep = true, $prefixKey = false)
698
    {
699
        $data = array();
700
        foreach ($this as $key => $record) {
701
 
702
            $key = $prefixKey ? get_class($record) . '_' .$key:$key;
703
 
704
            $data[$key] = $record->toArray($deep, $prefixKey);
705
        }
706
 
707
        return $data;
708
    }
709
 
710
    /**
711
     * Build an array made up of the values from the 2 specified columns
712
     *
713
     * @param string $key
714
     * @param string $value
715
     * @return array $result
716
     */
717
    public function toKeyValueArray($key, $value)
718
    {
719
        $result = array();
720
        foreach ($this as $record) {
721
            $result[$record->$key] = $record->$value;
722
        }
723
        return $result;
724
    }
725
 
726
    public function toHierarchy()
727
    {
728
        $collection = $this;
729
        $table = $collection->getTable();
730
 
731
        if ( ! $table->isTree() || ! $table->hasColumn('level')) {
732
            throw new Doctrine_Exception('Cannot hydrate model that does not implements Tree behavior with `level` column');
733
        }
734
 
735
        // Trees mapped
736
        $trees = new Doctrine_Collection($table);
737
        $l = 0;
738
 
739
        if (count($collection) > 0) {
740
            // Node Stack. Used to help building the hierarchy
741
            $stack = new Doctrine_Collection($table);
742
 
743
            foreach ($collection as $child) {
744
                $item = $child;
745
 
746
                $item->mapValue('__children', new Doctrine_Collection($table));
747
 
748
                // Number of stack items
749
                $l = count($stack);
750
 
751
                // Check if we're dealing with different levels
752
                while($l > 0 && $stack[$l - 1]['level'] >= $item['level']) {
753
                    array_pop($stack->data);
754
                    $l--;
755
                }
756
 
757
                // Stack is empty (we are inspecting the root)
758
                if ($l == 0) {
759
                    // Assigning the root child
760
                    $i = count($trees);
761
                    $trees[$i] = $item;
762
                    $stack[] = $trees[$i];
763
                } else {
764
                    // Add child to parent
765
                    $i = count($stack[$l - 1]['__children']);
766
                    $stack[$l - 1]['__children'][$i] = $item;
767
                    $stack[] = $stack[$l - 1]['__children'][$i];
768
                }
769
            }
770
        }
771
        return $trees;
772
    }
773
 
774
    /**
775
     * Populate a Doctrine_Collection from an array of data
776
     *
777
     * @param string $array
778
     * @return void
779
     */
780
    public function fromArray($array, $deep = true)
781
    {
782
        $data = array();
783
        foreach ($array as $rowKey => $row) {
784
            $this[$rowKey]->fromArray($row, $deep);
785
        }
786
    }
787
 
788
    /**
789
     * synchronizes a Doctrine_Collection with data from an array
790
     *
791
     * it expects an array representation of a Doctrine_Collection similar to the return
792
     * value of the toArray() method. It will create Dectrine_Records that don't exist
793
     * on the collection, update the ones that do and remove the ones missing in the $array
794
     *
795
     * @param array $array representation of a Doctrine_Collection
796
     */
797
    public function synchronizeWithArray(array $array)
798
    {
799
        foreach ($this as $key => $record) {
800
            if (isset($array[$key])) {
801
                $record->synchronizeWithArray($array[$key]);
802
                unset($array[$key]);
803
            } else {
804
                // remove records that don't exist in the array
805
                $this->remove($key);
806
            }
807
        }
808
        // create new records for each new row in the array
809
        foreach ($array as $rowKey => $row) {
810
            $this[$rowKey]->fromArray($row);
811
        }
812
    }
813
 
814
    public function synchronizeFromArray(array $array)
815
    {
816
        return $this->synchronizeWithArray($array);
817
    }
818
 
819
    /**
820
     * Export a Doctrine_Collection to one of the supported Doctrine_Parser formats
821
     *
822
     * @param string $type
823
     * @param string $deep
824
     * @return void
825
     */
826
    public function exportTo($type, $deep = true)
827
    {
828
        if ($type == 'array') {
829
            return $this->toArray($deep);
830
        } else {
831
            return Doctrine_Parser::dump($this->toArray($deep, true), $type);
832
        }
833
    }
834
 
835
    /**
836
     * Import data to a Doctrine_Collection from one of the supported Doctrine_Parser formats
837
     *
838
     * @param string $type
839
     * @param string $data
840
     * @return void
841
     */
842
    public function importFrom($type, $data)
843
    {
844
        if ($type == 'array') {
845
            return $this->fromArray($data);
846
        } else {
847
            return $this->fromArray(Doctrine_Parser::load($data, $type));
848
        }
849
    }
850
 
851
    /**
852
     * Perform a delete diff between the last snapshot and the current data
853
     *
854
     * @return array $diff
855
     */
856
    public function getDeleteDiff()
857
    {
858
        return array_udiff($this->_snapshot, $this->data, array($this, 'compareRecords'));
859
    }
860
 
861
    /**
862
     * Perform a insert diff between the last snapshot and the current data
863
     *
864
     * @return array $diff
865
     */
866
    public function getInsertDiff()
867
    {
868
        return array_udiff($this->data, $this->_snapshot, array($this, "compareRecords"));
869
    }
870
 
871
    /**
872
     * Compares two records. To be used on _snapshot diffs using array_udiff
873
     *
874
     * @param Doctrine_Record $a
875
     * @param Doctrine_Record $b
876
     * @return integer
877
     */
878
    protected function compareRecords($a, $b)
879
    {
880
        if ($a->getOid() == $b->getOid()) {
881
            return 0;
882
        }
883
 
884
        return ($a->getOid() > $b->getOid()) ? 1 : -1;
885
    }
886
 
887
    /**
888
     * Saves all records of this collection and processes the
889
     * difference of the last snapshot and the current data
890
     *
891
     * @param Doctrine_Connection $conn     optional connection parameter
892
     * @return Doctrine_Collection
893
     */
894
    public function save(Doctrine_Connection $conn = null, $processDiff = true)
895
    {
896
        if ($conn == null) {
897
            $conn = $this->_table->getConnection();
898
        }
899
 
900
        try {
901
            $conn->beginInternalTransaction();
902
 
903
            $conn->transaction->addCollection($this);
904
 
905
            if ($processDiff) {
906
                $this->processDiff();
907
            }
908
 
909
            foreach ($this->getData() as $key => $record) {
910
                $record->save($conn);
911
            }
912
 
913
            $conn->commit();
914
        } catch (Exception $e) {
915
            $conn->rollback();
916
            throw $e;
917
        }
918
 
919
        return $this;
920
    }
921
 
922
    /**
923
     * Replaces all records of this collection and processes the
924
     * difference of the last snapshot and the current data
925
     *
926
     * @param Doctrine_Connection $conn     optional connection parameter
927
     * @return Doctrine_Collection
928
     */
929
    public function replace(Doctrine_Connection $conn = null, $processDiff = true)
930
    {
931
        if ($conn == null) {
932
            $conn = $this->_table->getConnection();
933
        }
934
 
935
        try {
936
            $conn->beginInternalTransaction();
937
 
938
            $conn->transaction->addCollection($this);
939
 
940
            if ($processDiff) {
941
                $this->processDiff();
942
            }
943
 
944
            foreach ($this->getData() as $key => $record) {
945
                $record->replace($conn);
946
            }
947
 
948
            $conn->commit();
949
        } catch (Exception $e) {
950
            $conn->rollback();
951
            throw $e;
952
        }
953
 
954
        return $this;
955
    }
956
 
957
    /**
958
     * Deletes all records from this collection
959
     *
960
     * @return Doctrine_Collection
961
     */
962
    public function delete(Doctrine_Connection $conn = null, $clearColl = true)
963
    {
964
        if ($conn == null) {
965
            $conn = $this->_table->getConnection();
966
        }
967
 
968
        try {
969
            $conn->beginInternalTransaction();
970
            $conn->transaction->addCollection($this);
971
 
972
            foreach ($this as $key => $record) {
973
                $record->delete($conn);
974
            }
975
 
976
            $conn->commit();
977
        } catch (Exception $e) {
978
            $conn->rollback();
979
            throw $e;
980
        }
981
 
982
        if ($clearColl) {
983
            $this->clear();
984
        }
985
 
986
        return $this;
987
    }
988
 
989
    /**
990
     * Clears the collection.
991
     *
992
     * @return void
993
     */
994
    public function clear()
995
    {
996
        $this->data = array();
997
    }
998
 
999
    /**
1000
     * Frees the resources used by the collection.
1001
     * WARNING: After invoking free() the collection is no longer considered to
1002
     * be in a useable state. Subsequent usage may result in unexpected behavior.
1003
     *
1004
     * @return void
1005
     */
1006
    public function free($deep = false)
1007
    {
1008
        foreach ($this->getData() as $key => $record) {
1009
            if ( ! ($record instanceof Doctrine_Null)) {
1010
                $record->free($deep);
1011
            }
1012
        }
1013
 
1014
        $this->data = array();
1015
 
1016
        if ($this->reference) {
1017
            $this->reference->free($deep);
1018
            $this->reference = null;
1019
        }
1020
    }
1021
 
1022
    /**
1023
     * Get collection data iterator
1024
     *
1025
     * @return Iterator
1026
     */
1027
    public function getIterator()
1028
    {
1029
        $data = $this->data;
1030
        return new ArrayIterator($data);
1031
    }
1032
 
1033
    /**
1034
     * Returns a string representation of this object
1035
     *
1036
     * @return string $string
1037
     */
1038
    public function __toString()
1039
    {
1040
        return Doctrine_Lib::getCollectionAsString($this);
1041
    }
1042
 
1043
    /**
1044
     * Returns the relation object
1045
     *
1046
     * @return Doctrine_Relation
1047
     */
1048
    public function getRelation()
1049
    {
1050
        return $this->relation;
1051
    }
1052
 
1053
    /**
1054
     * checks if one of the containing records is modified
1055
     * returns true if modified, false otherwise
1056
     *
1057
     * @return boolean
1058
     */
1059
    final public function isModified() {
1060
        $dirty = (count($this->getInsertDiff()) > 0 || count($this->getDeleteDiff()) > 0);
1061
        if ( ! $dirty) {
1062
            foreach($this as $record) {
1063
                if ($dirty = $record->isModified()) {
1064
                    break;
1065
                }
1066
            }
1067
        }
1068
        return $dirty;
1069
    }
1070
}