You are here

class PartyDataBase in Party 8.2

Class PartyDataBase

This class manages the attachement of entities to a party, including loading, ordering, attaching and detaching entities. It also provides a helper for creating new attached entities.

Hierarchy

Expanded class hierarchy of PartyDataBase

File

lib/Drupal/party/Plugin/PartyDataBase.php, line 19
Contains \Drupal\party\Plugin\PartyDataBase.

Namespace

Drupal\party\Plugin
View source
class PartyDataBase {

  /**
   * The party data name.
   *
   * @var string
   */
  protected $dataName;

  /**
   * The party data configuration.
   *
   * @var string
   */
  protected $dataConfig;

  /**
   * An array of attatched entities, keyed by delta. This can contain a mix
   * of stub entities (with the is_stub property set to TRUE), fully loaded
   * entities and unsaved entities (with the is_new property set to TRUE).
   *
   * @var array
   */
  protected $entities = array();

  /**
   * The party object.
   *
   * @var Party
   */
  protected $party;

  /**
   * An array of action objects for this data set.
   *
   * @var array
   */
  protected $actions = array();

  /**
   * Constructor
   *
   * @param party $party
   *   The party object.
   * @param string $plugin_id
   *   The data set name.
   * @param array $configuration
   *   The data set info.
   */
  public function __construct(Party $party, $plugin_id, $configuration) {
    $this->dataName = $plugin_id;
    $this->dataConfig = $configuration;
    $this->party = $party;
    if (!empty($this->party
      ->id())) {

      // Without attached entities, this controller is pretty useless, so let's
      // assume anyone wants them loaded, but we'll do so using stubs to save a
      // full entity load.
      $query = db_select('party_attached_entity', 'ae')
        ->fields('ae', array(
        'delta',
        'entity_id',
      ))
        ->condition('pid', $this->party
        ->id(), '=')
        ->condition('data_set_name', $this->dataName);
      $result = $query
        ->execute()
        ->fetchAllAssoc('delta');

      // Stub entities no longer exist in drupal 8. So we create a stdClass for now.
      foreach ($result as $delta => $entity) {
        $stub = new stdClass();
        $stub->id = $entity->entity_id;
        $stub->bundle = $this
          ->getDataInfo('entity bundle');
        $stub->is_stub = TRUE;
        $this->entities[$delta] = $stub;
      }
    }
  }

  /**
   * Method to return the party object
   */
  public function getParty() {
    return $this->party;
  }

  /**
   * Get information from the data set or entity definition.
   *
   * @param string $key
   *   This is the information you want:
   *   - 'type': the data set entity type.
   *   - 'bundle': the data set entity bundle.
   *   - 'id key': the entity id key.
   *   - 'bundle key': the entity bundle key.
   *
   * @todo: Work out if this is depricated, in favour of
   *   drupal_container()->get('plugin.manager.partydata')->getDefinition().
   */
  public final function getDataInfo($key) {
    $entity_info = drupal_container()
      ->get('plugin.manager.entity')
      ->getDefinition($this->dataConfig['entity type']);

    // Return the relevant information
    switch ($key) {

      // Data Set Info
      case 'name':
        return $this->dataName;
      case 'path element':
        return $this->dataConfig['path element'];
      case 'label':
        return $this->dataConfig['label'];
      case 'entity type':
        return $this->dataConfig['entity type'];
      case 'entity bundle':
        return $this->dataConfig['entity bundle'];

      // entity Info
      case 'id key':
        return $entity_info['entity keys']['id'];
      case 'bundle key':
        return $entity_info['entity keys']['bundle'];
    }
  }

  /**
   * Get the delta from an entity object.
   *
   * @param $entity
   *   The entity object we want to find the delta for.
   *
   * @return int|FALSE
   *   The delta of $entity, or FALSE if it isn't attached.
   */
  public function getEntityDelta($entity) {

    // If $entity is saved, we can just compare entity ids.
    if ($entity
      ->isNew()) {

      // Flip our entity ids so it's delta keyed by id.
      $deltas = array_flip($this
        ->getEntityIds());
      if (isset($deltas[$entity
        ->id()])) {
        return $deltas[$entity
          ->id()];
      }
    }
    else {
      foreach ($this->entities as $delta => $row) {
        if ($entity === $row) {
          return $delta;
        }
      }
    }

    // If we've got here, we can't find it.
    return FALSE;
  }

