Version: current

      1 <?php
      2 /**
      3  * Utility class to render views of the differences between two data objects (or two versions of the
      4  * same data object).
      5  *
      6  * Construcing a diff object is done as follows:
      7  * <code>
      8  * $fromRecord = Versioned::get_version('SiteTree', $pageID, $fromVersion);
      9  * $toRecord = Versioned::get_version('SiteTree, $pageID, $toVersion);
     10  * $diff = new DataDifferencer($fromRecord, $toRecord);
     11  * </code>
     12  *
     13  * And then it can be used in a number of ways.  You can use the ChangedFields() method in a template:
     14  * <pre>
     15  * <dl class="diff">
     16  * <% control Diff.ChangedFields %>
     17  *    <dt>$Title</dt>
     18  *    <dd>$Diff</dd>
     19  * <% end_control %>
     20  * </dl>
     21  * </pre>
     22  *
     23  * Or you can get the diff'ed content as another DataObject, that you can insert into a form.
     24  * <code>
     25  * $form->loadDataFrom($diff->diffedData());
     26  * </code>
     27  *
     28  * If there are fields whose changes you aren't interested in, you can ignore them like so:
     29  * <code>
     30  * $diff->ignoreFields('AuthorID', 'Status');
     31  * </code>
     32  *
     33  * @package sapphire
     34  * @subpackage misc
     35  */
     36 class DataDifferencer extends ViewableData {
     37     protected $fromRecord;
     38     protected $toRecord;
     39
     40     protected $ignoredFields = array("ID","Version","RecordID");
     41
     42     /**
     43      * Construct a DataDifferencer to show the changes between $fromRecord and $toRecord.
     44      * If $fromRecord is null, this will represent a "creation".
     45      */
     46     function __construct($fromRecord, $toRecord) {
     47         if(!$toRecord) user_error("DataDifferencer constructed without a toRecord", E_USER_WARNING);
     48         $this->fromRecord = $fromRecord;
     49         $this->toRecord = $toRecord;
     50         parent::__construct();
     51     }
     52
     53     /**
     54      * Specify some fields to ignore changes from.  Repeated calls are cumulative.
     55      * @param $ignoredFields An array of field names to ignore.  Alternatively, pass the field names as
     56      * separate args.
     57      */
     58     function ignoreFields($ignoredFields) {
     59         if(!is_array($ignoredFields)) $ignoredFields = func_get_args();
     60         $this->ignoredFields = array_merge($this->ignoredFields, $ignoredFields);
     61     }
     62
     63     /**
     64      * Get a DataObject with altered values replaced with HTML diff strings, incorporating
     65      * <ins> and <del> tags.
     66      */
     67     function diffedData() {
     68         if($this->fromRecord) {
     69             $diffed = clone $this->fromRecord;
     70             $fields = array_keys($diffed->getAllFields() + $this->toRecord->getAllFields());
     71         } else {
     72             $diffed = clone $this->toRecord;
     73             $fields = array_keys($this->toRecord->getAllFields());
     74         }
     75
     76         foreach($fields as $field) {
     77             if(in_array($field, $this->ignoredFields)) continue;
     78
     79             if(!$this->fromRecord) {
     80                 $diffed->$field = "<ins>" . $this->toRecord->$field . "</ins>";
     81             } else if($this->fromRecord->$field != $this->toRecord->$field) {
     82                 $diffed->$field = Diff::compareHTML($this->fromRecord->$field, $this->toRecord->$field);
     83             }
     84         }
     85
     86         return $diffed;
     87     }
     88
     89     /**
     90      * Get a DataObjectSet of the changed fields.
     91      * Each element is an array data containing
     92      *  - Name: The field name
     93      *  - Title: The human-readable field title
     94      *  - Diff: An HTML diff showing the changes
     95      *  - From: The older version of the field
     96      *  - To: The newer version of the field
     97      */
     98     function ChangedFields() {
     99         $changedFields = new DataObjectSet();
    100
    101         if($this->fromRecord) {
    102             $base = $this->fromRecord;
    103             $fields = array_keys($this->fromRecord->getAllFields());
    104         } else {
    105             $base = $this->toRecord;
    106             $fields = array_keys($this->toRecord->getAllFields());
    107         }
    108
    109         foreach($fields as $field) {
    110             if(in_array($field, $this->ignoredFields)) continue;
    111
    112             if(!$this->fromRecord || $this->fromRecord->$field != $this->toRecord->$field) {
    113                 $changedFields->push(new ArrayData(array(
    114                     'Name' => $field,
    115                     'Title' => $base->fieldLabel($field),
    116                     'Diff' => $this->fromRecord
    117                         ? Diff::compareHTML($this->fromRecord->$field, $this->toRecord->$field)
    118                         : "<ins>" . $this->toRecord->$field . "</ins>",
    119                     'From' => $this->fromRecord ? $this->fromRecord->$field : null,
    120                     'To' => $this->toRecord ? $this->toRecord->$field : null,
    121                 )));
    122             }
    123         }
    124
    125         return $changedFields;
    126     }
    127
    128     /**
    129      * Get an array of the names of every fields that has changed.
    130      * This is simpler than {@link ChangedFields()}
    131      */
    132     function changedFieldNames() {
    133         $diffed = clone $this->fromRecord;
    134         $fields = array_keys($diffed->getAllFields());
    135
    136         $changedFields = array();
    137
    138         foreach($fields as $field) {
    139             if(in_array($field, $this->ignoredFields)) continue;
    140             if($this->fromRecord->$field != $this->toRecord->$field) {
    141                 $changedFields[] = $field;
    142             }
    143         }
    144
    145         return $changedFields;
    146     }
    147 }
    148

[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. -