Magento: modificar comportamientos mediante eventos y observadores

Magento logoUna de las formas más interesantes de modificar el comportamiento de tu Magento es utilizar los eventos. Se ejecutan en cada click del navegador, cuando un visitante se registra, cuando se añade un producto al comparador, cuando se crea un pedido, cuando se visita una página, una categoría, un producto, cuando un visitante entra a su zona de usuario, edita sus datos, busca contenidos.. es ingente la cantidad de eventos. Es decir, que contínuamente se están disparando una tonelada de eventos mientras que se ejecuta Magento.

Cuando empiezas a programar nuevos comportamientos, puedes buscar en StackOverflow o Googleando. A ver cuál es el evento que necesitas. Con la esperanza de que alguien haya compartido ya algo sobre esto..

Por ejemplo

Si necesitas comprobar ciertos datos de un pedido cuando se está haciendo en el front, puedes ponerte a observar los eventos siguientes:

sales_order_address_collection_load_after
sales_order_address_collection_load_before
sales_order_address_collection_set_sales_order
sales_order_address_save_after
sales_order_address_save_before
sales_order_address_save_commit_after
sales_order_collection_load_after
sales_order_collection_load_before
sales_order_creditmemo_resource_init_virtual_grid_columns
sales_order_invoice_resource_init_virtual_grid_columns
sales_order_item_collection_load_after
sales_order_item_collection_load_before
sales_order_item_collection_set_sales_order
sales_order_item_load_after
sales_order_item_load_before
sales_order_item_save_after
sales_order_item_save_before
sales_order_item_save_commit_after
sales_order_load_after
sales_order_load_before
sales_order_payment_collection_load_after
sales_order_payment_collection_load_before
sales_order_payment_collection_set_sales_order
sales_order_payment_place_end
sales_order_payment_place_start
sales_order_payment_save_after
sales_order_payment_save_before
sales_order_payment_save_commit_after
sales_order_place_after
sales_order_place_before
sales_order_resource_init_virtual_grid_columns
sales_order_resource_save_attribute_after
sales_order_resource_save_attribute_before
sales_order_resource_update_grid_records
sales_order_save_after
sales_order_save_before
sales_order_save_commit_after
sales_order_shipment_collection_load_before
sales_order_shipment_collection_set_sales_order
sales_order_shipment_load_after
sales_order_shipment_load_before
sales_order_shipment_resource_init_virtual_grid_columns
sales_order_shipment_track_collection_load_after
sales_order_shipment_track_collection_load_before
sales_order_status_history_collection_load_after
sales_order_status_history_collection_load_before
sales_order_status_history_collection_set_sales_order
sales_order_status_history_save_after
sales_order_status_history_save_before
sales_order_status_history_save_commit_after

Como es habitual, los programadores solemos compartir conocimientos por Internete. Así que una solución para ponerse a programar estas modificaciones es esta. Al principio es más que suficiente, pero cuanto más raro es el comportamiento que necesitas, más difícil va a ser que encuentres a alguien compartiendo la información.

Así que por esto que estoy compartiendo este post.. 😉 con esto ya podrás encontrar cualquier evento de Magento.

Un poco de teoría

Los eventos, listeners, observers.. son conceptos relacionados con los Patrones de Diseño de Software. No voy a entrar al detalle, ya que con esto se podría escribir otro post completo, con cada patrón.. Pero lo principal que hay que saber, es que podemos crear observadores que esperan a que ocurran ciertos eventos. Así que cuando ocurran esos eventos, podremos ejecutar las rutinas que queramos.

Por ejemplo, imagina que queremos decirle al usuario algo cuando ha puesto su NIF en el carrito de la compra. Podemos hacerlo con esto. Imagina que queremos comprobar si un pedido es de cierto grupo de clientes, y en ese caso moverlo a otro estado. También se puede hacer con esto.

Dichos eventos están predefinidos en el software. De forma que cuando ocurren estos eventos, el software busca qué observadores están enganchados, y los ejecuta.

Cómo se definen los eventos

En Magento, para definir un evento basta con escribir algo tal que así:

Mage::dispatchEvent('my_event', $data_array);

..con esto ya hemos creado un evento llamado my_event al cual se pueden enganchar observadores. Dicho evento se disparará en todos los lugares donde lo hayas escrito. Por ejemplo, puedes crearlo en tus controladores, o cuando ocurren ciertas acciones en los modelos. Además, dichos eventos recibirán los datos que le hayas pasado.

Bien, ahora cómo encuentro todos los eventos definidos

Una forma sería entonces buscar la cadena dispatchEvent() en el código fuente, pero nos encontramos algo tal que así:

