Löschen Sie die Geschäftsansicht programmgesteuert im Upgrade-Skript

12

Ich möchte eine Geschäftsansicht programmgesteuert löschen . Betrachtet man Mage_Adminhtml_System_StoreController::deleteStorePostAction(), ist dies ziemlich einfach (etwas verkürzter Code):

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

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

Ich möchte diesen Code in ein Datenaktualisierungsskript einfügen , damit der Löschvorgang automatisch ausgeführt wird.

Das Problem ist, dass während der Ausführung der Upgrade-Skripte in data/Magento nur Ereignisbeobachter aufgerufen werden , die in diesem globalBereich konfiguriert sind (siehe Magento-Strukturupdates vs. Datenupdates ). Bestimmte Beobachter wie enterprise_cmsund enterprise_searchfür das Ereignis store_delete_aftersind in dem adminhtmlBereich definiert , damit sie nicht hingerichtet werden. Das Löschen der Geschäftsansicht wird nicht wie ein im Backend ausgeführtes Löschen behandelt.

Wie gehen Sie mit solchen Vorgängen um? Zusätzliche Eventbereiche selbst in die Upgrade-Skripte laden (ich habe Angst davor)? Nehmen Sie im Upgrade-Skript keine derartigen Datenänderungen vor, sondern legen Sie Ihre magischen Scrips an einen heiligen, verborgenen Ort und führen Sie sie manuell aus.

Matthias Zeis
quelle
1
Warum müssen Sie die Geschäftsansicht problematisch löschen?
oleksii.svarychevskyi
Weil wir mehrere Umgebungen haben und es schneller und zuverlässiger ist, alle Konfigurationsänderungen programmgesteuert vorzunehmen.
Matthias Zeis
Haben Sie jemals eine Lösung dafür gefunden? Wie oft würdest du das machen? Ich persönlich würde mich für die zweite Option entscheiden: "Nehmen Sie im Upgrade-Skript keine derartigen Datenänderungen vor, sondern legen Sie Ihre magischen Scrips an einen heiligen, verborgenen Ort und führen Sie sie manuell aus."
ProxiBlue
Ich habe dieses Problem verschoben, weil es wichtigere Dinge zu tun gab. Magento SE's eigenes @philwinkle antwortete auf Twitter: "Ich mache das in einer Warteschlange oder höre und feuere ein Tandem-Adminhtml-Versandereignis (auch bekannt als Fake-It)" ( twitter.com/philwinkle/status/428183845985210369 ). Ich glaube, das ist zu riskant für mich und obwohl ich diesen Ansatz nicht liebe, werde ich es manuell tun.
Matthias Zeis
@MatthiasZeis kannst du das als Antwort hinzufügen und akzeptieren, damit die unbeantworteten Fragen nach unten zählen?
Sander Mangel

Antworten:

6

So kurz nachdem ich dies an Matthias getwittert hatte, ging ich in Funkstille. Ich hoffe, Sie haben die Spannung gespürt, als Sie diese Antwort seit ein paar Wochen erwartet haben.

Was ich unter "Ich mache das in einer Warteschlange" verstehe, ist die direkte Antwort auf:

Bestimmte Beobachter wie enterprise_cms und enterprise_search für das Ereignis store_delete_after werden im Bereich adminhtml definiert, damit sie nicht ausgeführt werden. Das Löschen der Geschäftsansicht wird nicht wie ein im Backend ausgeführtes Löschen behandelt.

Warteschlangenmethode:

Wenn ich weiß, dass es bestimmte Ereignisse gibt, die im richtigen Kontext nicht ausgelöst werden (hauptsächlich für EE, aber möglicherweise in anderen Kontexten), schiebe ich die Löschung normalerweise in eine Warteschlange, damit sie im gewünschten Kontext ausgeführt wird .

