class RulesEntityController

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

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

Title Sort descending Modifiers Object type Summary
RulesEntityController::applyConditions protected function Override to support having events and tags as conditions.
RulesEntityController::attachLoad protected function Overridden.
RulesEntityController::buildQuery protected function Overridden to support tags and events in $conditions.
RulesEntityController::create public function Overridden.
RulesEntityController::delete public function Overridden to also delete tags and events.
RulesEntityController::import public function Overridden to work with Rules' custom export format.
RulesEntityController::save public function
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.