  /**
   * Load the full entities.
   *
   * @param array|null $deltas
   *   (optional) An array of delta(s) to load or NULL to load all entities.
   *
   * @return $this
   */
  public function loadEntities($deltas = NULL) {

    // Get our array of deltas
    if ($deltas === NULL) {

      // Load all entities
      $deltas = array_keys($this->entities);
    }

    // Iterate over our deltas loading our entities if required.
    foreach ($deltas as $delta) {
      if (isset($this->entities[$delta]) && isset($this->entities[$delta]->is_stub)) {

        // This entity is a stub so needs loading. First get the entity id from
        // stub entity.
        $entity_id = $this->entities[$delta]->id;
        $entities_loaded = drupal_container()
          ->get('plugin.manager.entity')
          ->getStorageController($this
          ->getDataInfo('entity type'))
          ->load(array(
          $entity_id,
        ));
        $this->entities[$delta] = reset($entities_loaded);

        // Add the other properties party needs.
        $this->entities[$delta]->data_set_name = $this->dataName;
        $this->entities[$delta]->party_attaching_party = $this->party
          ->id();
      }
    }
    return $this;
  }

  /**
   * Get a particular attached entity
   *
   * @param int $delta
   *   (optional) A delta to get. Defaults to 0.
   * @param bool $create
   *   (optional) Create an entity if it doesn't exist. Defaults to FALSE.
   *
   * @return mixed
   *   An entity object or one doesn't exist and we're not creating, FALSE.
   */
  public function getEntity($delta = 0, $create = FALSE) {

    // If this delta exists, check it's loaded and return it.
    if (isset($this->entities[$delta])) {
      if (isset($this->entities[$delta]->is_stub)) {
        $this
          ->loadEntities(array(
          $delta,
        ));
      }
    }
    else {

      // Let's create an entity and attach it, but no saving happens unless
      // explicitly called. To maintain unsaved entities, this data controller
      // can be stored against forms etc.
      if ($create) {
        $entity = $this
          ->createEntity();
        $this
          ->attachEntity($entity, 'insert', FALSE, $delta);
      }
      else {

        // Nothing found and not creating; return false.
        return FALSE;
      }
    }
    return $this->entities[$delta];
  }

  /**
   * Get all attached entities
   *
   * @return array
   *   An array of all this data set's entities keyed by their delta.
   */
  public function getEntities() {
    $this
      ->loadEntities();
    return $this->entities;
  }

  /**
   * Get entity ids/deltas
   *
   * This allows you to get the entity ids and deltas of all the entities
   * without having to load them. There may also be sub entities, in which
   * case the value for that delta will be an empty string, as to not throw
   * a warning if the array is flipped.
   *
   * @return array
   *   Array of entity ids keyed by delta. Unsaved entities return an empty
   *   string.
   */
  public function getEntityIds() {
    $ids = array();
    foreach ($this->entities as $delta => $entity) {
      if (!$entity
        ->isNew()) {
        $ids[$delta] = $entity->is_stub ? $entity->id : $entity
          ->id();
      }
      else {

        // This is unsaved, so we use an empty string so warnings don't get
        // emitted on an array_flip().
        $ids[$delta] = '';
      }
    }
    return $ids;
  }

  /**
   * Get a PartyDataSetAction class for an action on this data set.
   *
   * @param $action
   *   The action name, as given in the data set info.
   *
   * @throws Exception if the $action provided is not defined for this data
   *   set.
   */
  public function getAction($action) {

    // If we've already built this action controller don't do so again.
    if (!empty($this->actions[$action])) {
      return $this->actions[$action];
    }

    // Check the action is legit.
    $info = party_get_data_set_info($this->data_set);
    if (empty($info['actions'][$action])) {
      throw new Exception(t('Action: @action does not exist for data set @data_set', array(
        '@action' => $action,
        '@data_set' => $this->data_set,
      )));
    }
    $this->actions[$action] = new $info['actions'][$action]['controller']($this, $action);
    return $this->actions[$action];
  }

  /**
   * Get the label of one of our attached entities.
   *
   * @param int $delta
   *  The delta of the entity to render.
   *
   * @return
   *  The text of the label.
   */
  public function getLabel($delta) {
    return $this
      ->getEntity($delta)
      ->label();
  }

  /**
   * Create a new entity
   *
   * This method provides a helper to create a new attached entity for the data
   * set without having to figure out the entity type and bundle.
   *
   * @return object
   *   A newly created unsaved entity of the correct type and bundle.
   */
  public function createEntity() {

    // Create a placeholder entity
    $values = array();
    if ($this
      ->getDataInfo('bundle key')) {
      $values[$this
        ->getDataInfo('bundle key')] = $this
        ->getDataInfo('entity bundle');
    }

    // Create the entity, set our data set and return
    $entity = drupal_container()
      ->get('plugin.manager.entity')
      ->getStorageController($this
      ->getDataInfo('entity type'))
      ->create($values);

    // Add the other properties party needs.
    $entity->data_set_name = $this->data_set;
    if (isset($this->party->pid)) {
      $entity->party_attaching_party = $this->party
        ->id();
    }
    return $entity;
  }

