Hapus tampilan toko secara terprogram dalam skrip pemutakhiran

12

Saya ingin menghapus tampilan toko secara terprogram . Melihat Mage_Adminhtml_System_StoreController::deleteStorePostAction(), ini cukup mudah (kode singkat):

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

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

Saya ingin memasukkan kode ini ke dalam skrip pemutakhiran data sehingga penghapusan akan dilakukan secara otomatis.

Masalahnya adalah saat menjalankan skrip peningkatan di data/Magento hanya memanggil pengamat acara yang dikonfigurasi di globalarea tersebut (lihat Pembaruan Struktur Magento vs. Pembaruan Data ). Pengamat tertentu menyukai enterprise_cmsdan enterprise_searchuntuk acara store_delete_aftertersebut ditentukan di adminhtmldaerah sehingga mereka tidak akan dieksekusi. Penghapusan tampilan toko tidak akan ditangani seperti penghapusan yang dilakukan di backend.

Bagaimana Anda menangani operasi seperti ini? Muat area acara tambahan sendiri di skrip pemutakhiran (saya takut itu)? Jangan melakukan modifikasi data seperti itu di skrip pemutakhiran tetapi letakkan skrip ajaib Anda di tempat tersembunyi yang suci dan jalankan secara manual?

Matthias Zeis
sumber
1
Mengapa Anda perlu menghapus tampilan toko bermasalah?
oleksii.svarychevskyi
Karena kami memiliki beberapa lingkungan dan lebih cepat dan lebih dapat diandalkan untuk melakukan semua perubahan konfigurasi secara terprogram.
Matthias Zeis
Pernah menemukan solusi untuk ini? Seberapa sering Anda melakukan ini? Secara pribadi, saya akan memilih opsi kedua "Jangan melakukan modifikasi data seperti itu di skrip upgrade tetapi menempatkan skrip magis Anda di tempat tersembunyi yang suci dan jalankan secara manual?"
ProxiBlue
Saya menunda masalah ini karena ada banyak hal penting yang harus dilakukan. @Philwinkle Magento SE sendiri menjawab di Twitter: "Saya melakukan ini dalam antrian; atau mendengarkan dan memecat acara pengiriman adminhtml tandem (alias palsu)" ( twitter.com/philwinkle/status/428183845985210369 ). Saya percaya ini terlalu berisiko bagi saya dan meskipun saya tidak menyukai pendekatan ini, saya akan melakukannya secara manual.
Matthias Zeis
@MatthiasZeis dapatkah Anda menambahkannya sebagai jawaban dan menerimanya untuk menjaga agar pertanyaan yang tidak dijawab mundur?
Sander Mangel

Jawaban:

6

Maka tak lama setelah saya tweet ini ke Matthias saya pergi di radio diam. Saya harap Anda merasakan ketegangan saat Anda menunggu jawaban ini selama beberapa minggu sekarang.

Yang saya maksud dengan "Saya melakukan ini dalam antrian" adalah respons langsung terhadap:

Pengamat tertentu seperti enterprise_cms dan enterprise_search untuk event store_delete_after didefinisikan di area adminhtml sehingga mereka tidak akan dieksekusi. Penghapusan tampilan toko tidak akan ditangani seperti penghapusan yang dilakukan di backend.

Metode Antrian:

Ketika saya tahu bahwa ada peristiwa tertentu yang tidak akan diaktifkan dalam konteks yang benar (sebagian besar untuk EE, tetapi mungkin berlaku dalam konteks lain) saya biasanya mendorong penghapusan ke antrian sehingga akan berjalan dalam konteks yang diperlukan untuk .

Dengan kata lain, buat tabel antrian (atau antrian / topik di RabbitMQ, dll.) Yang akan berisi perincian transaksi dan kait acara yang harus didengarkan. Ini bisa menjadi elegan atau sesederhana yang Anda inginkan. Ini dasar

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

Dan kemudian kerjakan antriannya nanti dalam CRON, di mana Anda sekarang memiliki kendali atas toko mana yang "berjalan" (alias Anda hanya menjalankannya seolah-olah itu admin, store 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));
        }
    }
}

Jelas jika Anda mulai menyukai Anda membungkus mencoba / menangkap dan membungkus transaksi. Saya pikir Anda mendapatkan intinya.

Ini mungkin satu-satunya cara untuk mengendalikan konteks di mana peristiwa tersebut terjadi.

Metode acara Tandem:

Anda dapat menjalankan sendiri metode "adminhtml" secara manual - Alan memberikan penjelasan yang cukup baik tentang apa yang akan Anda lakukan untuk memengaruhi itu , tetapi pada dasarnya sama dengan ini:

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

Versi admin pelanggan save memanggil model biasa simpan dan mengirim acara adminhtml sesudahnya. Anda dapat melakukan kebalikan dari ini dalam diri seorang pengamat jika Anda menginginkannya.

Philwinkle
sumber
5

Sialan, saya suka saya beberapa filwinkle, tapi saya harus tidak setuju dengan kompleksitas / kerapuhan mengangkut parameter tugas dan area ( adminhtml | crontab | frontend | global | install ) ke antrian, terutama jika antrian itu akan mengeksekusi konteks Magento. Jika ada konteks campuran yang perlu penanganan maka solusi antrian adalah implementasi ulang dari "masalah" saat ini!

Saya pikir pendekatan antrian rapuh. Argumen saya adalah bahwa memuat area acara secara prematur bukan masalah sama sekali. Untuk menjelaskan ini, mari kita kembali dan melihat masalahnya:

Apa bahayanya memuat area acara secara prematur dalam lingkup eksekusi?

Untuk memahami ini kita harus memeriksa area acara dalam konteks eksekusi. Matthias, saya membayangkan Anda sudah mengetahui hal ini, tetapi untuk peneguhan orang lain:

Skrip pengaturan data dieksekusi Mage_Core_Model_App::run()sebelum mengirim permintaan ke Front Controller:

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

Pada saat skrip pengaturan data mengeksekusi area acara global dimuat. Area acara perutean-kontekstual ( frontend atau adminhtml ) dimuat di kemudian hari Mage_Core_Controller_Varien_Action::preDispatch()sebagai hasil dari pencocokan router dengan tindakan pengontrol ( areanamanya diatur melalui pewarisan):

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

Jadi biasanya selama inisialisasi aplikasi hanya pengamat yang dikonfigurasi di bawah area acara global yang akan dieksekusi. Jika skrip pengaturan melakukan sesuatu seperti

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

maka hanya ada dua bahaya:

  1. Seorang pengamat telah salah konfigurasi di bawah adminhtml untuk mengamati peristiwa tanpa konteks seperti controller_front_init_beforeataucontroller_front_init_routers
  2. Permintaan adalah permintaan frontend .

# 1 seharusnya mudah dipahami. # 2 adalah masalah nyata, dan saya pikir Refleksi dapat menyelesaikan masalah (perhatikan bahwa saya tidak berpengalaman menggunakan refleksi):

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

Saya belum menguji ini, tetapi itu menghapus indeks peristiwa adminhtml dan Mage_Core_Model_App_Areaobjek yang sesuai .

tanda batas
sumber
1
Saya tidak pantas!! Saya tidak pantas!!!!
philwinkle