Eliminar la vista de la tienda mediante programación en el script de actualización

12

Quiero eliminar una vista de la tienda mediante programación . Mirando Mage_Adminhtml_System_StoreController::deleteStorePostAction(), esto es bastante fácil (código acortado un poco):

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

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

Quiero poner este código en un script de actualización de datos para que la eliminación se ejecute automáticamente.

El problema es que al ejecutar los scripts de actualización en data/Magento solo se llama a los observadores de eventos configurados en el globalárea (ver Actualizaciones de estructura de Magento vs. Actualizaciones de datos ). Ciertos observadores como enterprise_cmsy enterprise_searchpara el evento store_delete_afterse definen en el adminhtmlárea para que no se ejecuten. La eliminación de la vista de la tienda no se manejará como una eliminación ejecutada en el back-end.

¿Cómo manejas operaciones como esta? ¿Cargar áreas de eventos adicionales en los scripts de actualización (me temo que eso)? ¿No haces modificaciones de datos como esa en la secuencia de comandos de actualización sino que colocas tus secuencias de comandos mágicas en un lugar sagrado escondido y las ejecutas manualmente?

Matthias Zeis
fuente
1
¿Por qué necesita eliminar la vista de la tienda de manera problemática?
oleksii.svarychevskyi
Porque tenemos varios entornos y es más rápido y más confiable hacer todos los cambios de configuración mediante programación.
Matthias Zeis
¿Alguna vez has encontrado una solución para esto? ¿Con qué frecuencia harías esto? Personalmente, iría por la segunda opción "¿No hacer modificaciones de datos como esa en el script de actualización, sino poner sus scripts mágicos en un lugar sagrado escondido y ejecutarlos manualmente?"
ProxiBlue
Pospuse este problema porque había cosas más importantes que hacer. El propio @philwinkle de Magento SE respondió en Twitter: "Hago esto en una cola; o escucho y envío un evento de envío adminhtml en tándem (también conocido como falso)" ( twitter.com/philwinkle/status/428183845985210369 ). Creo que esto es demasiado arriesgado para mí y, aunque no me encanta este enfoque, lo haré manualmente.
Matthias Zeis
@MatthiasZeis, ¿puede agregar eso como respuesta y aceptarlo para que las preguntas no respondidas cuenten?
Sander Mangel

Respuestas:

6

Entonces, poco después de twittear esto a Matthias, entré en silencio por radio. Espero que hayas sentido el suspenso mientras esperabas esta respuesta por un par de semanas.

Lo que quiero decir con "hago esto en una cola" es una respuesta directa a:

Ciertos observadores como enterprise_cms y enterprise_search para el evento store_delete_after se definen en el área adminhtml para que no se ejecuten. La eliminación de la vista de la tienda no se manejará como una eliminación ejecutada en el back-end.

Método de cola:

Cuando sé que hay ciertos eventos que no se dispararán en el contexto correcto (principalmente para EE, pero pueden aplicarse en otros contextos), generalmente empujo la eliminación a una cola para que se ejecute en el contexto que necesita. .

En otras palabras, cree una tabla de cola (o cola / tema en RabbitMQ, etc.) que contendría detalles de la transacción y los ganchos de eventos que debería estar escuchando. Esto puede ser tan elegante o tan simple como quieras que sea. Aquí hay un básico

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

Y luego trabaje la cola más tarde en un CRON, donde ahora tiene control sobre qué tienda está "ejecutándose" (es decir, solo lo está ejecutando como si fuera el administrador, tienda 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));
        }
    }
}

Obviamente, si te apetece, envuelves un intento / captura y envuelves una transacción. Creo que entiendes la esencia.

Esta es posiblemente la única forma de controlar el contexto en el que se desencadena el evento.

Método de evento en tándem:

Puede activar el método "adminhtml" usted mismo manualmente: Alan da una explicación bastante decente de lo que haría para afectar eso , pero esencialmente es lo mismo que esto:

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

La versión de administración del cliente guardar llama al modelo normal guardar y luego envía el evento adminhtml. Podrías hacer lo contrario de esto en un observador si lo deseas.

philwinkle
fuente
5

Maldita sea, me encanta un poco de philwinkle, pero tengo que estar en desacuerdo con la complejidad / fragilidad de transportar los parámetros de la tarea y el área ( adminhtml | crontab | frontend | global | install ) a una cola, especialmente si esa cola se va a ejecutar Un contexto de Magento. Si hay contextos mixtos que necesitan ser manejados, entonces la solución de la cola es una reimplementación del "problema" actual.

Creo que el enfoque de la cola es frágil. Mi argumento es que cargar áreas de eventos prematuramente no es realmente un problema en absoluto. Para explicar esto, retrocedamos y veamos el problema:

¿Cuál es el peligro de cargar un área de evento prematuramente en un ámbito de ejecución?

Para comprender esto, debemos examinar las áreas de eventos en el contexto de ejecución. Matthias, imagino que ya lo sabes, pero para la edificación de otros:

Los scripts de configuración de datos se ejecutan Mage_Core_Model_App::run()antes de enviar la solicitud al controlador frontal:

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

Para cuando se ejecutan los scripts de configuración de datos , se carga el área de evento global . Las áreas de eventos contextuales de enrutamiento ( frontend o adminhtml ) se cargan más adelante Mage_Core_Controller_Varien_Action::preDispatch()como resultado de que el enrutador coincida con una acción del controlador (el areanombre se establece por herencia):

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

Por lo tanto, normalmente durante la inicialización de la aplicación, solo se ejecutarán los observadores configurados en el área de evento global . Si el script de configuración hace algo como

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

entonces solo hay dos peligros:

  1. Un observador se configuró incorrectamente en adminhtml para observar un evento sin contexto como controller_front_init_beforeocontroller_front_init_routers
  2. La solicitud es una solicitud de interfaz .

# 1 debería ser fácil de conseguir. # 2 es la verdadera preocupación, y creo que Reflection puede resolver el problema (tenga en cuenta que lamentablemente no tengo experiencia con el uso de la reflexión):

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

No he probado esto, pero elimina el índice de eventos adminhtml y el Mage_Core_Model_App_Areaobjeto correspondiente .

puntos de referencia
fuente
1
¡¡No soy digno!! ¡¡¡¡No soy digno!!!!
philwinkle