  /**
   * Save an entity.
   *
   * This method saves the entity at the position specified by delta. This
   * allows for other modules to use different logic for saving their entities.
   *
   * @param int $delta
   *   (optional) The delta of the entity to save. Defaults to 0.
   *
   * @return EntityInterface
   *   The entity that has been saved.
   */
  public function saveEntity($delta = 0) {
    $entity = $this
      ->getEntity($delta);

    // If getting the entity failed, return false.
    if (!$entity) {
      return $entity;
    }
    drupal_container()
      ->get('plugin.manager.entity')
      ->getStorageController($this
      ->getDataInfo('entity type'))
      ->save($entity);
    return $entity;
  }

  /**
   * Attach an entity to the party
   *
   * This method puts the entity in the right place in the $entities array.
   * NB: This does not save the new order, you must call
   * PartyDefaultData::save() to do that. This method cannot be overloaded
   * and any extensions of this class can make use of
   * PartyDefaultDataSet::preAttach() and PartyDefaultDataSet::postAttach()
   * to perform any additional logic required.
   *
   * @param object $entity
   *   The entity we're attaching.
   * @param string $method
   *   The $method we're using can be one of:
   *   - 'append' (default): the entity will be added to the end of the list.
   *   - 'prepend': the entity will be added at the front of the list.
   *   - 'insert': the entity will be inserted at $delta.
   * @param bool $reattach
   *   If this entity is already attached, should we remove it and then
   *   reattach using the requested method. Defaults to FALSE.
   * @param int $delta
   *   If the $method is set to insert. This is the target delta of the
   *   attached entity.
   *
   * @return $this
   *
   * @see PartyDefaultDataSet::preAttach()
   * @see PartyDefaultDataSet::postAttach()
   */
  public final function attachEntity($entity, $method = 'append', $reattach = FALSE, $delta = 0) {

    // Fire any pre attach logic
    $this
      ->preAttach($entity, $method, $delta);

    // Check if this entity is already attached
    $delta = $this
      ->getEntityDelta($entity);
    if ($delta !== FALSE) {
      if ($reattach) {

        // Do this manually as we don't want to trigger the pre/post callbacks.
        unset($this->entities[$delta]);
        $this->entities = array_values($this->entities);
        $delta = FALSE;
      }
      else {

        // Let's make sure our attached entity if fully loaded and update it if necessary
        $this->entities[$delta] = $entity;
      }
    }

    // Only attach if it wasn't already and hasn't been detached.
    if ($delta === FALSE) {
      switch ($method) {
        case 'append':
          $this->entities[] = $entity;
          break;
        case 'prepend':
          array_unshift($this->entities, $entity);
          break;
        case 'insert':

          // Put the entity and its availability in the right place
          array_splice($this->entities, $delta, 0, $entity);
          break;
      }
    }

    // Fire any post attach logic
    $this
      ->postAttach($entity, $method, $delta);
    return $this;
  }

  /**
   * Overload any pre attach logic
   *
   * Both $method and $delta can be taken by reference, allowing you to make
   * alterations to the attach behaviour.
   *
   * @param object $entity
   * @param string $method
   * @param int $delta
   *
   * @see PartyData::attachEntity()
   */
  protected function preAttach($entity, &$method, &$delta) {
  }

  /**
   * Overload any post attach logic
   *
   * @param object $entity
   * @param string $method
   * @param int $delta
   *
   * @see PartyData::attachEntity()
   */
  protected function postAttach($entity, $method, $delta) {
  }

  /**
   * Detach an entity
   *
   * This is a helper method to detach an entity when you have it's object.
   * This method cannot be overloaded and any extensions of this class can
   * make use of PartyDefaultDataSet::preAttach() and
   * PartyDefaultDataSet::postAttach() to perform any additional logic
   * required.
   *
   * @param int $delta
   *   The delta of the entity to detach
   *
   * @return $this
   *
   * @see PartyDefaultDataSet::detchEntityByDelta()
   * @see PartyDefaultDataSet::preDetach()
   * @see PartyDefaultDataSet::postDetach()
   */
  public final function detachEntity($entity) {

    // Figure out our delta
    $delta = $this
      ->getEntityDelta($entity);

    // Pass this onto the detachEntityByDelta method to actually detach
    $this
      ->detachEntityByDelta($delta);
    return $this;
  }

  /**
   * Detatch an entity by delta
   *
   * This method cannot be overloaded and any extensions of this class can
   * make use of PartyDefaultDataSet::preAttach() and
   * PartyDefaultDataSet::postAttach() to perform any additional logic
   * required.
   *
   * @param int $delta
   *   The delta of the entity to detach
   * @param bool $return
   *   Whether you want to return the detached entity or $this for chaining.
   *   Defaults to FALSE.
   *
   * @return object|$this
   *   Depending on $return, either the detached entity or this for chaining.
   *
   * @see PartyDefaultDataSet::preDetach()
   * @see PartyDefaultDataSet::postDetach()
   */
  public final function detachEntityByDelta($delta, $return = FALSE) {

    // Fire any pre attach logic
    $this
      ->preDetach($delta);
    if (isset($this->entities[$delta])) {

      // Get our entity for returning if requested
      $entity = $this->entities[$delta];

      // Detach our entity
      unset($this->entities[$delta]);

      // Reset our numeric indexes
      $this->entities = array_values($this->entities);
    }
    else {

      // Can't return the entity if it didn't exist
      $entity = FALSE;
    }

    // Fire any post attach logic
    $this
      ->postDetach($delta);
    if ($return) {
      return $entity;
    }
    else {
      return $this;
    }
  }

  /**
   * Overload any pre detach logic
   *
   * $delta can be taken by reference, allowing you to make alterations to the
   * detach behaviour.
   *
   * @param int $delta
   *
   * @see PartyData::detachEntity()
   */
  protected function preDetach($delta) {
  }

  /**
   * Overload any post detach logic
   *
   * @param int $delta
   *
   * @see PartyData::detachEntity()
   */
  protected function postDetach($delta) {
  }

  /**
   * Save the attached entities information
   *
   * This method cannot be overloaded and any extensions of this class can
   * make use of PartyDefaultDataSet::preSave() and
   * PartyDefaultDataSet::postSave() to perform any additional logic required.
   *
   * @param bool $save_entities
   *   Whether or not to save entities as we go through them. If an entity is
   *   unsaved, we will always save it, as otherwise we can't save the deltas.
   *
   * @return $this
   *
   * @todo Remove the need to save entity type and bundle, as it's easy to
   *   sniff our from the data set.
   *
   * @see PartyDefaultDataSet::preSave()
   * @see PartyDefaultDataSet::postSave()
   */
  public final function save($save_entities = FALSE) {

    // Fire any pre attach logic
    $this
      ->preSave($save_entities);

    // Clear out our old bits
    $query = db_delete('party_attached_entity');
    $query
      ->condition('pid', $this->party
      ->id(), '=');
    $query
      ->condition('data_set_name', $this->dataName);
    $query
      ->execute();

    // Insert our entities
    $query = db_insert('party_attached_entity');
    $query
      ->fields(array(
      'party_id',
      'entity_id',
      'delta',
      'data_set_name',
      'entity_type',
      'entity_bundle',
    ));
    foreach ($this->entities as $delta => &$entity) {
      if ($save_entities || $entity
        ->isNew()) {

        // Save our entities as we go
        $this
          ->saveEntity($delta);
      }

      // Add our record
      $query
        ->values(array(
        'party_id' => $this->party
          ->id(),
        'eid' => $entity
          ->id(),
        'delta' => $delta,
        'data_set' => $this->dataName,
        'entity_type' => $this
          ->getDataInfo('entity type'),
        'entity_bundle' => $this
          ->getDataInfo('entity bundle'),
      ));
    }
    $query
      ->execute();

    // Update the label whenever attached entities are changed.
    drupal_container()
      ->get('plugin.manager.entity')
      ->getStorageController('party')
      ->setLabel($this->party);

    // Fire any post attach logic
    $this
      ->postSave($save_entities);
    return $this;
  }

  /**
   * Overload any pre save logic
   *
   * @param bool $save_entities
   *   Whether or not to save entities as we go through them.
   *
   * @see PartyData::save()
   */
  protected function preSave($save_entities) {
  }

  /**
   * Overload any post save logic
   *
   * @param bool $save_entities
   *   Whether or not to save entities as we go through them.
   *
   * @see PartyData::save()
   */
  protected function postSave($save_entities) {
  }

