You are here

class RulesEntityController in Rules 7.2

Make sure loaded rule configs are instantiated right.

Hierarchy

Expanded class hierarchy of RulesEntityController

1 string reference to 'RulesEntityController'
rules_entity_info in ./rules.module
Implements hook_entity_info().

File

includes/rules.core.inc, line 16
Rules base classes and interfaces needed for any rule evaluation.

View source
class RulesEntityController extends EntityAPIControllerExportable {

  /**
   * Overridden.
   *
   * @see EntityAPIController::create()
   */
  public function create(array $values = array()) {

    // Default to rules as owning module.
    $values += array(
      'owner' => 'rules',
    );
    return parent::create($values);
  }

  /**
   * Overridden.
   *
   * @see DrupalDefaultEntityController::attachLoad()
   */
  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {

    // Retrieve stdClass records and store them as rules objects in 'data'.
    $ids = array_keys($queried_entities);
    $result = db_select('rules_tags')
      ->fields('rules_tags', array(
      'id',
      'tag',
    ))
      ->condition('id', $ids, 'IN')
      ->execute();
    foreach ($result as $row) {
      $tags[$row->id][] = $row->tag;
    }
    $result = db_select('rules_dependencies')
      ->fields('rules_dependencies', array(
      'id',
      'module',
    ))
      ->condition('id', $ids, 'IN')
      ->execute();
    foreach ($result as $row) {
      $modules[$row->id][] = $row->module;
    }
    $entities = array();
    foreach ($queried_entities as $record) {
      $entity = $record->data;

      // Set the values of the other columns.
      foreach ($this->entityInfo['schema_fields_sql']['base table'] as $field) {
        $entity->{$field} = $record->{$field};
      }
      unset($entity->data, $entity->plugin);

      // Add any tags or dependencies.
      $entity->dependencies = isset($modules[$entity->id]) ? $modules[$entity->id] : array();
      $entity->tags = isset($tags[$entity->id]) ? $tags[$entity->id] : array();
      $entities[$entity->id] = $entity;
    }
    $queried_entities = $entities;
    parent::attachLoad($queried_entities, $revision_id);
  }

  /**
   * Override to support having events and tags as conditions.
   *
   * @see EntityAPIController::applyConditions()
   * @see rules_query_rules_config_load_multiple_alter()
   */
  protected function applyConditions($entities, $conditions = array()) {
    if (isset($conditions['event']) || isset($conditions['plugin'])) {
      foreach ($entities as $key => $entity) {
        if (isset($conditions['event']) && (!$entity instanceof RulesTriggerableInterface || !in_array($conditions['event'], $entity
          ->events()))) {
          unset($entities[$key]);
        }
        if (isset($conditions['plugin']) && !is_array($conditions['plugin'])) {
          $conditions['plugin'] = array(
            $conditions['plugin'],
          );
        }
        if (isset($conditions['plugin']) && !in_array($entity
          ->plugin(), $conditions['plugin'])) {
          unset($entities[$key]);
        }
      }
      unset($conditions['event'], $conditions['plugin']);
    }
    if (!empty($conditions['tags'])) {
      foreach ($entities as $key => $entity) {
        foreach ($conditions['tags'] as $tag) {
          if (in_array($tag, $entity->tags)) {
            continue 2;
          }
        }
        unset($entities[$key]);
      }
      unset($conditions['tags']);
    }
    return parent::applyConditions($entities, $conditions);
  }

  /**
   * Overridden to work with Rules' custom export format.
   *
   * @param string $export
   *   A serialized string in JSON format as produced by the
   *   RulesPlugin::export() method, or the PHP export as usual PHP array.
   * @param string $error_msg
   *   The error message.
   */
  public function import($export, &$error_msg = '') {
    $export = is_array($export) ? $export : drupal_json_decode($export);
    if (!is_array($export)) {
      $error_msg = t('Unable to parse the pasted export.');
      return FALSE;
    }

    // The key is the configuration name and the value the actual export.
    $name = key($export);
    $export = current($export);
    if (!isset($export['PLUGIN'])) {
      $error_msg = t('Export misses plugin information.');
      return FALSE;
    }

    // Create an empty configuration, re-set basic keys and import.
    $config = rules_plugin_factory($export['PLUGIN']);
    $config->name = $name;
    foreach (array(
      'label',
      'active',
      'weight',
      'tags',
      'access_exposed',
      'owner',
    ) as $key) {
      if (isset($export[strtoupper($key)])) {
        $config->{$key} = $export[strtoupper($key)];
      }
    }
    if (!empty($export['REQUIRES'])) {
      foreach ($export['REQUIRES'] as $module) {
        if (!module_exists($module)) {
          $error_msg = t('Missing the required module %module.', array(
            '%module' => $module,
          ));
          return FALSE;
        }
      }
      $config->dependencies = $export['REQUIRES'];
    }
    $config
      ->import($export);
    return $config;
  }
  public function save($rules_config, DatabaseTransaction $transaction = NULL) {
    $transaction = isset($transaction) ? $transaction : db_transaction();

    // Load the stored entity, if any.
    if (!isset($rules_config->original) && $rules_config->{$this->idKey}) {
      $rules_config->original = entity_load_unchanged($this->entityType, $rules_config->{$this->idKey});
    }
    $original = isset($rules_config->original) ? $rules_config->original : NULL;
    $return = parent::save($rules_config, $transaction);
    $this
      ->storeTags($rules_config);
    if ($rules_config instanceof RulesTriggerableInterface) {
      $this
        ->storeEvents($rules_config);
    }
    $this
      ->storeDependencies($rules_config);

    // See if there are any events that have been removed.
    if ($original && $rules_config->plugin == 'reaction rule') {
      foreach (array_diff($original
        ->events(), $rules_config
        ->events()) as $event_name) {

        // Check if the event handler implements the event dispatcher interface.
        $handler = rules_get_event_handler($event_name, $rules_config
          ->getEventSettings($event_name));
        if (!$handler instanceof RulesEventDispatcherInterface) {
          continue;
        }

        // Only stop an event dispatcher if there are no rules for it left.
        if (!rules_config_load_multiple(FALSE, array(
          'event' => $event_name,
          'plugin' => 'reaction rule',
          'active' => TRUE,
        )) && $handler
          ->isWatching()) {
          $handler
            ->stopWatching();
        }
      }
    }
    return $return;
  }

  /**
   * Save tagging information to the rules_tags table.
   */
  protected function storeTags($rules_config) {
    db_delete('rules_tags')
      ->condition('id', $rules_config->id)
      ->execute();
    if (!empty($rules_config->tags)) {
      foreach ($rules_config->tags as $tag) {
        db_insert('rules_tags')
          ->fields(array(
          'id',
          'tag',
        ), array(
          $rules_config->id,
          $tag,
        ))
          ->execute();
      }
    }
  }

  /**
   * Save event information to the rules_trigger table.
   */
  protected function storeEvents(RulesTriggerableInterface $rules_config) {
    db_delete('rules_trigger')
      ->condition('id', $rules_config->id)
      ->execute();
    foreach ($rules_config
      ->events() as $event) {
      db_insert('rules_trigger')
        ->fields(array(
        'id' => $rules_config->id,
        'event' => $event,
      ))
        ->execute();
    }
  }
  protected function storeDependencies($rules_config) {
    db_delete('rules_dependencies')
      ->condition('id', $rules_config->id)
      ->execute();
    if (!empty($rules_config->dependencies)) {
      foreach ($rules_config->dependencies as $dependency) {
        db_insert('rules_dependencies')
          ->fields(array(
          'id' => $rules_config->id,
          'module' => $dependency,
        ))
          ->execute();
      }
    }
  }

  /**
   * Overridden to support tags and events in $conditions.
   *
   * @see EntityAPIControllerExportable::buildQuery()
   */
  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
    $query = parent::buildQuery($ids, $conditions, $revision_id);
    $query_conditions =& $query
      ->conditions();
    foreach ($query_conditions as &$condition) {

      // One entry in $query_conditions is a string with key '#conjunction'.
      // @see QueryConditionInterface::conditions()
      if (is_array($condition)) {

        // Support using 'tags' => array('tag1', 'tag2') as condition.
        if ($condition['field'] == 'base.tags') {
          $query
            ->join('rules_tags', 'rt', 'base.id = rt.id');
          $condition['field'] = 'rt.tag';
        }

        // Support using 'event' => $name as condition.
        if ($condition['field'] == 'base.event') {
          $query
            ->join('rules_trigger', 'tr', "base.id = tr.id");
          $condition['field'] = 'tr.event';

          // Use like operator to support % wildcards also.
          $condition['operator'] = 'LIKE';
        }
      }
    }
    return $query;
  }

  /**
   * Overridden to also delete tags and events.
   *
   * @see EntityAPIControllerExportable::delete()
   */
  public function delete($ids, DatabaseTransaction $transaction = NULL) {
    $transaction = isset($transaction) ? $transaction : db_transaction();

    // Use entity-load as ids may be the names as well as the ids.
    $configs = $ids ? entity_load('rules_config', $ids) : array();
    if ($configs) {
      foreach ($configs as $config) {
        db_delete('rules_trigger')
          ->condition('id', $config->id)
          ->execute();
        db_delete('rules_tags')
          ->condition('id', $config->id)
          ->execute();
        db_delete('rules_dependencies')
          ->condition('id', $config->id)
          ->execute();
      }
    }
    $return = parent::delete($ids, $transaction);

    // Stop event dispatchers when deleting the last rule of an event set.
    $processed = array();
    foreach ($configs as $config) {
      if ($config
        ->getPluginName() != 'reaction rule') {
        continue;
      }
      foreach ($config
        ->events() as $event_name) {

        // Only process each event once.
        if (!empty($processed[$event_name])) {
          continue;
        }
        $processed[$event_name] = TRUE;

        // Check if the event handler implements the event dispatcher interface.
        $handler = rules_get_event_handler($event_name, $config
          ->getEventSettings($event_name));
        if (!$handler instanceof RulesEventDispatcherInterface) {
          continue;
        }

        // Only stop an event dispatcher if there are no rules for it left.
        if ($handler
          ->isWatching() && !rules_config_load_multiple(FALSE, array(
          'event' => $event_name,
          'plugin' => 'reaction rule',
          'active' => TRUE,
        ))) {
          $handler
            ->stopWatching();
        }
      }
    }
    return $return;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DrupalDefaultEntityController::$cache protected property Whether this entity type should use the static cache.
DrupalDefaultEntityController::$entityCache protected property Static cache of entities, keyed by entity ID.
DrupalDefaultEntityController::$entityInfo protected property Array of information about the entity.
DrupalDefaultEntityController::$entityType protected property Entity type for this controller instance.
DrupalDefaultEntityController::$hookLoadArguments protected property Additional arguments to pass to hook_TYPE_load().
DrupalDefaultEntityController::$idKey protected property Name of the entity's ID field in the entity database table.
DrupalDefaultEntityController::$revisionKey protected property Name of entity's revision database table field, if it supports revisions.
DrupalDefaultEntityController::$revisionTable protected property The table that stores revisions, if the entity supports revisions.
DrupalDefaultEntityController::cleanIds protected function Ensures integer entity IDs are valid.
DrupalDefaultEntityController::filterId protected function Callback for array_filter that removes non-integer IDs.
EntityAPIController::$bundleKey protected property
EntityAPIController::$cacheComplete protected property
EntityAPIController::$defaultRevisionKey protected property
EntityAPIController::buildContent public function Implements EntityAPIControllerInterface. Overrides EntityAPIControllerInterface::buildContent
EntityAPIController::deleteRevision public function Implements EntityAPIControllerRevisionableInterface::deleteRevision(). Overrides EntityAPIControllerRevisionableInterface::deleteRevision
EntityAPIController::query public function Builds and executes the query for loading.
EntityAPIController::renderEntityProperty protected function Renders a single entity property.
EntityAPIController::saveRevision protected function Saves an entity revision.
EntityAPIControllerExportable::$entityCacheByName protected property
EntityAPIControllerExportable::$nameKey protected property
EntityAPIControllerExportable::cacheGet protected function Overridden. Overrides DrupalDefaultEntityController::cacheGet
EntityAPIControllerExportable::cacheGetByName protected function Like cacheGet() but keyed by name.
EntityAPIControllerExportable::cacheSet protected function Overridden. Overrides DrupalDefaultEntityController::cacheSet
EntityAPIControllerExportable::export public function Overridden. Overrides EntityAPIController::export
EntityAPIControllerExportable::invoke public function Overridden to care about reverted bundle entities and to skip Rules. Overrides EntityAPIController::invoke
EntityAPIControllerExportable::load public function Overridden to support passing numeric ids as well as names as $ids. Overrides EntityAPIController::load
EntityAPIControllerExportable::resetCache public function Overrides DrupalDefaultEntityController::resetCache(). Overrides EntityAPIController::resetCache
EntityAPIControllerExportable::view public function Implements EntityAPIControllerInterface. Overrides EntityAPIController::view
EntityAPIControllerExportable::__construct public function Overridden. Overrides EntityAPIController::__construct
RulesEntityController::applyConditions protected function Override to support having events and tags as conditions. Overrides EntityAPIControllerExportable::applyConditions
RulesEntityController::attachLoad protected function Overridden. Overrides EntityAPIControllerExportable::attachLoad
RulesEntityController::buildQuery protected function Overridden to support tags and events in $conditions. Overrides EntityAPIControllerExportable::buildQuery
RulesEntityController::create public function Overridden. Overrides EntityAPIController::create
RulesEntityController::delete public function Overridden to also delete tags and events. Overrides EntityAPIControllerExportable::delete
RulesEntityController::import public function Overridden to work with Rules' custom export format. Overrides EntityAPIController::import
RulesEntityController::save public function Overridden to care exportables that are overridden. Overrides EntityAPIControllerExportable::save
RulesEntityController::storeDependencies protected function
RulesEntityController::storeEvents protected function Save event information to the rules_trigger table.
RulesEntityController::storeTags protected function Save tagging information to the rules_tags table.