Mit anderen Worten, erstellen Sie eine Warteschlangentabelle (oder Warteschlange / Thema in RabbitMQ usw.), die Details der Transaktion und der Ereignis-Hooks enthält, die sie abhören soll. Dies kann so elegant oder so simpel sein, wie Sie es möchten. Ist hier ein grundlegendes

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

Und dann bearbeiten Sie die Warteschlange später in einem CRON, in dem Sie nun steuern können, welcher Speicher "ausgeführt wird" (auch bekannt als "Administrator", Speicher 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));
        }
    }
}

Offensichtlich, wenn Sie Lust bekommen, wickeln Sie in einem Versuch / Fang und wickeln Sie in einer Transaktion. Ich denke, du verstehst das Wesentliche.

Dies ist wahrscheinlich die einzige Möglichkeit, den Kontext zu steuern, in dem das Ereignis ausgelöst wird.

Tandem-Ereignismethode:

Sie können die "adminhtml" -Methode manuell auslösen - Alan gibt eine anständige Erklärung dafür, was Sie tun würden, um das zu beeinflussen , aber im Grunde ist es dasselbe wie das Folgende :

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

Die Administratorversion des Kundenspeichers ruft den regulären Modellspeicher auf und löst anschließend das adminhtml-Ereignis aus. Sie können dies bei einem Beobachter auch umgekehrt tun, wenn Sie dies wünschen.

Philwinkle
quelle
5

Verdammt, ich liebe mich ein bisschen, aber ich muss der Komplexität / Sprödigkeit des Transports der Task-Parameter und des Bereichs ( adminhtml | crontab | frontend | global | install ) in eine Warteschlange widersprechen , insbesondere wenn diese Warteschlange ausgeführt wird ein Magento-Kontext. Wenn es gemischte Kontexte gibt, die behandelt werden müssen, ist die Warteschlangenlösung eine Neuimplementierung des aktuellen "Problems"!

Ich denke, der Ansatz der Warteschlange ist spröde. Mein Argument ist, dass das vorzeitige Laden von Ereignisbereichen überhaupt kein Problem darstellt. Um dies zu erklären, wollen wir uns das Problem ansehen:

Welche Gefahr besteht, wenn ein Ereignisbereich in einem Ausführungsbereich vorzeitig geladen wird?

Um dies zu verstehen, müssen wir Ereignisbereiche im Ausführungskontext untersuchen. Matthias, ich stelle mir vor, dass du das schon weißt, aber für die Erbauung anderer:

Daten-Setup-Skripte werden ausgeführt, Mage_Core_Model_App::run()bevor die Anforderung an den Front Controller gesendet wird:

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

Zu dem Zeitpunkt, an dem Daten-Setup-Skripte ausgeführt werden, wird der globale Ereignisbereich geladen. Die routing-kontextuellen Ereignisbereiche ( Frontend oder Adminhtml ) werden später Mage_Core_Controller_Varien_Action::preDispatch()als Ergebnis eines Routers geladen, der mit einer Controller-Aktion übereinstimmt (der areaName wird über die Vererbung festgelegt):

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

Also normalerweise bei App - Initialisierung nur die Beobachter unter dem konfigurierten globalen Veranstaltungsbereich ausgeführt. Wenn das Setup-Skript etwas wie

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

Dann gibt es nur zwei Gefahren:

  1. Ein Beobachter wurde unter adminhtml falsch konfiguriert , um ein kontextloses Ereignis wie controller_front_init_beforeoder zu beobachtencontroller_front_init_routers
  2. Die Anfrage ist eine Frontend- Anfrage.

# 1 sollte leicht zu finden sein. # 2 ist das eigentliche Problem und ich denke, dass Reflection das Problem lösen kann (beachte, dass ich mit Reflection absolut unerfahren bin):

<?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()

Ich habe das nicht getestet, aber es entfernt den adminhtml-Ereignisindex und das entsprechende Mage_Core_Model_App_AreaObjekt.

benmarks
quelle
1
Ich bin nicht würdig!! Ich bin nicht würdig!!!!
Philwinkle