  /**
   * Re-order attached entities
   *
   * This will re-order any entities to match the order specified in $order.
   * Any entities that are left out of $order will be appended to the new
   * order. To remove an entity, use PartyData::detachEntity().
   *
   * @param array $order
   *   A numeric array of old deltas in the new order
   *
   * @return $this
   */
  public final function reorderEntities($order) {

    // First we need to collect missed entities
    $missed_entities = array_diff_key($this->entities, array_fill_keys($order, TRUE));
    $entities = array();

    // Iterate over re-ordering our entities
    foreach ($order as $delta) {
      if (isset($this->entities[$delta])) {
        $entities[] = $this->entities[$delta];
      }
    }

    // Append any missed entities
    foreach ($missed_entities as $entity) {
      $entities[] = $entity;
    }

    // Store array values so we don't have any missed
    $this->entities = $entities;
    return $this;
  }

  /**
   * Shift a particular entity in the order
   *
   * @param int $delta
   *   The delta of the item we're shifting
   * @param string $method
   *   The method of shift we want:
   *    - 'up' Shifts the entity up by $target
   *    - 'down' Shifts the entity down by $target
   *    - 'insert' Moves the entity to that position, shifting everything else
   *    - 'swap' Swaps the entity with the entity in $target
   * @param int $target
   *   If method is 'up' or 'down', shift the element by this amount.
   *   If method is 'swap', swap the entity with the entity at this delta.
   *
   * @return $this
   */
  public final function shiftEntity($delta, $method, $target = 1) {
    if (isset($this->entities[$delta])) {
      $entity = $this->entities[$delta];
      switch ($method) {
        case 'up':

          // Calculate our new position
          $newDelta = min(0, $delta - $target);
          unset($this->entities[$delta]);
          array_splice($this->entities, $newDelta, 0, array(
            $entity,
          ));
          break;
        case 'down':

          // Calculate our new position, including removal of the moved entity
          $newDelta = $delta + $target - 1;
          unset($this->entities[$delta]);
          array_splice($this->entities, $newDelta, 0, array(
            $entity,
          ));
          break;
        case 'insert':
          array_splice($this->entities, $target, 0, array(
            $entity,
          ));
          break;
        case 'swap':

          // Make sure we have the other entity if it exists
          if (isset($this->entities[$target])) {
            $otherEntity = $this->entities[$target];
          }

          // Set our new position for this entity
          $this->entities[$target] = $entity;

          // If we had another entity, put it back in place
          if (isset($otherEntity)) {
            $this->entities[$delta] = $otherEntity;
          }

          // Make sure our deltas are numeric and sequencial
          $this->entities = array_values($this->entities);
          break;
      }
    }
    return $this;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PartyDataBase::$actions protected property An array of action objects for this data set.
PartyDataBase::$dataConfig protected property The party data configuration.
PartyDataBase::$dataName protected property The party data name.
PartyDataBase::$entities protected property An array of attatched entities, keyed by delta. This can contain a mix of stub entities (with the is_stub property set to TRUE), fully loaded entities and unsaved entities (with the is_new property set to TRUE).
PartyDataBase::$party protected property The party object.
PartyDataBase::attachEntity final public function Attach an entity to the party
PartyDataBase::createEntity public function Create a new entity
PartyDataBase::detachEntity final public function Detach an entity
PartyDataBase::detachEntityByDelta final public function Detatch an entity by delta
PartyDataBase::getAction public function Get a PartyDataSetAction class for an action on this data set.
PartyDataBase::getDataInfo final public function Get information from the data set or entity definition.
PartyDataBase::getEntities public function Get all attached entities
PartyDataBase::getEntity public function Get a particular attached entity
PartyDataBase::getEntityDelta public function Get the delta from an entity object.
PartyDataBase::getEntityIds public function Get entity ids/deltas
PartyDataBase::getLabel public function Get the label of one of our attached entities.
PartyDataBase::getParty public function Method to return the party object
PartyDataBase::loadEntities public function Load the full entities.
PartyDataBase::postAttach protected function Overload any post attach logic
PartyDataBase::postDetach protected function Overload any post detach logic
PartyDataBase::postSave protected function Overload any post save logic
PartyDataBase::preAttach protected function Overload any pre attach logic
PartyDataBase::preDetach protected function Overload any pre detach logic
PartyDataBase::preSave protected function Overload any pre save logic
PartyDataBase::reorderEntities final public function Re-order attached entities
PartyDataBase::save final public function Save the attached entities information
PartyDataBase::saveEntity public function Save an entity.
PartyDataBase::shiftEntity final public function Shift a particular entity in the order
PartyDataBase::__construct public function Constructor