Version: current

      1 <?php
      2 /**
      3  * This is a special kind of DataObjectSet used to represent the items linked to in a 1-many or many-many
      4  * join.  It provides add and remove methods that will update the database.
      5  * @package sapphire
      6  * @subpackage model
      7  */
      8 class ComponentSet extends DataObjectSet {
      9     /**
     10      * Type of relationship (eg '1-1', '1-many').
     11      * @var string
     12      */
     13     protected $type;
     14
     15     /**
     16      * Object that owns this set.
     17      * @var DataObject
     18      */
     19     protected $ownerObj;
     20
     21     /**
     22      * Class of object that owns this set.
     23      * @var string
     24      */
     25     protected $ownerClass;
     26
     27     /**
     28      * Table that holds this relationship.
     29      * @var string
     30      */
     31     protected $tableName;
     32
     33     /**
     34      * Class of child side of the relationship.
     35      * @var string
     36      */
     37     protected $childClass;
     38
     39     /**
     40      * Field to join on.
     41      * @var string
     42      */
     43     protected $joinField;
     44
     45     /**
     46      * Set the ComponentSet specific information.
     47      * @param string $type Type of relationship (eg '1-1', '1-many').
     48      * @param DataObject $ownerObj Object that owns this set.
     49      * @param string $ownerClass Class of object that owns this set.
     50      * @param string $tableName Table that holds this relationship.
     51      * @param string $childClass Class of child side of the relationship.
     52      * @param string $joinField Field to join on.
     53      */
     54     function setComponentInfo($type, $ownerObj, $ownerClass, $tableName, $childClass, $joinField = null) {
     55         $this->type = $type;
     56         $this->ownerObj = $ownerObj;
     57         $this->ownerClass = $ownerClass ? $ownerClass : $ownerObj->class;
     58         $this->tableName = $tableName;
     59         $this->childClass = $childClass;
     60         $this->joinField = $joinField;
     61     }
     62
     63     /**
     64      * Get the ComponentSet specific information
     65      *
     66      * Returns an array on the format array(
     67      *      'type' => <string>,
     68      *      'ownerObj' => <Object>,
     69      *      'ownerClass' => <string>,
     70      *      'tableName' => <string>,
     71      *      'childClass' => <string>,
     72      *      'joinField' => <string>|null );
     73      *
     74      * @return array
     75      */
     76     public function getComponentInfo() {
     77         return array(
     78             'type' => $this->type,
     79             'ownerObj' => $this->ownerObj,
     80             'ownerClass' => $this->ownerClass,
     81             'tableName' => $this->tableName,
     82             'childClass' => $this->childClass,
     83             'joinField' => $this->joinField
     84         );
     85     }
     86
     87     /**
     88      * Get an array of all the IDs in this component set, where the keys are the same as the
     89      * values.
     90      * @return array
     91      */
     92     function getIdList() {
     93         $list = array();
     94         foreach($this->items as $item) {
     95             $list[$item->ID] = $item->ID;
     96         }
     97         return $list;
     98     }
     99
    100     /**
    101      * Add an item to this set.
    102      * @param DataObject|int|string $item Item to add, either as a DataObject or as the ID.
    103      * @param array $extraFields A map of extra fields to add.
    104      */
    105     function add($item, $extraFields = null) {
    106         if(!isset($item)) {
    107             user_error("ComponentSet::add() Not passed an object or ID", E_USER_ERROR);
    108         }
    109
    110         if(is_object($item)) {
    111             if(!is_a($item, $this->childClass)) {
    112                 user_error("ComponentSet::add() Tried to add an '{$item->class}' object, but a '{$this->childClass}' object expected", E_USER_ERROR);
    113             }
    114         } else {
    115             if(!$this->childClass) {
    116                 user_error("ComponentSet::add() \$this->childClass not set", E_USER_ERROR);
    117             }
    118
    119             $item = DataObject::get_by_id($this->childClass, $item);
    120             if(!$item) return;
    121         }
    122
    123         // If we've already got a database object, then update the database
    124         if($this->ownerObj->ID && is_numeric($this->ownerObj->ID)) {
    125             $this->loadChildIntoDatabase($item, $extraFields);
    126         }
    127
    128         // In either case, add something to $this->items
    129         $this->items[] = $item;
    130     }
    131
    132     /**
    133      * Method to save many-many join data into the database for the given $item.
    134      * Used by add() and write().
    135      * @param DataObject|string|int The item to save, as either a DataObject or the ID.
    136      * @param array $extraFields Map of extra fields.
    137      */
    138     protected function loadChildIntoDatabase($item, $extraFields = null) {
    139         if($this->type == '1-to-many') {
    140             $child = DataObject::get_by_id($this->childClass,$item->ID);
    141             if (!$child) $child = $item;
    142             $joinField = $this->joinField;
    143             $child->$joinField = $this->ownerObj->ID;
    144             $child->write();
    145
    146         } else {
    147             $parentField = $this->ownerClass . 'ID';
    148             $childField = ($this->childClass == $this->ownerClass) ? "ChildID" : ($this->childClass . 'ID');
    149
    150             DB::query( "DELETE FROM \"$this->tableName\" WHERE \"$parentField\" = {$this->ownerObj->ID} AND \"$childField\" = {$item->ID}" );
    151
    152             $extraKeys = $extraValues = '';
    153             if($extraFields) foreach($extraFields as $k => $v) {
    154                 $extraKeys .= ", \"$k\"";
    155                 $extraValues .= ", '" . DB::getConn()->addslashes($v) . "'";
    156             }
    157
    158             DB::query("INSERT INTO \"$this->tableName\" (\"$parentField\",\"$childField\" $extraKeys) VALUES ({$this->ownerObj->ID}, {$item->ID} $extraValues)");
    159         }
    160     }
    161
    162     /**
    163      * Add a number of items to the component set.
    164      * @param array $items Items to add, as either DataObjects or IDs.
    165      */
    166     function addMany($items) {
    167         foreach($items as $item) {
    168             $this->add($item);
    169         }
    170     }
    171
    172     /**
    173      * Sets the ComponentSet to be the given ID list.
    174      * Records will be added and deleted as appropriate.
    175      * @param array $idList List of IDs.
    176      */
    177     function setByIDList($idList) {
    178         $has = array();
    179         // Index current data
    180         if($this->items) foreach($this->items as $item) {
    181            $has[$item->ID] = true;
    182         }
    183
    184         // Keep track of items to delete
    185         $itemsToDelete = $has;
    186
    187         // add items in the list
    188         // $id is the database ID of the record
    189         if($idList) foreach($idList as $id) {
    190             $itemsToDelete[$id] = false;
    191             if($id && !isset($has[$id])) $this->add($id);
    192         }
    193
    194         // delete items not in the list
    195         $removeList = array();
    196         foreach($itemsToDelete as $id => $actuallyDelete) {
    197             if($actuallyDelete) $removeList[] = $id;
    198         }
    199         $this->removeMany($removeList);
    200     }
    201
    202     /**
    203      * Remove an item from this set.
    204      *
    205      * @param DataObject|string|int $item Item to remove, either as a DataObject or as the ID.
    206      */
    207     function remove($item) {
    208         if(is_object($item)) {
    209             if(!is_a($item, $this->childClass)) {
    210                 user_error("ComponentSet::remove() Tried to remove an '{$item->class}' object, but a '{$this->childClass}' object expected", E_USER_ERROR);
    211             }
    212         } else {
    213             $item = DataObject::get_by_id($this->childClass, $item);
    214         }
    215
    216         // Manipulate the database, if it's in there
    217         if($this->ownerObj->ID && is_numeric($this->ownerObj->ID)) {
    218             if($this->type == '1-to-many') {
    219                 $child = DataObject::get_by_id($this->childClass,$item->ID);
    220                 $joinField = $this->joinField;
    221                 if($child->$joinField == $this->ownerObj->ID) {
    222                     $child->$joinField = null;
    223                     $child->write();
    224                 }
    225
    226             } else {
    227                 $parentField = $this->ownerClass . 'ID';
    228                 $childField = ($this->childClass == $this->ownerClass) ? "ChildID" : ($this->childClass . 'ID');
    229                 DB::query("DELETE FROM \"$this->tableName\" WHERE \"$parentField\" = {$this->ownerObj->ID} AND \"$childField\" = {$item->ID}");
    230             }
    231         }
    232
    233         // Manipulate the in-memory array of items
    234         if($this->items) foreach($this->items as $i => $candidateItem) {
    235             if($candidateItem->ID == $item->ID) {
    236                 unset($this->items[$i]);
    237                 break;
    238             }
    239         }
    240     }
    241
    242     /**
    243      * Remove many items from this set.
    244      * @param array $itemList The items to remove, as a numerical array with IDs or as a DataObjectSet
    245      */
    246     function removeMany($itemList) {
    247         if(!count($itemList)) return false;
    248
    249         if($this->type == '1-to-many') {
    250             foreach($itemList as $item) $this->remove($item);
    251         } else {
    252             $itemCSV = implode(", ", $itemList);
    253             $parentField = $this->ownerClass . 'ID';
    254             $childField = ($this->childClass == $this->ownerClass) ? "ChildID" : ($this->childClass . 'ID');
    255             DB::query("DELETE FROM \"$this->tableName\" WHERE \"$parentField\" = {$this->ownerObj->ID} AND \"$childField\" IN ($itemCSV)");
    256         }
    257     }
    258
    259     /**
    260      * Remove all items in this set.
    261      */
    262     function removeAll() {
    263         if(!empty($this->tableName)) {
    264             $parentField = $this->ownerClass . 'ID';
    265             DB::query("DELETE FROM \"$this->tableName\" WHERE \"$parentField\" = {$this->ownerObj->ID}");
    266         } else {
    267             foreach($this->items as $item) {
    268                 $this->remove($item);
    269             }
    270         }
    271     }
    272
    273     /**
    274      * Write this set to the database.
    275      * Called by DataObject::write().
    276      * @param boolean $firstWrite This should be set to true if it the first time the set is being written.
    277      */
    278     function write($firstWrite = false) {
    279         if($firstWrite) {
    280             foreach($this->items as $item) {
    281                 $this->loadChildIntoDatabase($item);
    282             }
    283         }
    284     }
    285
    286     /**
    287      * Returns information about this set in HTML format for debugging.
    288      *
    289      * @return string
    290      */
    291     function debug() {
    292         $size = count($this->items);
    293
    294         $output = <<<OUT
    295 <h3>ComponentSet</h3>
    296 <ul>
    297     <li>Type: {$this->type}</li>
    298     <li>Size: $size</li>
    299 </ul>
    300
    301 OUT;
    302
    303         return $output;
    304     }
    305 }
    306
    307 ?>
    308

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -