コード例 #1
0
 /**
  * Handles all of the routine tasks that go along with saving elements.
  *
  * Those tasks include:
  *
  * - Validating its content (if $validateContent is `true`, or it’s left as `null` and the element is enabled)
  * - Ensuring the element has a title if its type {@link BaseElementType::hasTitles() has titles}, and giving it a
  *   default title in the event that $validateContent is set to `false`
  * - Saving a row in the `elements` table
  * - Assigning the element’s ID on the element model, if it’s a new element
  * - Assigning the element’s ID on the element’s content model, if there is one and it’s a new set of content
  * - Updating the search index with new keywords from the element’s content
  * - Setting a unique URI on the element, if it’s supposed to have one.
  * - Saving the element’s row(s) in the `elements_i18n` and `content` tables
  * - Deleting any rows in the `elements_i18n` and `content` tables that no longer need to be there
  * - Calling the field types’ {@link BaseFieldType::onAfterElementSave() onAfterElementSave()} methods
  * - Cleaing any template caches that the element was involved in
  *
  * This method should be called by a service’s “saveX()” method, _after_ it is done validating any attributes on
  * the element that are of particular concern to its element type. For example, if the element were an entry,
  * saveElement() should be called only after the entry’s sectionId and typeId attributes had been validated to
  * ensure that they point to valid section and entry type IDs.
  *
  * @param BaseElementModel $element         The element that is being saved
  * @param bool|null        $validateContent Whether the element's content should be validated. If left 'null', it
  *                                          will depend on whether the element is enabled or not.
  *
  * @throws Exception|\Exception
  * @return bool
  */
 public function saveElement(BaseElementModel $element, $validateContent = null)
 {
     $elementType = $this->getElementType($element->getElementType());
     $isNewElement = !$element->id;
     // Validate the content first
     if ($elementType->hasContent()) {
         if ($validateContent === null) {
             $validateContent = (bool) $element->enabled;
         }
         if ($validateContent && !craft()->content->validateContent($element)) {
             $element->addErrors($element->getContent()->getErrors());
             return false;
         } else {
             // Make sure there's a title
             if ($elementType->hasTitles()) {
                 $fields = array('title');
                 $content = $element->getContent();
                 $content->setRequiredFields($fields);
                 if (!$content->validate($fields) && $content->hasErrors('title')) {
                     // Just set *something* on it
                     if ($isNewElement) {
                         $content->title = 'New ' . $element->getClassHandle();
                     } else {
                         $content->title = $element->getClassHandle() . ' ' . $element->id;
                     }
                 }
             }
         }
     }
     // Get the element record
     if (!$isNewElement) {
         $elementRecord = ElementRecord::model()->findByAttributes(array('id' => $element->id, 'type' => $element->getElementType()));
         if (!$elementRecord) {
             throw new Exception(Craft::t('No element exists with the ID “{id}”.', array('id' => $element->id)));
         }
     } else {
         $elementRecord = new ElementRecord();
         $elementRecord->type = $element->getElementType();
     }
     // Set the attributes
     $elementRecord->enabled = (bool) $element->enabled;
     $elementRecord->archived = (bool) $element->archived;
     $transaction = craft()->db->getCurrentTransaction() === null ? craft()->db->beginTransaction() : null;
     try {
         // Fire an 'onBeforeSaveElement' event
         $event = new Event($this, array('element' => $element, 'isNewElement' => $isNewElement));
         $this->onBeforeSaveElement($event);
         // Is the event giving us the go-ahead?
         if ($event->performAction) {
             // Save the element record first
             $success = $elementRecord->save(false);
             if ($success) {
                 if ($isNewElement) {
                     // Save the element id on the element model, in case {id} is in the URL format
                     $element->id = $elementRecord->id;
                     if ($elementType->hasContent()) {
                         $element->getContent()->elementId = $element->id;
                     }
                 }
                 // Save the content
                 if ($elementType->hasContent()) {
                     craft()->content->saveContent($element, false, (bool) $element->id);
                 }
                 // Update the search index
                 craft()->search->indexElementAttributes($element);
                 // Update the locale records and content
                 // We're saving all of the element's locales here to ensure that they all exist and to update the URI in
                 // the event that the URL format includes some value that just changed
                 $localeRecords = array();
                 if (!$isNewElement) {
                     $existingLocaleRecords = ElementLocaleRecord::model()->findAllByAttributes(array('elementId' => $element->id));
                     foreach ($existingLocaleRecords as $record) {
                         $localeRecords[$record->locale] = $record;
                     }
                 }
                 $mainLocaleId = $element->locale;
                 $locales = $element->getLocales();
                 $localeIds = array();
                 if (!$locales) {
                     throw new Exception('All elements must have at least one locale associated with them.');
                 }
                 foreach ($locales as $localeId => $localeInfo) {
                     if (is_numeric($localeId) && is_string($localeInfo)) {
                         $localeId = $localeInfo;
                         $localeInfo = array();
                     }
                     $localeIds[] = $localeId;
                     if (!isset($localeInfo['enabledByDefault'])) {
                         $localeInfo['enabledByDefault'] = true;
                     }
                     if (isset($localeRecords[$localeId])) {
                         $localeRecord = $localeRecords[$localeId];
                     } else {
                         $localeRecord = new ElementLocaleRecord();
                         $localeRecord->elementId = $element->id;
                         $localeRecord->locale = $localeId;
                         $localeRecord->enabled = $localeInfo['enabledByDefault'];
                     }
                     // Is this the main locale?
                     $isMainLocale = $localeId == $mainLocaleId;
                     if ($isMainLocale) {
                         $localizedElement = $element;
                     } else {
                         // Copy the element for this locale
                         $localizedElement = $element->copy();
                         $localizedElement->locale = $localeId;
                         if ($localeRecord->id) {
                             // Keep the original slug
                             $localizedElement->slug = $localeRecord->slug;
                         } else {
                             // Default to the main locale's slug
                             $localizedElement->slug = $element->slug;
                         }
                     }
                     if ($elementType->hasContent()) {
                         if (!$isMainLocale) {
                             $content = null;
                             if (!$isNewElement) {
                                 // Do we already have a content row for this locale?
                                 $content = craft()->content->getContent($localizedElement);
                             }
                             if (!$content) {
                                 $content = craft()->content->createContent($localizedElement);
                                 $content->setAttributes($element->getContent()->getAttributes());
                                 $content->id = null;
                                 $content->locale = $localeId;
                             }
                             $localizedElement->setContent($content);
                         }
                         if (!$localizedElement->getContent()->id) {
                             craft()->content->saveContent($localizedElement, false, false);
                         }
                     }
                     // Capture the original slug, in case it's entirely composed of invalid characters
                     $originalSlug = $localizedElement->slug;
                     // Clean up the slug
                     ElementHelper::setValidSlug($localizedElement);
                     // If the slug was entirely composed of invalid characters, it will be blank now.
                     if ($originalSlug && !$localizedElement->slug) {
                         $localizedElement->slug = $originalSlug;
                         $element->addError('slug', Craft::t('{attribute} is invalid.', array('attribute' => Craft::t('Slug'))));
                         // Don't bother with any of the other locales
                         $success = false;
                         break;
                     }
                     ElementHelper::setUniqueUri($localizedElement);
                     $localeRecord->slug = $localizedElement->slug;
                     $localeRecord->uri = $localizedElement->uri;
                     if ($isMainLocale) {
                         $localeRecord->enabled = (bool) $element->localeEnabled;
                     }
                     $success = $localeRecord->save();
                     if (!$success) {
                         // Pass any validation errors on to the element
                         $element->addErrors($localeRecord->getErrors());
                         // Don't bother with any of the other locales
                         break;
                     }
                 }
                 if ($success) {
                     if (!$isNewElement) {
                         // Delete the rows that don't need to be there anymore
                         craft()->db->createCommand()->delete('elements_i18n', array('and', 'elementId = :elementId', array('not in', 'locale', $localeIds)), array(':elementId' => $element->id));
                         if ($elementType->hasContent()) {
                             craft()->db->createCommand()->delete($element->getContentTable(), array('and', 'elementId = :elementId', array('not in', 'locale', $localeIds)), array(':elementId' => $element->id));
                         }
                     }
                     // Call the field types' onAfterElementSave() methods
                     $fieldLayout = $element->getFieldLayout();
                     if ($fieldLayout) {
                         foreach ($fieldLayout->getFields() as $fieldLayoutField) {
                             $field = $fieldLayoutField->getField();
                             if ($field) {
                                 $fieldType = $field->getFieldType();
                                 if ($fieldType) {
                                     $fieldType->element = $element;
                                     $fieldType->onAfterElementSave();
                                 }
                             }
                         }
                     }
                     // Finally, delete any caches involving this element. (Even do this for new elements, since they
                     // might pop up in a cached criteria.)
                     craft()->templateCache->deleteCachesByElement($element);
                 }
             }
         } else {
             $success = false;
         }
         // Commit the transaction regardless of whether we saved the user, in case something changed
         // in onBeforeSaveElement
         if ($transaction !== null) {
             $transaction->commit();
         }
     } catch (\Exception $e) {
         if ($transaction !== null) {
             $transaction->rollback();
         }
         throw $e;
     }
     if ($success) {
         // Fire an 'onSaveElement' event
         $this->onSaveElement(new Event($this, array('element' => $element, 'isNewElement' => $isNewElement)));
     } else {
         if ($isNewElement) {
             $element->id = null;
             if ($elementType->hasContent()) {
                 $element->getContent()->id = null;
                 $element->getContent()->elementId = null;
             }
         }
     }
     return $success;
 }