/**
  * Add implicit changes that should be included in this changeset
  *
  * When an item is created or changed, all it's owned items which have
  * changes are implicitly added
  *
  * When an item is deleted, it's owner (even if that owner does not have changes)
  * is implicitly added
  */
 public function sync()
 {
     // Start a transaction (if we can)
     DB::get_conn()->withTransaction(function () {
         // Get the implicitly included items for this ChangeSet
         $implicit = $this->calculateImplicit();
         // Adjust the existing implicit ChangeSetItems for this ChangeSet
         /** @var ChangeSetItem $item */
         foreach ($this->Changes()->filter(['Added' => ChangeSetItem::IMPLICITLY]) as $item) {
             $objectKey = $this->implicitKey($item);
             // If a ChangeSetItem exists, but isn't in $implicit, it's no longer required, so delete it
             if (!array_key_exists($objectKey, $implicit)) {
                 $item->delete();
             } else {
                 $item->ReferencedBy()->setByIDList($implicit[$objectKey]['ReferencedBy']);
                 unset($implicit[$objectKey]);
             }
         }
         // Now $implicit is all those items that are implicitly included, but don't currently have a ChangeSetItem.
         // So create new ChangeSetItems to match
         foreach ($implicit as $key => $props) {
             $item = new ChangeSetItem($props);
             $item->Added = ChangeSetItem::IMPLICITLY;
             $item->ChangeSetID = $this->ID;
             $item->ReferencedBy()->setByIDList($props['ReferencedBy']);
             $item->write();
         }
     });
 }
 /**
  * Build item resource from a changesetitem
  *
  * @param ChangeSetItem $changeSetItem
  * @return array
  */
 protected function getChangeSetItemResource(ChangeSetItem $changeSetItem)
 {
     $baseClass = DataObject::getSchema()->baseDataClass($changeSetItem->ObjectClass);
     $baseSingleton = DataObject::singleton($baseClass);
     $thumbnailWidth = (int) $this->config()->thumbnail_width;
     $thumbnailHeight = (int) $this->config()->thumbnail_height;
     $hal = ['_links' => ['self' => ['href' => $this->ItemLink($changeSetItem->ID)]], 'ID' => $changeSetItem->ID, 'Created' => $changeSetItem->Created, 'LastEdited' => $changeSetItem->LastEdited, 'Title' => $changeSetItem->getTitle(), 'ChangeType' => $changeSetItem->getChangeType(), 'Added' => $changeSetItem->Added, 'ObjectClass' => $changeSetItem->ObjectClass, 'ObjectID' => $changeSetItem->ObjectID, 'BaseClass' => $baseClass, 'Singular' => $baseSingleton->i18n_singular_name(), 'Plural' => $baseSingleton->i18n_plural_name(), 'Thumbnail' => $changeSetItem->ThumbnailURL($thumbnailWidth, $thumbnailHeight)];
     // Get preview urls
     $previews = $changeSetItem->getPreviewLinks();
     if ($previews) {
         $hal['_links']['preview'] = $previews;
     }
     // Get edit link
     $editLink = $changeSetItem->CMSEditLink();
     if ($editLink) {
         $hal['_links']['edit'] = ['href' => $editLink];
     }
     // Depending on whether the object was added implicitly or explicitly, set
     // other related objects.
     if ($changeSetItem->Added === ChangeSetItem::IMPLICITLY) {
         $referencedItems = $changeSetItem->ReferencedBy();
         $referencedBy = [];
         foreach ($referencedItems as $referencedItem) {
             $referencedBy[] = ['href' => $this->SetLink($referencedItem->ID)];
         }
         if ($referencedBy) {
             $hal['_links']['referenced_by'] = $referencedBy;
         }
     }
     return $hal;
 }