アップグレードスクリプトでプログラムでストアビューを削除する


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_cmsenterprise_searchイベントのためにstore_delete_afterで定義されているadminhtml、彼らが実行され得ることはありませんので、エリア。ストアビューの削除は、バックエンドで実行される削除のようには処理されません。

このような操作はどのように処理しますか?アップグレードスクリプトで追加のイベントエリアを自分で読み込みます(私はそれを恐れています)?アップグレードスクリプトでそのようなデータ変更を行うのではなく、魔法のスクリプトを神聖な隠された場所に置いて、手動で実行しますか?


1
ストアビューを問題なく削除する必要があるのはなぜですか?
oleksii.svarychevskyi 14年

いくつかの環境があり、すべての構成変更をプログラムで行う方が迅速で信頼性が高いためです。
マティアスツァイス14年

これに対する解決策を見つけましたか?どのくらいの頻度でこれを行いますか?個人的には、2番目のオプション「アップグレードスクリプトでそのようなデータ変更を行わず、魔法のスクリプトを神聖な隠し場所に置いて手動で実行しますか?」を選択します。
ProxiBlue

もっと重要なことがあるので、この問題を延期しました。Magento SEの@philwinkleがTwitterで回答しました。「私はこれらをキューで実行します。または、タンデムadminhtmlディスパッチイベント(別名フェイクイット)を聞いて起動します」(twitter.com/philwinkle/status/428183845985210369)。これは私にとってリスクが大きすぎると思います。このアプローチは好きではありませんが、手動で行います。
マティアスツァイス14

@MatthiasZeisは、それを回答として追加し、それを受け入れて、未回答の質問がカウントダウンされるようにできますか?
サンダーマンジェル

回答:


6

だから、これをマティアスにツイートした直後に、私は無音状態になりました。数週間この回答を待っていたので、サスペンスを感じたことを願っています。

「キューでこれらを行う」とは、次のことに直接応答することを意味します。

イベントstore_delete_afterのenterprise_cmsやenterprise_searchのような特定のオブザーバーは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));
        }
    }
}

あなたが空想を得ているなら、あなたはtry / catchでラップし、トランザクションでラップすることは明らかです。あなたは要点を知っていると思います。

これは、イベントが発生するコンテキストを制御する唯一の方法です。

タンデムイベントメソッド:

自分で「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()
    ));        
    //..
}

顧客の保存の管理者バージョンは、通常のモデル保存を呼び出し、その後、adminhtmlイベントをディスパッチします。必要に応じて、オブザーバーでこの逆を行うことができます。


5

くそー、私は私にいくつかのツルニチニチソウが大好きですが、タスクのパラメーター領域の輸送の複雑さ/脆弱性に同意する必要があります( adminhtml | crontab | frontend | global | install)をキュー、特にそのキューが実行されること必要がありますMagentoコンテキスト。処理が必要な混合コンテキストがある場合、キューソリューションは現在の「問題」の再実装です。

キューのアプローチは脆弱だと思います。私の主張は、イベントエリアを時期尚早にロードすることは本当に問題ではないということです。これを説明するために、バックアップして問題を見てみましょう。

実行スコープでイベント領域を時期尚早にロードする危険性は何ですか?

これを理解するには、実行コンテキストのイベント領域を調べる必要があります。マティアス、あなたはすでにこれを知っていると思いますが、他の人の啓発のために:

データ設定スクリプトはで実行されます 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;
}

データセットアップスクリプトが実行されるまでに、グローバルイベントエリアがロードされます。ルーティングコンテキストイベントエリア(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);

次に、2つの危険のみがあります。

  1. オブザーバーが下で誤って設定されています adminhtmlのcontroller_front_init_beforeまたはcontroller_front_init_routers
  2. リクエストは フロントエンド要求です。

#1は簡単にgrepできるはずです。#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オブジェクトは削除されます。


1
私は価値がない!私は立派ではありません!!!!
philwinkle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.