Usuń widok sklepu programowo w skrypcie aktualizacji

12

Chcę programowo usunąć widok sklepu . Patrząc na Mage_Adminhtml_System_StoreController::deleteStorePostAction()to, jest to dość łatwe (nieco skrócony kod):

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

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

Chcę umieścić ten kod w skrypcie aktualizacji danych, aby usunięcie zostało wykonane automatycznie.

Problem polega na tym, że podczas wykonywania skryptów aktualizacji w data/Magento wywołuje tylko obserwatorów zdarzeń skonfigurowanych w tym globalobszarze (patrz Aktualizacje struktury Magento vs. Aktualizacje danych ). Niektórzy obserwatorzy, tacy jak enterprise_cmsi enterprise_searchdla zdarzenia, store_delete_aftersą zdefiniowani w adminhtmlobszarze, więc nie zostaną straceni. Usunięcie widoku sklepu nie będzie traktowane jak usunięcie wykonane w wewnętrznej bazie danych.

Jak sobie radzisz z takimi operacjami? Sam załaduj dodatkowe obszary zdarzeń w skryptach aktualizacji (obawiam się, że)? Czy nie modyfikujesz danych w skrypcie aktualizacji, ale umieszczasz swoje magiczne skrypty w świętym, ukrytym miejscu i wykonujesz je ręcznie?

Matthias Zeis
źródło
1
Dlaczego musisz problematycznie usuwać widok sklepu?
oleksii.svarychevskyi
Ponieważ mamy kilka środowisk i szybsze i bardziej niezawodne jest wykonywanie wszystkich zmian konfiguracji programowo.
Matthias Zeis
Czy kiedykolwiek znalazłeś rozwiązanie tego problemu? Jak często to robisz? Osobiście wybrałbym drugą opcję „Czy nie modyfikujesz danych w skrypcie aktualizacji, ale umieszczasz swoje magiczne skrypty w świętym ukrytym miejscu i wykonujesz je ręcznie?”
ProxiBlue
Odłożyłem ten problem, ponieważ były ważniejsze rzeczy do zrobienia. Własna Magento SE @philwinkle odpowiedziała na Twitterze: „Robię to w kolejce; lub słucham i odpalam tandemowe wydarzenie adminhtml dispatch (aka fake it)” ( twitter.com/philwinkle/status/428183845985210369 ). Uważam, że jest to dla mnie zbyt ryzykowne i chociaż nie podoba mi się to podejście, zrobię to ręcznie.
Matthias Zeis
@MatthiasZeis, możesz dodać to jako odpowiedź i zaakceptować, aby odliczać pytania bez odpowiedzi?
Sander Mangel

Odpowiedzi:

6

Więc krótko po tym, jak napisałem to na Twitterze do Macieja, przeszedłem w ciszy radiowej. Mam nadzieję, że poczułeś napięcie, czekając na odpowiedź od kilku tygodni.

To, co rozumiem przez „robię to w kolejce”, jest bezpośrednią odpowiedzią na:

Niektóre obserwatory, takie jak enterprise_cms i enterprise_search dla zdarzenia store_delete_after, są zdefiniowane w obszarze adminhtml, więc nie zostaną wykonane. Usunięcie widoku sklepu nie będzie traktowane jak usunięcie wykonane w wewnętrznej bazie danych.

Metoda kolejki:

Kiedy wiem, że istnieją pewne zdarzenia, które nie będą uruchamiane we właściwym kontekście (głównie dla EE, ale mogą mieć zastosowanie w innych kontekstach), zwykle usuwam usunięcie z kolejki, aby działało w kontekście, w którym musi .

Innymi słowy, utwórz tabelę kolejek (lub kolejkę / temat w RabbitMQ itp.), Która zawierałaby szczegóły transakcji i haczyki zdarzeń, których powinna nasłuchiwać. Może to być tak eleganckie lub tak proste, jak chcesz. Oto podstawowa

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

A potem pracuj w kolejce później w CRON, gdzie masz teraz kontrolę nad tym, który sklep „działa” (czyli po prostu uruchamiasz go tak, jakby to był administrator, sklep 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));
        }
    }
}

Oczywiście, jeśli masz ochotę, zawijaj try / catch i zawijaj transakcję. Myślę, że rozumiesz.

Jest to realnie jedyny sposób kontrolowania kontekstu, w którym wydarzenie się uruchamia.

Metoda zdarzenia tandemowego:

Możesz samodzielnie uruchomić metodę „adminhtml” - Alan daje całkiem przyzwoite wyjaśnienie, co byś zrobił, aby na to wpłynąć , ale zasadniczo jest to to samo:

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

Administracyjna wersja składowania klienta wywołuje zwykłe zapisywanie modelu, a następnie wywołuje zdarzenie adminhtml. Możesz zrobić to odwrotnie u obserwatora, jeśli chcesz.

philwinkle
źródło
5

Cholera, kocham mnie trochę dziwactwa, ale muszę się nie zgodzić ze złożonością / kruchością transportu parametrów zadania i obszaru ( adminhtml | crontab | frontend | global | install ) do kolejki, szczególnie jeśli ta kolejka będzie się uruchamiać kontekst Magento. Jeśli istnieją konteksty mieszane, które wymagają obsługi, rozwiązanie kolejki stanowi ponowną implementację bieżącego „problemu”!

Myślę, że podejście do kolejki jest kruche. Moim argumentem jest to, że przedwczesne ładowanie obszarów zdarzeń wcale nie jest problemem. Aby to wyjaśnić, cofnijmy się i spójrzmy na problem:

Jakie jest niebezpieczeństwo przedwczesnego załadowania obszaru zdarzenia w zakresie wykonania?

Aby to zrozumieć, musimy zbadać obszary zdarzeń w kontekście wykonania. Matthias, wyobrażam sobie, że już to wiesz, ale dla budowania innych:

Skrypty konfiguracji danych są wykonywane Mage_Core_Model_App::run()przed wysłaniem żądania do kontrolera frontowego:

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

Do czasu wykonania skryptów konfiguracji danych globalny obszar zdarzeń jest ładowany. Obszary zdarzeń kontekstu routingu ( frontend lub adminhtml ) są ładowane później w Mage_Core_Controller_Varien_Action::preDispatch()wyniku dopasowania routera do akcji kontrolera ( areanazwa jest ustawiana poprzez dziedziczenie):

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

Tak więc normalnie podczas inicjalizacji aplikacji będą wykonywane tylko obserwatory skonfigurowane w obszarze globalnych zdarzeń. Jeśli skrypt instalacyjny robi coś takiego jak

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

wtedy są tylko dwa niebezpieczeństwa:

  1. Obserwator został źle skonfigurowany w adminhtml, aby obserwować zdarzenie bez kontekstu, takie jak controller_front_init_beforelubcontroller_front_init_routers
  2. Żądanie jest żądaniem nakładki .

# 1 powinno być łatwe do pozyskania. # 2 jest prawdziwym problemem i myślę, że Refleksja może rozwiązać problem (zauważ, że jestem bardzo niedoświadczony w używaniu refleksji):

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

Nie przetestowałem tego, ale usuwa indeks zdarzeń adminhtml i odpowiadający mu Mage_Core_Model_App_Areaobiekt.

zalety
źródło
1
Nie jestem warty!! Nie jestem warty!!!!
philwinkle