Skip to content
Snippets Groups Projects
Forked from project / hierarchy_manager
57 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
PluginTypeManager.php 6.28 KiB
<?php

namespace Drupal\hierarchy_manager;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager;
use Drupal\hierarchy_manager\Plugin\HmSetupPluginManager;

class PluginTypeManager {
  
  /**
   * Display plugin manager.
   *
   * @var \Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager
   */
  protected $displayManager;
  
  /**
   * Setup plugin manager.
   *
   * @var \Drupal\hierarchy_manager\Plugin\HmSetupPluginManager
   */
  protected $setupManager;
  
  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;
  
  /**
   * {@inheritdoc}
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, HmDisplayPluginManager $display_manager, HmSetupPluginManager $setup_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->displayManager = $display_manager;
    $this->setupManager = $setup_manager;
  }
  
  /**
   * Construct an item inside the hierarchy.
   * 
   * @param string|int $id
   *   Item id.
   * @param string $label
   *   Item text.
   * @param string $parent
   *   Parent id of the item.
   * @param string $edit_url
   *   The URL where to edit this item.
   * @return array
   *   The hierarchy item array.
   */
  public function buildHierarchyItem($id, $label, $parent, $edit_url) {
    return 
    [
    'id' => $id,
    'text' => $label,
    'parent' => $parent,
    'edit_url' => $edit_url,
    ];
  }
  
  /**
   * Get a display plugin instance according to a setup plugin.
   * 
   * @param string $setup_plugin_id
   *   setup plugin ID.
   * @return NULL|object
   *   The display plugin instance.
   */
  public function getDisplayPluginInstance(string $setup_plugin_id) {
    // The setup plugin instance.
    $setup_plugin = $this->setupManager->createInstance($setup_plugin_id);
    // Display profile.
    $display_profile = $this->entityTypeManager->getStorage('hm_display_profile')->load($setup_plugin->getDispalyProfileId());
    
    if (empty($display_profile)) {
      return NULL;
    }
    
    // Display plugin ID.
    $display_plugin_id = $display_profile->get("plugin");
    
    return $this->displayManager->createInstance($display_plugin_id);
  }
  
  /**
   * Update the items for a hierarchy
   * 
   * @param int $target_position
   *   Which position the new items will be insert.
   * @param array $all_siblings
   *   All siblings of the new items in an array[$item_id => (int)$weight]
   * @param array $updated_items
   *   IDs of new items inserted.
   * @param int|bool $after
   *   Indicator if new items are inserted after target position.
   *   
   * @return array
   *   All siblings needed to move and their new weights.
   */
  public function updateHierarchy(int $target_position, array $all_siblings, array $updated_items, $after) {
    $filtered_moving_siblings = [];
    $first_half = TRUE;

    $total = count($all_siblings);
    if ($target_position === 0) {
      // The insert postion is the first position.
      // we don't need to move any siblings.
      $weight = (int) reset($all_siblings) - 1;
    }   
    elseif ($target_position >= $total - 1) {
      // The insert postion is the end,
      // we don't need to move any siblings.
      $last_item= array_slice($all_siblings, -1, 1, TRUE);
      $weight = (int) reset($last_item) + 1;
    }
    else {
      $target_item = array_slice($all_siblings, $target_position, 1, TRUE);
      $weight = (int) reset($target_item);
      // If the target position is in the second half,
      // we will move all siblings
      // after the target position forward.
      // Otherwise, we will move siblings
      // before the target position backwards.
      if ($target_position >= $total / 2) {
        $first_half = FALSE;
        
        if ($after) {
          // Insert after the target position.
          // The target stay where it is.
          $weight += 1;
          $moving_siblings = array_slice($all_siblings, $target_position + 1, NULL, TRUE);
        }
        else {
          // Insert before the target position.
          // The target need to move forwards.
          $moving_siblings = array_slice($all_siblings, $target_position, NULL, TRUE);
        }
        $step = $weight + count($updated_items);
      }
      else {
        if ($after) {
          // Insert after the target position.
          // The target need to move backwards.
          $moving_siblings = array_slice($all_siblings, 0, $target_position + 1, TRUE);
        }
        else {
          // Insert before the target position.
          // The target stay where it is.
          $weight -= 1;
          $moving_siblings = array_slice($all_siblings, 0, $target_position, TRUE);
        }
        $weight = $step = $weight - count($updated_items);
        // Reverse the siblings_moved array
        // as we will decrease the weight
        // starting from the first element
        // and the new weight should be in
        // an opposite order.
        $moving_siblings = array_reverse($moving_siblings, TRUE);
      }
      
      // Move all siblings that need to move.
      foreach($moving_siblings as $item_id => $item_weight) {
        // Skip all links in the updated array. They will be moved later.
        if (in_array($item_id, $updated_items)) {
          continue;
        }
        if ($first_half) {
          // While moving the first half of the siblings,
          // all moving siblings' weight are decreased,
          // if they are greater than the step.
          if ((int)$item_weight < --$step) {
            // There is planty room, no need to move anymore.
            break;
          }
          else {
            // Update the weight.
            $filtered_moving_siblings[$item_id] = $step;
          }
        }
        else {
          // While moving the second half of the siblings,
          // all moving siblings' weight are increased,
          // if they are less than the step.
          if ((int)$item_weight < ++$step) {
            // Update the weight.
            $filtered_moving_siblings[$item_id] = $step;
          }
          else {
            // There is planty room, no need to move anymore.
            break;
          }
        }
      }
    }
    
    return ['start_weight' => $weight, 'moving_siblings' => $filtered_moving_siblings];
  }
}