monster_menus-9.0.x-dev/src/Entity/MMTree.php

src/Entity/MMTree.php
<?php

namespace Drupal\monster_menus\Entity;

use Drupal\Core\Database\Database;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\monster_menus\Constants;
use Drupal\user\UserInterface;

class MMTreeDepthException extends \Exception {}

/**
 * Defines the MM Page entity.
 *
 * @ingroup monster_menus
 *
 * @ContentEntityType(
 *   id = "mm_tree",
 *   label = @Translation("MM Page"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\monster_menus\MMTreeListBuilder",
 *     "views_data" = "Drupal\monster_menus\Entity\MMTreeViewsData",
 *     "translation" = "Drupal\monster_menus\MMTreeTranslationHandler",
 *     "storage_schema" = "Drupal\monster_menus\MMTreeStorageSchema",
 *     "form" = {
 *       "default" = "Drupal\monster_menus\Form\EditContentForm",
 *       "add" = "Drupal\monster_menus\Form\EditContentForm",
 *       "edit" = "Drupal\monster_menus\Form\EditContentForm",
 *       "delete" = "Drupal\monster_menus\Form\DeleteContentConfirmForm",
 *     },
 *     "access" = "Drupal\monster_menus\MMTreeAccessControlHandler",
 *     "route_provider" = {
 *       "html" = "Drupal\monster_menus\MMTreeHtmlRouteProvider",
 *     },
 *   },
 *   base_table = "mm_tree",
 *   data_table = "",
 *   translatable = FALSE,
 *   admin_permission = "administer all menus",
 *   uri_callback = "mm_tree_uri",
 *   entity_keys = {
 *     "id" = "mmtid",
 *     "revision" = "vid",
 *     "label" = "name",
 *     "uuid" = "uuid",
 *     "uid" = "uid",
 *     "status" = "status",
 *   },
 *   links = {
 *     "canonical" = "/mm/{mm_tree}",
 *     "add-form" = "/mm/{mm_tree}/settings/sub",
 *     "edit-form" = "/mm/{mm_tree}/settings/edit",
 *     "delete-form" = "/mm/{mm_tree}/settings/delete",
 *     "version-history" = "/mm/{mm_tree}/settings/revisions",
 *   }
 * )
 */
class MMTree extends ContentEntityBase implements MMTreeInterface {

  use EntityChangedTrait;
  use MMTreeExtendedSettingsTrait;