Listando eventos Magento

Ahora es cuando te echas las manos a la cabeza al ver que muchos eventos no tienen un nombre fijo, sino que se generan dichos nombres al vuelo. Y además, luego se van a disparar dichos eventos al vuelo según se usen. Esto es un ejemplo de la brillante estructura interna que puede alcanzar un módulo. Si te fijas en el AEO Scheduler, este módulo, genera eventos para cada tarea programada de Magento. Así podrás modificar el comportamiento de cualquier tarea programada antes o después de su ejecución, cuando ha fallado, cuando no ha fallado..

Algo escribí sobre cómo crear tareas programadas de Magento aquí.

Entonces, cómo listo todos los eventos disponibles?

Podemos hacer un pequeño arreglo para encontrar todos los eventos relacionados con ciertos comportamiento. No hay más que poner esto en la función del core de Magento que dispara los eventos. En directorio del core de Magento está la función dispatchEvent(). Si ponemos lo siguiente en el fichero app/code/core/Mage/Core/Model/App.php:

...
public function dispatchEvent($eventName, $args)
{
    if(!empty($eventName))
        Mage::log($eventName, Zend_Log::INFO, 'availableEvents.log', false);
    $eventName = strtolower($eventName);
    foreach ($this->_events as $area=>$events) {
...

..estaremos guardando en el fichero availableEvents.log todos los nombres de eventos. Serán muchos y se generará un fichero bien grande mientras que usamos en local Magento. ¡NO HACER ESTO EN PRODUCCIÓN!

Con esto guardando los nombres de eventos, he arrancado en local el proyecto, y he hecho un pedido de prueba. De ahí ha salido el listado de arriba de eventos.

Solo queda poner bonito el listado de eventos

Por si sirve de ayuda, con este sencillo script que he hecho se pueden ordenar y eliminar los duplicados que tendremos en el fichero availableEvents.log:

<?php
/**
* LOOK AT app/code/core/Mage/Core/Model/App.php FOR dispatchEvent FUNCTION AND:
* var/log/availableEvents.log FOR GETTING ALL EVENT NAMES.
*/
$events = file(__DIR__.'/../web/var/log/availableEvents.log');

echo 'Loaded '.count($events).' event names.'.PHP_EOL;

$names = array();
foreach ($events as $key => $value) {
    $events[$key] = preg_replace("/.* INFO \(6\): /", '', $value);
    $names[] = $events[$key];
    //echo $events[$key];
}

$names = array_unique($names);
sort($names);

echo 'Finally '.count($names).' event names.'.PHP_EOL;

foreach ($names as $name) {
    echo $name;
}

Terminando

Para hacernos una idea, en pocos segundos, en una visita rápida para hacer un pedido en el front de un proyecto. Se han disparado 400 eventos, con un total de 80 000 veces, ¿brutal el potencial que tiene esto verdad?

Loaded 80357 event names.
Finally 418 event names.
admin_session_user_login_failed
admin_session_user_login_success
admin_user_authenticate_after
admin_user_authenticate_before
admin_user_load_after
admin_user_load_before
adminhtml_block_html_before
adminhtml_controller_action_predispatch_start
advancedstock_order_considered_by_cron
advancedstock_order_item_preparation_warehouse_changed
advancedstock_order_item_reserved_qty_changed
advancedstock_stock_aftersave
always
aoe_scheduler_job_load_after
aoe_scheduler_job_load_before
aoe_scheduler_schedule_collection_load_after
aoe_scheduler_schedule_collection_load_before
aoe_scheduler_schedule_delete_after
aoe_scheduler_schedule_delete_before
aoe_scheduler_schedule_delete_commit_after
aoe_scheduler_schedule_save_after
aoe_scheduler_schedule_save_before
aoe_scheduler_schedule_save_commit_after
application_clean_cache
aschroder_smtppro_after_send
aschroder_smtppro_template_before_send
catalog_block_product_list_collection
catalog_category_collection_load_after
catalog_category_collection_load_before
catalog_category_flat_loadnodes_before
catalog_category_load_after
catalog_category_load_before
catalog_category_tree_init_inactive_category_ids
catalog_compare_item_save_after
catalog_compare_item_save_before
catalog_compare_item_save_commit_after
catalog_controller_category_init_after
catalog_controller_category_init_before
catalog_entity_attribute_load_after
catalog_helper_output_construct
catalog_prepare_price_select
catalog_product_attribute_backend_media_load_gallery_before
catalog_product_collection_apply_limitations_after
catalog_product_collection_apply_limitations_before
catalog_product_collection_before_add_count_to_categories
catalog_product_collection_load_after
catalog_product_collection_load_before
catalog_product_compare_add_product
catalog_product_get_final_price
catalog_product_is_salable_after
catalog_product_is_salable_before
catalog_product_load_after
catalog_product_load_before
catalog_product_type_prepare_full_options
cataloginventory_stock_item_save_after
cataloginventory_stock_item_save_before
cataloginventory_stock_item_save_commit_after
catalogsearch_query_load_after
catalogsearch_query_save_after
catalogsearch_query_save_before
catalogsearch_query_save_commit_after
checkout_allow_guest
checkout_block_cart_sidebar_aftertohtml
checkout_cart_add_product_complete
checkout_cart_product_add_after
checkout_cart_save_after
checkout_cart_save_before
checkout_controller_onepage_save_shipping_method
checkout_onepage_controller_success_action
checkout_quote_destroy
checkout_quote_init
checkout_submit_all_after
checkout_type_onepage_save_order
checkout_type_onepage_save_order_after
cms_page_load_after
cms_page_load_before
cms_page_render
controller_action_layout_generate_blocks_after
controller_action_layout_generate_blocks_before
controller_action_layout_generate_xml_before
controller_action_layout_load_before
controller_action_layout_render_before
controller_action_layout_render_before_adminhtml_dashboard_index
controller_action_layout_render_before_adminhtml_index_login
controller_action_layout_render_before_catalog_category_view
controller_action_layout_render_before_catalogsearch_result_index
controller_action_layout_render_before_checkout_cart_index
controller_action_layout_render_before_checkout_onepage_success
controller_action_layout_render_before_cms_index_index
controller_action_layout_render_before_cms_index_noRoute
controller_action_layout_render_before_customer_account_login
controller_action_layout_render_before_opc_index_index
controller_action_noroute
controller_action_postdispatch
controller_action_postdispatch_adminhtml
controller_action_postdispatch_adminhtml_dashboard_index
controller_action_postdispatch_adminhtml_dashboard_tunnel
controller_action_postdispatch_adminhtml_index_index
controller_action_postdispatch_adminhtml_index_login
controller_action_postdispatch_adminhtml_zendesk_ticketsAll
controller_action_postdispatch_ajaxcart
controller_action_postdispatch_ajaxcart_index_add
controller_action_postdispatch_ajaxcart_whishlist_compare
controller_action_postdispatch_catalog
controller_action_postdispatch_catalog_category_noRoute
controller_action_postdispatch_catalog_category_view
controller_action_postdispatch_catalog_index_noRoute
controller_action_postdispatch_catalogsearch
controller_action_postdispatch_catalogsearch_result_index
controller_action_postdispatch_checkout
controller_action_postdispatch_checkout_cart_index
controller_action_postdispatch_checkout_onepage_success
controller_action_postdispatch_cms
controller_action_postdispatch_cms_index_index
controller_action_postdispatch_cms_index_noRoute
controller_action_postdispatch_customer
controller_action_postdispatch_customer_account_login
controller_action_postdispatch_m_searchautocomplete
controller_action_postdispatch_m_searchautocomplete_ajax_get
controller_action_postdispatch_opc
controller_action_postdispatch_opc_index_index
controller_action_postdispatch_opc_json_comment
controller_action_postdispatch_opc_json_payments
controller_action_postdispatch_opc_json_reloadShippingsPayments
controller_action_postdispatch_opc_json_review
controller_action_postdispatch_opc_json_saveBilling
controller_action_postdispatch_opc_json_saveOrder
controller_action_postdispatch_opc_json_savePayment
controller_action_postdispatch_opc_json_saveShippingMethod
controller_action_predispatch
controller_action_predispatch_adminhtml
controller_action_predispatch_adminhtml_dashboard_index
controller_action_predispatch_adminhtml_dashboard_tunnel
controller_action_predispatch_adminhtml_index_index
controller_action_predispatch_adminhtml_index_login
controller_action_predispatch_adminhtml_zendesk_ticketsAll
controller_action_predispatch_ajaxcart
controller_action_predispatch_ajaxcart_index_add
controller_action_predispatch_ajaxcart_whishlist_compare
controller_action_predispatch_catalog
controller_action_predispatch_catalog_category_noRoute
controller_action_predispatch_catalog_category_view
controller_action_predispatch_catalogsearch
controller_action_predispatch_catalogsearch_result_index
controller_action_predispatch_checkout
controller_action_predispatch_checkout_cart_index
controller_action_predispatch_checkout_onepage_success
controller_action_predispatch_cms
controller_action_predispatch_cms_index_index
controller_action_predispatch_cms_index_noRoute
controller_action_predispatch_customer
controller_action_predispatch_customer_account_index
controller_action_predispatch_customer_account_login
controller_action_predispatch_m_searchautocomplete
controller_action_predispatch_m_searchautocomplete_ajax_get
controller_action_predispatch_opc
controller_action_predispatch_opc_index_index
controller_action_predispatch_opc_json_comment
controller_action_predispatch_opc_json_payments
controller_action_predispatch_opc_json_reloadShippingsPayments
controller_action_predispatch_opc_json_review
controller_action_predispatch_opc_json_saveBilling
controller_action_predispatch_opc_json_saveOrder
controller_action_predispatch_opc_json_savePayment
controller_action_predispatch_opc_json_saveShippingMethod
controller_front_init_before
controller_front_init_routers
controller_front_send_response_after
controller_front_send_response_before
controller_response_redirect
core_abstract_delete_after
core_abstract_delete_before
core_abstract_delete_commit_after
core_abstract_load_after
core_abstract_load_before
core_abstract_save_after
core_abstract_save_before
core_abstract_save_commit_after
core_block_abstract_prepare_layout_after
core_block_abstract_prepare_layout_before
core_block_abstract_to_html_after
core_block_abstract_to_html_before
core_collection_abstract_load_after
core_collection_abstract_load_before
core_copy_fieldset_customer_account_to_quote
core_copy_fieldset_sales_convert_quote_address_to_order
core_copy_fieldset_sales_convert_quote_address_to_order_address
core_copy_fieldset_sales_convert_quote_item_to_order_item
core_copy_fieldset_sales_convert_quote_item_to_order_item_discount
core_copy_fieldset_sales_convert_quote_payment_to_order_payment
core_copy_fieldset_sales_convert_quote_to_order
core_layout_block_create_after
core_layout_update_updates_get_after
core_locale_set_locale
core_session_abstract_add_message
core_session_abstract_clear_messages
cron_after
cron_after_success
cron_aoescheduler_heartbeat_after
cron_aoescheduler_heartbeat_after_success
cron_aoescheduler_heartbeat_before
cron_before
cron_captcha_delete_expired_images_after
cron_captcha_delete_expired_images_after_success
cron_captcha_delete_expired_images_before
cron_captcha_delete_old_attempts_after
cron_captcha_delete_old_attempts_after_success
cron_captcha_delete_old_attempts_before
cron_continuegenerating_after
cron_continuegenerating_after_success
cron_continuegenerating_before
cron_core_email_queue_send_all_after
cron_core_email_queue_send_all_after_success
cron_core_email_queue_send_all_before
cron_doofinder_feed_generate_after
cron_doofinder_feed_generate_after_success
cron_doofinder_feed_generate_before
cron_ebizmarts_abandoned_cart_after
cron_ebizmarts_abandoned_cart_after_success
cron_ebizmarts_abandoned_cart_before
cron_ebizmarts_autoresponder_after
cron_ebizmarts_autoresponder_after_success
cron_ebizmarts_autoresponder_before
cron_exception
cron_execute_tasks_after
cron_execute_tasks_after_success
cron_execute_tasks_before
cron_export_shipments_shoppingflux_before
cron_export_update_stock_shoppingflux_after
cron_export_update_stock_shoppingflux_after_success
cron_export_update_stock_shoppingflux_before
cron_generatefeed_after
cron_generatefeed_after_success
cron_generatefeed_before
cron_import_pricing_after
cron_import_pricing_after_success
cron_import_pricing_before
cron_magemonkey_sendorders_asynch_after
cron_magemonkey_sendorders_asynch_after_success
cron_magemonkey_sendorders_asynch_before
cron_magemonkey_sendsubscribers_asynch_after
cron_magemonkey_sendsubscribers_asynch_after_success
cron_magemonkey_sendsubscribers_asynch_before
cron_magemonkey_webhooks_asynch_before
cron_magemonkey_webhooks_asynch_exception
cron_newsletter_send_all_after
cron_newsletter_send_all_after_success
cron_newsletter_send_all_before
cron_pagantis_pagantiseom_after
cron_pagantis_pagantiseom_after_success
cron_pagantis_pagantiseom_before
cron_realex_expiredcards_after
cron_realex_expiredcards_after_success
cron_realex_expiredcards_before
cron_realex_ordercleanup_after
cron_realex_ordercleanup_after_success
cron_realex_ordercleanup_before
cron_run_queue_after
cron_run_queue_after_success
cron_run_queue_before
cron_searchsphinx_check_daemon_after
cron_searchsphinx_check_daemon_after_success
cron_searchsphinx_check_daemon_before
cron_searchsphinx_reindex_delta_job_after
cron_searchsphinx_reindex_delta_job_after_success
cron_searchsphinx_reindex_delta_job_before
cron_turpentine_crawl_urls_after
cron_turpentine_crawl_urls_after_success
cron_turpentine_crawl_urls_before
cron_update_availability_status_after
cron_update_availability_status_after_success
cron_update_availability_status_before
cron_update_stocks_after
cron_update_stocks_after_success
cron_update_stocks_before
currency_display_options_forming
custom_quote_process
customer_address_format
customer_address_validation_after
customer_entity_attribute_load_after
customer_group_load_after
customer_group_load_before
customer_load_after
customer_load_before
customer_session_init
default
eav_collection_abstract_load_before
gift_options_prepare_items
http_response_send_before
model_delete_after
model_delete_before
model_delete_commit_after
model_load_after
model_load_before
model_save_after
model_save_before
model_save_commit_after
opc_saveGiftMessage
orderpreparartion_after_dispatch_order
payment_method_is_active
persistent_session_expired
prepare_catalog_product_collection_prices
process_collection_load_after
process_collection_load_before
resource_get_tablename
sales_convert_quote_address_to_order
sales_convert_quote_address_to_order_address
sales_convert_quote_item_to_order_item
sales_convert_quote_payment_to_order_payment
sales_convert_quote_to_order
sales_model_service_quote_submit_after
sales_model_service_quote_submit_before
sales_model_service_quote_submit_success
sales_order_address_collection_load_after
sales_order_address_collection_load_before
sales_order_address_collection_set_sales_order
sales_order_address_save_after
sales_order_address_save_before
sales_order_address_save_commit_after
sales_order_collection_load_after
sales_order_collection_load_before
sales_order_creditmemo_resource_init_virtual_grid_columns
sales_order_invoice_resource_init_virtual_grid_columns
sales_order_item_collection_load_after
sales_order_item_collection_load_before
sales_order_item_collection_set_sales_order
sales_order_item_load_after
sales_order_item_load_before
sales_order_item_save_after
sales_order_item_save_before
sales_order_item_save_commit_after
sales_order_load_after
sales_order_load_before
sales_order_payment_collection_load_after
sales_order_payment_collection_load_before
sales_order_payment_collection_set_sales_order
sales_order_payment_place_end
sales_order_payment_place_start
sales_order_payment_save_after
sales_order_payment_save_before
sales_order_payment_save_commit_after
sales_order_place_after
sales_order_place_before
sales_order_resource_init_virtual_grid_columns
sales_order_resource_save_attribute_after
sales_order_resource_save_attribute_before
sales_order_resource_update_grid_records
sales_order_save_after
sales_order_save_before
sales_order_save_commit_after
sales_order_shipment_collection_load_before
sales_order_shipment_collection_set_sales_order
sales_order_shipment_load_after
sales_order_shipment_load_before
sales_order_shipment_resource_init_virtual_grid_columns
sales_order_shipment_track_collection_load_after
sales_order_shipment_track_collection_load_before
sales_order_status_history_collection_load_after
sales_order_status_history_collection_load_before
sales_order_status_history_collection_set_sales_order
sales_order_status_history_save_after
sales_order_status_history_save_before
sales_order_status_history_save_commit_after
sales_prepare_amount_expression
sales_quote_add_item
sales_quote_address_collect_totals_after
sales_quote_address_collect_totals_before
sales_quote_address_collection_load_after
sales_quote_address_collection_load_before
sales_quote_address_discount_item
sales_quote_address_save_after
sales_quote_address_save_before
sales_quote_address_save_commit_after
sales_quote_collect_totals_after
sales_quote_collect_totals_before
sales_quote_config_get_product_attributes
sales_quote_item_collection_products_after_load
sales_quote_item_qty_set_after
sales_quote_item_save_after
sales_quote_item_save_before
sales_quote_item_save_commit_after
sales_quote_item_set_product
sales_quote_load_after
sales_quote_load_before
sales_quote_payment_delete_after
sales_quote_payment_delete_before
sales_quote_payment_delete_commit_after
sales_quote_payment_import_data_before
sales_quote_payment_save_after
sales_quote_payment_save_before
sales_quote_payment_save_commit_after
sales_quote_product_add_after
sales_quote_save_after
sales_quote_save_before
sales_quote_save_commit_after
salesorder_aftersave
salesorder_beforesave
salesorderitem_aftersave
salesorderplanning_productavailabilitystatus_aftersave
store_group_load_after
store_group_load_before
store_load_after
store_load_before
tax_rate_data_fetch
turpentine_session_init
visitor_init
Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *