Удалить вид магазина программно в скрипте обновления

12

Я хочу удалить вид магазина программно . Глядя на Mage_Adminhtml_System_StoreController::deleteStorePostAction()это, это довольно легко (немного сокращенный код):

$model = Mage::getModel('core/store')->load($id);

if ($model->getId() && $model->isCanDelete()) {
    $model->delete();
    Mage::dispatchEvent('store_delete', array('store' => $model));
}

Я хочу поместить этот код в скрипт обновления данных, чтобы удаление выполнялось автоматически.

Проблема заключается в том, что при выполнении сценариев обновления в data/Magento вызывается только наблюдатель событий, настроенный в этой globalобласти (см. Обновления структуры Magento и Обновления данных ). Определенные наблюдатели, такие как enterprise_cmsи enterprise_searchдля события store_delete_after, определены в adminhtmlобласти, поэтому они не будут выполнены. Удаление представления магазина не будет обрабатываться как удаление, выполненное в серверной части.

Как вы справляетесь с такими операциями? Загружать дополнительные области событий самостоятельно в сценарии обновления (я боюсь этого)? Не выполняйте такие модификации данных в сценарии обновления, а кладите свои магические надписи в священное и скрытое место и выполняйте их вручную?

Матиас Цейс
источник
1
Почему вам нужно удалить представление магазина проблематично?
Алексей.Сваричевский
Потому что у нас есть несколько сред, и все изменения конфигурации выполняются быстрее и надежнее.
Матиас Цейс
Когда-нибудь нашли решение для этого? Как часто вы будете это делать? Лично я бы выбрал второй вариант: «Не делайте такие модификации данных в сценарии обновления, а кладите свои магические скрипы в священное скрытое место и выполняйте их вручную?»
ProxiBlue
Я отложил эту проблему, потому что были более важные дела. Собственный Magento SE @philwinkle ответил в Твиттере: «Я делаю это в очереди; или слушаю и запускаю тандемное событие отправки adminhtml (он же фальсифицирует его)» ( twitter.com/philwinkle/status/428183845985210369 ). Я считаю, что это слишком рискованно для меня, и хотя я не люблю такой подход, я сделаю это вручную.
Матиас Зейс
@MatthiasZeis вы можете добавить это в качестве ответа и принять его, чтобы вести обратный отсчет оставшихся без ответа вопросов?
Сандер Мангель

Ответы:

6

Вскоре после того, как я написал это Матиасу, я начал молчать по радио. Я надеюсь, что вы чувствовали неизвестность, поскольку вы ждали этот ответ в течение пары недель.

То, что я имею в виду под «я делаю это в очереди», является прямым ответом на:

Некоторые наблюдатели, такие как enterprise_cms и enterprise_search для события store_delete_after, определены в области adminhtml, поэтому они не будут выполнены. Удаление представления магазина не будет обрабатываться как удаление, выполненное в серверной части.

Метод очереди:

Когда я знаю, что есть определенные события, которые не будут запускаться в правильном контексте (в основном для EE, но могут применяться в других контекстах), я обычно выталкиваю удаление в очередь, чтобы оно выполнялось в контексте, который ему необходим ,

Другими словами, создайте таблицу очередей (или очередь / тему в RabbitMQ и т. Д.), Которая будет содержать подробную информацию о транзакции и обработчиках событий, которые она должна прослушивать. Это может быть настолько элегантно или просто, насколько вы хотите. Вот основной

$queue = Mage::getModel('yourcompany/queue_job')
         ->setJobType('delete')
         ->setEntityType('core/store')
         ->setEntityId(12)
         ->setDispatchEvent('store_delete')
         ->setDispatchEventDataKey('store')
         ->save();

А затем обработайте очередь позже в CRON, где у вас теперь есть контроль над тем, какое хранилище «работает» (иначе вы просто запускаете его, как будто это администратор, хранилище 0):

foreach(Mage::getModel('yourcompany/queue_job')->getCollection() as $job){
    if($job->getJobType()=='delete'){

        $model = Mage::getModel($this->getEntityType())->load($this->getEntityId());

        if ($model->getId() && $model->isCanDelete()) {
            $model->delete();
            Mage::dispatchEvent($job->getDispatchEvent(), array($job->setDispatchEventDataKey() => $model));
        }
    }
}

Очевидно, что если вы начинаете фантазировать, вы заключаете сделку / ловите и заключаете сделку. Я думаю, ты понял суть.

Вероятно, это единственный способ контролировать контекст, в котором происходит событие.

Метод тандемного события:

Вы можете вручную запустить метод «adminhtml» - Алан довольно неплохо объясняет, что бы вы сделали, чтобы повлиять на это , но, по сути, он такой же, как этот:

#File: app/code/core/Mage/Adminhtml/controllers/CustomerController.php
public function saveAction()
{
    //...
    $customer->save();
    //...
    Mage::dispatchEvent('adminhtml_customer_prepare_save', array(
        'customer'  => $customer,
        'request'   => $this->getRequest()
    ));        
    //..
}

Административная версия сохранения клиента вызывает обычное сохранение модели и затем отправляет событие adminhtml. Вы можете сделать обратное в наблюдателе самостоятельно, если пожелаете.

philwinkle
источник
5

Черт возьми, я люблю кое-что, но я должен не согласиться со сложностью / хрупкостью переноса параметров задачи и области ( adminhtml | crontab | frontend | global | install ) в очередь, особенно если эта очередь будет выполняться Magento контекст. Если есть смешанные контексты, которые требуют обработки, тогда решение очереди - это переопределение текущей «проблемы»!

Я думаю, что подход к очереди является хрупким. Мой аргумент заключается в том, что преждевременная загрузка областей событий вообще не является проблемой. Чтобы объяснить это, давайте вернемся назад и посмотрим на проблему:

В чем опасность преждевременной загрузки области события в область выполнения?

Чтобы понять это, мы должны изучить области событий в контексте выполнения. Матиас, я полагаю, что вы уже знаете это, но для наставления других:

Сценарии настройки данных выполняются Mage_Core_Model_App::run()до отправки запроса на Front Controller:

public function run($params)
{
    $options = isset($params['options']) ? $params['options'] : array();
    $this->baseInit($options);
    Mage::register('application_params', $params);

    if ($this->_cache->processRequest()) {
        $this->getResponse()->sendResponse();
    } else {
        $this->_initModules();
//Global event area is loaded here
        $this->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);

        if ($this->_config->isLocalConfigLoaded()) {
            $scopeCode = isset($params['scope_code']) ? $params['scope_code'] : '';
            $scopeType = isset($params['scope_type']) ? $params['scope_type'] : 'store';
            $this->_initCurrentStore($scopeCode, $scopeType);
            $this->_initRequest();
//Data setup scripts are executed here: 
            Mage_Core_Model_Resource_Setup::applyAllDataUpdates();
        }

        $this->getFrontController()->dispatch();
    }
    return $this;
}

К тому времени, когда выполняются сценарии настройки данных, глобальная область событий загружена. Области событий маршрутизации и контекста ( frontend или adminhtml ) загружаются позже в Mage_Core_Controller_Varien_Action::preDispatch()результате того, что маршрутизатор соответствует действию контроллера ( areaимя задается посредством наследования):

public function preDispatch()
{
    //...
    Mage::app()->loadArea($this->getLayout()->getArea());
    //...
}

Поэтому обычно во время инициализации приложения будут выполняться только те наблюдатели, которые сконфигурированы в глобальной области событий. Если сценарий установки делает что-то вроде

$this->loadAreaPart(Mage_Core_Model_App_Area::AREA_ADMINHTML, Mage_Core_Model_App_Area::PART_EVENTS);

тогда есть только две опасности:

  1. Наблюдатель был неправильно настроен в adminhtml для наблюдения за контекстным событием, таким как controller_front_init_beforeилиcontroller_front_init_routers
  2. Запрос является интерфейсом запроса.

№ 1 должно быть легко найти. # 2 - это реальная проблема, и я думаю, что Reflection может решить эту проблему (обратите внимание, что я крайне неопытен в использовании отражения):

<?php

//Start setup script as normal
$installer = $this;
$installer->startSetup()

//Load adminhtml event area
Mage::app()->loadAreaPart(
    Mage_Core_Model_App_Area::AREA_ADMINHTML,
    Mage_Core_Model_App_Area::PART_EVENTS
);

// your setup script logic here

//I hope this isn't a bad idea.
$reflectedApp = new ReflectionClass('Mage_Core_Model_App');
$_areas = $reflectedApp->getProperty('_areas');
$_areas->setAccessible(true);
$areas = $_areas->getValue(Mage::app());
unset($areas['adminhtml']);
$_areas->setValue(Mage::app(),$areas); //reset areas

//End setup script as normal
$installer->endSetup()

Я не проверял это, но он удаляет индекс события adminhtml и соответствующий Mage_Core_Model_App_Areaобъект.

benmarks
источник
1
Я не достоин !! Я не достоин !!!!
Philwinkle