  /**
   * Initial values of several fields, to determine upon save whether the sort
   * index needs to be updated.
   *
   * @var $oldName
   *   The user-readable name
   * @var $oldWeight
   *   The weight (sort order)
   * @var $oldHidden
   *   Whether the item is hidden in menus
   * @var $oldParent
   *  The parent MM Tree ID
   */
  private $oldName, $oldWeight, $oldHidden, $oldParent;

  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageInterface $storage, array &$values) {
    parent::preCreate($storage, $values);
    $values += ['uid' => \Drupal::currentUser()->id()];
  }

  /**
   * @inheritDoc
   */
  public function isGroup() {
    return (bool) mm_content_is_group($this->isNew() ? $this->get('parent')->getString() : $this->id());
  }

  /**
   * @inheritDoc
   */
  public function save() {
    $parent = $this->get('parent')->getString();
    if (!isset($parent)) {
      throw new \Exception('MMTree::save() requires that "parent" be set.');
    }
    $parent = (int) $parent;

    if (empty($this->getChangedTime())) {
      $this->setChangedTime(mm_request_time());
    }

    if (is_null($this->get('muid')->value)) {
      $this->set('muid', \Drupal::currentUser()->id());
    }

    if ($this->isNew()) {
      $msg = ['An attempt to create a new child of mmtid=@mmtid failed, because it would result in a tree that is too deeply nested. To correct this problem, increase the length of mm_tree.sort_idx then run mm_content_update_sort().', ['@mmtid' => $parent]];
      if (!_mm_content_test_sort_length($sort_idx = _mm_content_get_next_sort($parent), $msg)) {
        throw new MMTreeDepthException('This MMTree could not be created because it would cause the tree to become too deeply nested. Please contact a system administrator.');
      }

      if (empty($this->getCreatedTime())) {
        $this->setCreatedTime(mm_request_time());
      }

      if (empty($this->get('cuid')->value)) {
        $this->set('cuid', \Drupal::currentUser()->id());
      }

      $this->set('sort_idx_dirty', 1);
      $this->set('sort_idx', $sort_idx);

      $transaction = $this->getDatabase()->startTransaction();
      try {
        $parent_list = mm_content_get_parents_with_self($parent, FALSE, FALSE); // don't include virtual parents
        // Note: save() automatically writes a revision.
        parent::save();
        mm_content_update_parents($this->id(), $parent_list, TRUE);
        $this->saveExtendedSettings();
        // If the direct parent is a recycle bin, update the mm_recycle table.
        // This should only happen during migrate or import, since that is the
        // only time a new MMTree would be created inside a bin.
        if (mm_content_user_can($parent, Constants::MM_PERMS_IS_RECYCLE_BIN)) {
          $this->getDatabase()->insert('mm_recycle')
            ->fields([
              'type' => 'cat',
              'id' => $this->id(),
              'bin_mmtid' => $parent,
              'recycle_date' => mm_request_time(),
            ])
            ->execute();
        }
        mm_content_clear_caches($parent);
        mm_content_update_sort_queue($parent);
      }
      catch (\Exception $e) {
        $transaction->rollBack();
        throw $e;
      }
    }
    // Update.
    else {
      $fields = $this->getFields();
      if ($fields['name']->value != $this->oldName || $fields['weight']->value != $this->oldWeight || $fields['hidden']->value != $this->oldHidden || $parent != $this->oldParent) {
        if ($parent != $this->oldParent) {
          // Location in tree is changing.
          if ($fields['weight']->value == $this->oldWeight) {
            // Weight is not intentionally set, so reset it.
            $this->set('weight', 0);
          }
          mm_content_update_parents($this->id());
        }
        $this->set('sort_idx_dirty', 1);
        mm_content_update_sort_queue($parent);
      }
      $transaction = $this->getDatabase()->startTransaction();
      try {
        parent::save();
        $this->saveExtendedSettings();
        mm_content_clear_caches($parent);
        mm_content_update_sort_queue($parent);
        mm_content_clear_routing_cache_tagged($this->id());
      }
      catch (\Exception $e) {
        $transaction->rollBack();
        throw $e;
      }
    }
  }

  /**
   * @inheritDoc
   */
  public function delete() {
    parent::delete();
    static::deleteMultiple([$this->id()], FALSE);
  }

  /**
   * Delete multiple MMTree entities.
   *
   * @param array $mmtids
   *   The list of tree entries to delete.
   * @param bool $doBaseTable
   *   Whether to also delete rows from the base mm_tree table.
   */
  public static function deleteMultiple($mmtids, $doBaseTable = TRUE) {
    $database = Database::getConnection();
    $use_db_query = $database->databaseType() == 'mysql';

    if ($doBaseTable) {
      $database->delete('mm_tree')
        ->condition('mmtid', $mmtids, 'IN')
        ->execute();
    }
    $or = $database->condition('OR');
    $database->delete('mm_tree_parents')
      ->condition($or
        ->condition('mmtid', $mmtids, 'IN')
        ->condition('parent', $mmtids, 'IN')
      )
      ->execute();
    $database->delete('mm_node2tree')
      ->condition('mmtid', $mmtids, 'IN')
      ->execute();
    // Clear the cache used by mm_content_get_by_nid.
    mm_content_get_by_nid([], TRUE);
    $database->delete('mm_node_reorder')
      ->condition('mmtid', $mmtids, 'IN')
      ->execute();
    $database->delete('mm_recycle')
      ->condition('type', 'cat')
      ->condition('id', $mmtids, 'IN')
      ->execute();
    $database->delete('mm_recycle')
      ->condition('type', 'node')
      ->condition('bin_mmtid', $mmtids, 'IN')
      ->execute();
    $database->update('mm_recycle')
      ->fields(['from_mmtid' => 0])
      ->condition('type', 'node')
      ->condition('from_mmtid', $mmtids, 'IN')
      ->execute();

    // This needs to happen before deleting groups, below.
    static::deleteMultipleExtendedSettings($mmtids);

    // Remove ad-hoc groups (gid<0) first.
    // It's far faster to use $database->query(), since DBTNG doesn't allow JOIN.
    if ($use_db_query) {
      $database->query('DELETE g FROM {mm_group} g INNER JOIN {mm_tree_access} a ON a.gid = g.gid WHERE a.mmtid IN(:mmtids[]) AND a.gid < 0', [':mmtids[]' => $mmtids]);
    }
    else {
      // DELETE FROM {mm_group} WHERE
      //   (SELECT 1 FROM {mm_tree_access} a WHERE a.gid = {mm_group}.gid
      //     AND a.mmtid IN(:mmtids) AND a.gid < 0)
      $adhoc = $database->select('mm_tree_access', 'a');
      $adhoc->addExpression(1);
      $adhoc->where('a.gid = {mm_group}.gid')
        ->condition('a.mmtid', $mmtids, 'IN')
        ->condition('a.gid', 0, '<');
      $database->delete('mm_group')
        ->condition($adhoc)
        ->execute();
    }

    // Remove remaining groups.
    $or = $database->condition('OR');
    $database->delete('mm_tree_access')
      ->condition($or
        ->condition('mmtid', $mmtids, 'IN')
        ->condition('gid', $mmtids, 'IN')
      )
      ->execute();
    $database->delete('mm_node_write')
      ->condition('gid', $mmtids, 'IN')
      ->execute();
    $database->delete('mm_node_redir')
      ->condition('mmtid', $mmtids, 'IN')
      ->execute();
    $database->delete('mm_tree_bookmarks')
      ->condition('data', $mmtids, 'IN')
      ->execute();
  }

  /**
   * @inheritDoc
   */
  public static function postLoad(EntityStorageInterface $storage, array &$entities) {
    // Copy some values during initial load for comparison during save() to see
    // if the sort index needs to be updated.
    /** @var MMTree[] $entities */
    foreach ($entities as $entity) {
      $entity->setOldSortValues($entity->getName(), $entity->get('weight')->value, $entity->get('hidden')->value, (int) $entity->get('parent')->getString());
    }
    parent::postLoad($storage, $entities);
  }

  public function setOldSortValues($name, $weight, $hidden, $parent) {
    $this->oldName = $name;
    $this->oldWeight = $weight;
    $this->oldHidden = $hidden;
    $this->oldParent = $parent;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->get('name')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setName($name) {
    $this->set('name', $name);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() {
    return $this->get('ctime')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp) {
    $this->set('ctime', $timestamp);
    return $this;
  }


  /**
   * {@inheritdoc}
   */
  public function getChangedTime() {
    return $this->get('mtime')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setChangedTime($timestamp) {
    $this->set('mtime', $timestamp);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwner() {
    return $this->get('uid')->entity;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwnerId() {
    return $this->get('uid')->target_id;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwnerId($uid) {
    $this->set('uid', $uid);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwner(UserInterface $account) {
    $this->set('uid', $account->id());
    return $this;
  }

  /**
   * Get a representation of the entity as a standard object.
   *
   * @return \stdClass
   */
  public function toObject() {
    $object = (object) [];
    /** @var FieldItemListInterface $property */
    foreach ($this->getFields() as $name => $property) {
      $val = $property->getValue();
      $object->{$name} = $val[0]['value'] ?? $val[0]['target_id'] ?? NULL;
    }
    return $object;
  }

  /**
   * {@inheritdoc}
   */
  public function &__get($name) {
    if (in_array($name, static::$extendedFieldKeys)) {
      $data = $this->getExtendedSettings();
      if (!isset($data[$name])) {
        $data[$name] = NULL;
      }
      return $data[$name];
    }
    return parent::__get($name);
  }

  /**
   * {@inheritdoc}
   */
  public function __set($name, $value) {
    if (in_array($name, static::$extendedFieldKeys)) {
      $this->extendedSettings[$name] = $value;
    }
    else {
      parent::__set($name, $value);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function __isset($name) {
    if (in_array($name, static::$extendedFieldKeys)) {
      $data = $this->getExtendedSettings();
      return isset($data[$name]);
    }
    return parent::__isset($name);
  }

  /**
   * {@inheritdoc}
   */
  public function __unset($name) {
    if (in_array($name, static::$extendedFieldKeys)) {
      unset($this->extendedSettings[$name]);
    }
    else {
      parent::__unset($name);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = parent::baseFieldDefinitions($entity_type);

    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Owner'))
      ->setDescription(t('User ID of the owner'))
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'author',
        'weight' => 0,
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => 5,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Page Name'))
      ->setDescription(t('Name of the page'))
      ->setRevisionable(TRUE)
      ->setSettings([
        'max_length' => 128,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => -4,
      ])
      ->setDisplayOptions('form', [
        'type' => 'string_textfield',
        'weight' => -4,
      ])
      ->setTranslatable(TRUE)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['alias'] = BaseFieldDefinition::create('string')
      ->setLabel(t('URL alias'))
      ->setDescription(t('Alias of the page'))
      ->setRevisionable(TRUE)
      ->setSettings([
        'max_length' => 128,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => -4,
      ])
      ->setDisplayOptions('form', [
        'type' => 'string_textfield',
        'weight' => -4,
      ])
      ->setTranslatable(TRUE)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['parent'] = BaseFieldDefinition::create('entity_reference')
      ->setReadOnly(TRUE)
      ->setLabel(t('Parent MMTID'))
      ->setDescription(t('ID of the parent'))
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'mm_tree')
      ->setSetting('handler', 'default');

    $fields['default_mode'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Access mode(s)'))
      ->setDescription(t('Access mode(s) for anonymous user'))
      ->setRevisionable(TRUE)
      ->setSettings([
        'max_length' => 7,
        'text_processing' => 0,
      ])
      ->setDefaultValue('');

    $fields['weight'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Weight'))
      ->setDescription(t('Menu order'))
      ->setDefaultValue(0);

    $fields['theme'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Theme'))
      ->setRevisionable(TRUE)
      ->setDescription(t('Theme for this page and its children'))
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ]);

    $fields['sort_idx'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Sort index'))
      ->setReadOnly(TRUE)
      ->setSettings([
        'max_length' => min(intval(255 / Constants::MM_CONTENT_BTOA_CHARS), Constants::MM_CONTENT_MYSQL_MAX_JOINS) * Constants::MM_CONTENT_BTOA_CHARS,
        'text_processing' => 0,
      ]);

    $fields['sort_idx_dirty'] = BaseFieldDefinition::create('boolean')
      ->setReadOnly(TRUE)
      ->setLabel(t('Sort index is dirty'))
      ->setDefaultValue(FALSE);

    $fields['hover'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Hover'))
      ->setRevisionable(TRUE)
      ->setTranslatable(TRUE)
      ->setDescription(t('Displayed when mouse hovers over menu entry'))
      ->setSettings([
        'max_length' => 128,
        'text_processing' => 0,
      ]);

    $fields['rss'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('RSS feed is enabled'))
      ->setRevisionable(TRUE)
      ->setDefaultValue(FALSE);

    $fields['ctime'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('Creation time'))
      ->setReadOnly(TRUE);

    $fields['cuid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Creator'))
      ->setDescription(t('User ID of the creator'))
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDefaultValue(1)
      ->setReadOnly(TRUE);

    $fields['mtime'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Modification time'))
      ->setDescription(t('The time when the entry was last edited'))
      ->setRevisionable(TRUE);

    $fields['muid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Editor'))
      ->setDescription(t('User ID of the editor'))
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDefaultValue(NULL)
      ->setRevisionable(TRUE);

    $fields['node_info'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Default attribution display mode'))
      ->setRevisionable(TRUE)
      ->setDefaultValue(1);

    $fields['previews'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Show only teasers'))
      ->setRevisionable(TRUE)
      ->setDefaultValue(FALSE);

    $fields['hidden'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Hidden'))
      ->setDescription(t('Page is hidden in menu'))
      ->setRevisionable(TRUE)
      ->setDefaultValue(FALSE);

    $fields['comment'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Default comment display mode'))
      ->setRevisionable(TRUE)
      ->setDefaultValue(0);

    // Add a placeholder for extended settings which is only used during import.
    $fields['extendedSettings'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Placeholder for extended settings'))
      ->setRevisionable(FALSE)
      ->setCustomStorage(TRUE)
      ->setDefaultValue(NULL);

    return $fields;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc