在升级脚本中以编程方式删除商店视图


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中执行升级脚本时,只会调用在该区域中配置的事件观察器(请参阅Magento结构更新与数据更新)。在该区域中定义了某些与事件类似的观察者,因此不会被执行。商店视图删除不会像在后端执行的删除那样处理。globalenterprise_cmsenterprise_searchstore_delete_afteradminhtml

您如何处理这样的操作?您自己在升级脚本中加载其他事件区域(我担心)吗?不要在升级脚本中进行类似的数据修改,而是将魔术笔迹放置在神圣的隐藏地方并手动执行吗?


1
为什么需要有问题地删除商店视图?
oleksii.svarychevskyi 2014年

因为我们有多个环境,所以以编程方式进行所有配置更改都更快,更可靠。
Matthias Zeis 2014年

是否找到解决方案?您会多久这样做一次?就我个人而言,我会选择第二个选项“不要在升级脚本中进行数据修改,而是将您的魔术笔迹放在神圣的隐藏地方并手动执行?”
ProxiBlue 2014年

我推迟了这个问题,因为还有更多重要的事情要做。Magento SE自己的@philwinkle在Twitter上回答:“我在队列中进行这些操作;或者侦听并触发串联adminhtml调度事件(也称为假事件)”(twitter.com/philwinkle/status/428183845985210369)。我认为这对我来说太冒险了,尽管我不喜欢这种方法,但我会手动进行。
Matthias Zeis 2014年

@MatthiasZeis您可以将其添加为答案并接受它来减少未回答的问题吗?
桑德·曼格尔

Answers:


6

因此,在我将其发布到Matthias上的那一刻之后,我就保持了无线电的沉默。希望您已经等待了几个星期,所以您感到悬念。

我的意思是“我在队列中进行这些操作”是直接响应:

在adminhtml区域中定义了某些针对事件store_delete_after的观察者,例如enterprise_cms和enterprise_search,因此不会执行它们。商店视图删除不会像在后端执行的删除那样处理。

排队方法:

当我知道某些事件不会在正确的上下文中触发时(大多数情况下是针对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”方法- 艾伦(Alan)提供了相当不错的解释来说明如何影响该方法,但从本质上讲,它与此相同:

#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()
    ));        
    //..
}

客户保存的admin版本将调用常规模型保存,然后分派adminhtml事件。如果您愿意,可以自己在观察者中进行相反的操作。


5

该死,我爱我一点点,但我不同意将任务参数区域(adminhtml | crontab | frontend | global | install)传输到队列的复杂性/脆弱性,尤其是如果该队列将要执行时Magento环境。如果存在需要处理的混合上下文,则队列解决方案是对当前“问题”的重新实现!

我认为队列方法很脆弱。我的观点是,过早加载事件区域根本不是问题。为了解释这一点,让我们备份并查看问题:

在执行范围内过早加载事件区域有什么危险?

为了理解这一点,我们必须检查执行上下文中的事件区域。Matthias,我想您已经知道这一点,但是对于其他人来说很有趣:

Mage_Core_Model_App::run()将请求分发到前端控制器之前,将执行数据设置脚本:

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;
}

在执行数据设置脚本时,将加载全局事件区域。路由上下文事件区域(frontendadminhtml)稍后被加载,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_beforecontroller_front_init_routers
  2. 该请求是一个前端请求。

#1应该很容易grep。#2是真正的关注点,我认为反射可以解决问题(请注意,我对使用反射没有任何经验):

<?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对象。


1
我不值得!!我不值得!!!!
philwinkle 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.