Ci è stato chiesto all'atto dell'autenticazione di un utente su Magento, di verificarne il diritto di accedere mediante l'invocazione di un web service su un sistema di controllo esterno. Per utente, in questo caso, intendiamo un utente del frontend, non del backend.
Questo è solo un esempio del perché potrebbe essere necessario agganciarsi al meccanismo di autenticazione degli utenti e, come al solito, si può sfruttare un evento che Magento dispaccia al login.
L'aspetto curioso, in questo caso, è che, al contrario di quanto ci si potrebbe aspettare, Magento dispaccia un solo evento, dopo aver autenticato un utente e non prima.
Come si fa, dunque, ad inibile l'autenticazione?
In realtà è molto semplice. L'evento in questione è il customer_customer_authenticated; per agganciarlo basta dichiarare l'observer nel config.xml del nostro modulo, nella sezione <frontend>, come mostrato di seguito:
<config>
...
<global>
<models>
<mymodelalias>
<class>Mypackage_Mymodule_Model</class>
</mymodelalias>
</models>
</global>
...
<frontend>
...
<events>
<customer_customer_authenticated>
<observers>
<myprefix_authenticate> <!-- deve essere univoco in tutta la configurazione -->
<type>singleton</type>
<class>mymodelalias/observer</class>
<method>checkPermission</method>
</myprefix_authenticate>
</observers>
</customer_customer_authenticated>
...
</frontend>
...
</config>
Ed ecco a seguire uno scheletro della classe Mypackage_Mymodule_Model_Observer che implementa il metodo checkPermission():
class Mypackage_Mymodule_Model_Observer {
public function checkPermission($observer) {
$event = $observer->getEvent();
$customer = $event->getModel();
// occorre implementare la logica che inibisce il login;
// se il login non è consentito, occorre eseguire il codice del ramo if(...)
if (...) {
throw Mage::exception('Mage_Core',
Mage::helper('core')->__('Permission denied.'),
503 // è richiesto un codice numerico di errore arbitrario
);
}
}
}
Come si può notare, per inibile il login è sufficiente generare una eccezione come quella mostrata nel codice del metodo checkPermission(). Il controllo effettuato nel ramo if(...) può essere più o meno complesso se non demandato a metodi o servizi esterni.
Magento Community
Approfondimenti tecnici in italiano sulla piattaforma Magento CE
martedì 2 aprile 2013
venerdì 25 gennaio 2013
Come evitare il caricamento del carrello al login
Quando in fase di checkout l'utente fa login e arriva all'ultimo passaggio può trovarsi di fronte ad una sorpresa: il carrello, e quindi l'ordine che sta per confermare, è cambiato!
Questo accade perché al momento dell'autenticazione Magento unisce i contenuti del carrello in sessione con quelli del carrello associato all'utente autenticato.
Tale comportamento può essere anche utile ma sicuramente non in fase di checkout dove sorprese del genere posono costituire uno dei motivi di abbandono del processo d'acquisto.
Evitare che questo accada è molto semplice. Basta agganciare all'evento sales_quote_merge_before il metodo seguente:
public function emptySavedCart($observer) {
$event = $observer->getEvent();
$quote = $event->getQuote();
foreach ($quote->getAllItems() as $item) {
$quote->removeItem($item->getId())->save();
}
}
La sintassi da inserire nel file di config.xml del modulo che realizza tale comportamento è riportata di seguito:
<config>
<frontend>
<events>
<sales_quote_merge_before>
<observers>
<mymodule_observer>
<type>singleton</type>
<class>mymodule/observer</class>
<method>emptySavedCart</method>
</mymodule_observer>
</observers>
</sales_quote_merge_before>
</events>
</frontend>
</config>
Poiché l'evento sales_quote_merge_before è lanciato da un metodo invocato solo nel caso ci sia già qualcosa nel carrello in sessione, nel caso quest'ultimo non contenga elementi il comportamento rimarrà quello standard: al login il carrello si popolerà con gli eventuali contenuti del carrello associato all'utente.
Questo accade perché al momento dell'autenticazione Magento unisce i contenuti del carrello in sessione con quelli del carrello associato all'utente autenticato.
Tale comportamento può essere anche utile ma sicuramente non in fase di checkout dove sorprese del genere posono costituire uno dei motivi di abbandono del processo d'acquisto.
Evitare che questo accada è molto semplice. Basta agganciare all'evento sales_quote_merge_before il metodo seguente:
public function emptySavedCart($observer) {
$event = $observer->getEvent();
$quote = $event->getQuote();
foreach ($quote->getAllItems() as $item) {
$quote->removeItem($item->getId())->save();
}
}
La sintassi da inserire nel file di config.xml del modulo che realizza tale comportamento è riportata di seguito:
<config>
<frontend>
<events>
<sales_quote_merge_before>
<observers>
<mymodule_observer>
<type>singleton</type>
<class>mymodule/observer</class>
<method>emptySavedCart</method>
</mymodule_observer>
</observers>
</sales_quote_merge_before>
</events>
</frontend>
</config>
Poiché l'evento sales_quote_merge_before è lanciato da un metodo invocato solo nel caso ci sia già qualcosa nel carrello in sessione, nel caso quest'ultimo non contenga elementi il comportamento rimarrà quello standard: al login il carrello si popolerà con gli eventuali contenuti del carrello associato all'utente.
Etichette:
cart,
checkout,
login,
quote,
sales_quote_merge_before
domenica 11 novembre 2012
Template Path Hints Pro diventa un modulo Community su GitHub
In un precedente post avevamo descritto come poter verificare la struttura di un template di Magento senza abilitare gli invasivi Template Path Hints.
Abbiamo creato un modulo denominato Template Path Hints Pro e lo abbiamo rilasciato su GitHub con l'intento di farlo evolvere col tempo per trasformarlo in uno strumento sempre più utile.
Il modulo è ovviamente gratuito e liberamente utilizzabile e modificabile a proprio piacimento.
Link su GitHub: https://github.com/aleron75/Webgriffe_TphPro
Abbiamo creato un modulo denominato Template Path Hints Pro e lo abbiamo rilasciato su GitHub con l'intento di farlo evolvere col tempo per trasformarlo in uno strumento sempre più utile.
Il modulo è ovviamente gratuito e liberamente utilizzabile e modificabile a proprio piacimento.
Link su GitHub: https://github.com/aleron75/Webgriffe_TphPro
Etichette:
block,
github,
Layout,
Template Path Hints
lunedì 29 ottobre 2012
Prestazioni: un occhio ai dettagli
Dopo un prolungato periodo di silenzio dovuto ai numerosi impegni lavorativi riprendiamo la buona abitudine di condividere un po' di esperienza su Magento.
In occasione dell'ottimizzazione di alcune procedure di importazione prodotti abbiamo ottenuto dei miglioramenti significativi, riducendo complessivamente di un ordine di grandezza la durata della elaborazione, grazie ad un semplice accorgimento.
L'accorgimento consiste nell'evitare di salvare un prodotto in maniera tradizionale, attraverso il metodo save() di un Model e nello sfruttare, invece, il metodo saveAttribute() del rispettivo Resource Model.
Occorre sottolineare che devono sussistere determinati prerequisiti affinché i miglioramenti siano percepibili; eccone alcuni elencati di seguito:
Ed ecco di seguito la tabella del Profiler che mostra i due casi nelle righe evidenziate: si può notare come salvando il singolo attributo il tempo si riduce di oltre 30 volte con un notevole risparmio anche di memoria allocata.
In occasione dell'ottimizzazione di alcune procedure di importazione prodotti abbiamo ottenuto dei miglioramenti significativi, riducendo complessivamente di un ordine di grandezza la durata della elaborazione, grazie ad un semplice accorgimento.
L'accorgimento consiste nell'evitare di salvare un prodotto in maniera tradizionale, attraverso il metodo save() di un Model e nello sfruttare, invece, il metodo saveAttribute() del rispettivo Resource Model.
Occorre sottolineare che devono sussistere determinati prerequisiti affinché i miglioramenti siano percepibili; eccone alcuni elencati di seguito:
- L'insieme di attributi da modificare su un Model è limitato e noto a priori (solitamente lo è in una procedura di importazione dati).
- Il processo di indicizzazione può essere rimandato al termine dell'elaborazione di tutti i prodotti, evitando di farlo girare ad ogni salvataggio.
- Si può rinunciare al layer di funzionalità dato dall'utilizzo dei Model come, ad esempio, il meccanismo di event dispatching scatenato da questi e non dai Resource Model.
Riportiamo di seguito il frammento di codice che effettua il salvataggio di un prodotto di prova utilizzando i due metodi citati; sfruttiamo il Varien Profiler per misurare e confrontare le prestazioni. Il ciclo da 1 a 100 è utilizzato per raccogliere dati su un campione di casi sufficientemente ampio.
$product = Mage::getModel('catalog/product')->load(1);
Varien_Profiler::enable();
Varien_Profiler::start('SaveWholeProduct');
for ($i = 0; $i < 100; $i ++) {
$product
->setIsMassupdate(true)
->setDescription('Sample Product Description')
->save();
}
Varien_Profiler::stop('SaveWholeProduct');
Varien_Profiler::start('SaveProductAttribute');
for ($i = 0; $i < 100; $i ++) {
$product
->setDescription('Sample Product Description')
->getResource()
->saveAttribute($product, 'description');
}
Varien_Profiler::stop('SaveProductAttribute');
Ed ecco di seguito la tabella del Profiler che mostra i due casi nelle righe evidenziate: si può notare come salvando il singolo attributo il tempo si riduce di oltre 30 volte con un notevole risparmio anche di memoria allocata.
martedì 14 agosto 2012
Differenziare la connessione a DB per diversi ambienti
La connessione al Database in Magento è generalmente impostata nel file
Ciascun Modulo ha la possibilità di ridefinire la connessione che i propri Resource Model devono utilizzare. Questa pratica è poco utilizzata ma andandone a studiare i meccanismi ci si accorge che è possibile sfruttarla per configurare una connessione generale in un file diverso dal
In questo modo è possibile impostare la connessione di default (dell'ambiente di staging o production) nel file local.xml che magari sarà posto sotto controllo di versione e una connessione specifica in un file diverso per ciascuna macchina di sviluppo.
Quest'ultimo file non sarà sotto controllo di versione e ciascuno sviluppatore potrà personalizzarlo per utilizzare la connessione al proprio DB locale.
Per ottenere lo scopo occorre creare un file XML in
Per comprendere il funzionamento di tale meccanismo occorre analizzare un paio di metodi della classe
Il primo metodo è il
Il metodo cicla la cartella
Il secondo metodo utile per comprendere il funzionamento del meccanismo illustrato è il
Il metodo viene invocato, attraverso diversi livelli di indirezione, ogni volta che è necessaria la connessione al DB. Invocato su un nodo
Poiché generalmente le connection di tutti i moduli di Magento utilizzano i parametri definiti in
local.xml che si trova nel folder <magento_dir>/app/etc/.Ciascun Modulo ha la possibilità di ridefinire la connessione che i propri Resource Model devono utilizzare. Questa pratica è poco utilizzata ma andandone a studiare i meccanismi ci si accorge che è possibile sfruttarla per configurare una connessione generale in un file diverso dal
local.xml.In questo modo è possibile impostare la connessione di default (dell'ambiente di staging o production) nel file local.xml che magari sarà posto sotto controllo di versione e una connessione specifica in un file diverso per ciascuna macchina di sviluppo.
Quest'ultimo file non sarà sotto controllo di versione e ciascuno sviluppatore potrà personalizzarlo per utilizzare la connessione al proprio DB locale.
Per ottenere lo scopo occorre creare un file XML in
<magento_dir>/app/etc/ e nominarlo, ad esempio, local_dev.xml. Il contenuto del file è illustrato di seguito:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<global>
<resources>
<default_setup>
<connection>
<use>dev_setup</use>
</connection>
</default_setup>
<dev_setup>
<connection>
<host><![CDATA[localhost]]></host>
<username><![CDATA[dbusername]]></username>
<password><![CDATA[dbpassword]]></password>
<dbname><![CDATA[dbname]]></dbname>
<initStatements><![CDATA[SET NAMES utf8]]></initStatements>
<model><![CDATA[mysql4]]></model>
<type><![CDATA[pdo_mysql]]></type>
<pdoType><![CDATA[]]></pdoType>
<active>1</active>
</connection>
</dev_setup>
</resources>
</global>
</config>
Per comprendere il funzionamento di tale meccanismo occorre analizzare un paio di metodi della classe
Mage_Core_Model_Config.Il primo metodo è il
loadBase() che viene invocato ad ogni richiesta di avvio del runtime di Magento attraverso i metodi run(), app() o init():
public function loadBase()
{
$etcDir = $this->getOptions()->getEtcDir();
$files = glob($etcDir.DS.'*.xml');
$this->loadFile(current($files));
while ($file = next($files)) {
$merge = clone $this->_prototype;
$merge->loadFile($file);
$this->extend($merge);
}
if (in_array($etcDir.DS.'local.xml', $files)) {
$this->_isLocalConfigLoaded = true;
}
return $this;
}
Il metodo cicla la cartella
<magento_dir>/app/etc/ e carica tutti i file XML presenti. Questo è il motivo per il quale il nostro file local_dev.xml viene caricato assieme agli altri.Il secondo metodo utile per comprendere il funzionamento del meccanismo illustrato è il
getResourceConnectionConfig():
public function getResourceConnectionConfig($name)
{
$config = $this->getResourceConfig($name);
if ($config) {
$conn = $config->connection;
if ($conn) {
if (!empty($conn->use)) {
return $this->getResourceConnectionConfig((string)$conn->use);
} else {
return $conn;
}
}
}
return false;
}
Il metodo viene invocato, attraverso diversi livelli di indirezione, ogni volta che è necessaria la connessione al DB. Invocato su un nodo
$name, il metodo torna, se definito, il nodo global->resources->$name->connection della configurazione. Questo a meno che non sia definito il nodo global->resources->$name->connection->use; perché in tal caso il metodo viene chiamato ricorsivamente sul valore del nodo finché non viene trovato un nodo contenente i parametri di connessione.Poiché generalmente le connection di tutti i moduli di Magento utilizzano i parametri definiti in
<default_setup> e per questa non è definito il nodo <use>, quando con il nostro file local_dev.xml andiamo a definire il nodo <use>dev_setup</use> il metodo getResourceConnectionConfig() restituisce il valore del nodo connection definito in <dev_setup> scavalcando quello definito in local.xml e ottenendo il risultato descritto.
venerdì 27 luglio 2012
Salvare un documento PDF nell'entità Shipment
A partire dalla versione 1.6.0.0 di Magento è stato introdotto il campo shipping_label sul Model sales/order_shipment.
Nella grid degli ordini, la mass action di stampa etichette prende tutti questi campi e, tramite Zend_Pdf, li tratta come PDF binari, accodandoli e restituendo un unico PDF monolitico.
Il modo migliore per generare questa etichetta è attraverso il metodo di un Observer registrato sull'evento sales_order_shipment_save_before.
Di seguito alcuni passaggi utili per creare l'etichetta e impostare il campo.
<global>
[...]
<events>
<sales_order_shipment_save_before>
<observers>
<modulo_observer>
<type>singleton</type>
<class>modulo/observer</class>
<method>before</method>
</wgtnt_observer>
</observers>
</sales_order_shipment_save_before>
</events>
[...]
</global>
[...]
</config>
class Vendor_Modulo_Model_Observer { public function before($observer) {
$obj = $observer->getDataObject();
if ($obj) {
$label = Mage::getModel('modulo/pdf_etichetta');
$obj->setShippingLabel($label->render($obj));
}
}
}
Ci addentreremo più nel dettaglio di questo processo in un prossimo articolo, per ora ci limitiamo ad indicare gli elementi principali all'interno del metodo render():
Costanti di classe
const BIGX = 595; #A5, misure prese dalle wikipedia
const BIGY = 421;
Creare gli oggetti dei font che useremo, se usiamo font standard (definiti in Zend_Pdf_Font) non ci sarà la necessita di fare un fontembedding e quindi avremo PDF dalle dimensioni più contenute.
$f_arial = Zend_Pdf_Font::fontWithName( Zend_Pdf_Font::FONT_HELVETICA );
$f_courier = Zend_Pdf_Font::fontWithName( Zend_Pdf_Font::FONT_COURIER );
$f_arialbold = Zend_Pdf_Font::fontWithName( Zend_Pdf_Font::FONT_HELVETICA_BOLD );
Creare l'oggetto PDF
$this->_pdf = new Zend_Pdf();
Creare una pagina, se servono più pagine da qui in poi dovremo essere in un ciclo.
$this->_page = $this->_pdf->newPage(self::BIGX . ':' . self::BIGY . ':');
Aggiungere la pagina al PDF, se ce ne dimentichiamo non otterremo alcun output.
$this->_pdf->pages[] = $this->_page;
Da qui in poi possiamo usare le funzione della Zend_Pdf per stampare del testo sulla pagina.
$this->_page->setFont($f_arialbold, 10);
$this->_page->drawText('Sender', 200, 200);
Generare l'output finale con
return $this->_pdf->render();
Nella grid degli ordini, la mass action di stampa etichette prende tutti questi campi e, tramite Zend_Pdf, li tratta come PDF binari, accodandoli e restituendo un unico PDF monolitico.
Il modo migliore per generare questa etichetta è attraverso il metodo di un Observer registrato sull'evento sales_order_shipment_save_before.
Di seguito alcuni passaggi utili per creare l'etichetta e impostare il campo.
1. Definiamo l'Observer, nel config.xml:
<config> [...]<global>
[...]
<events>
<sales_order_shipment_save_before>
<observers>
<modulo_observer>
<type>singleton</type>
<class>modulo/observer</class>
<method>before</method>
</wgtnt_observer>
</observers>
</sales_order_shipment_save_before>
</events>
[...]
</global>
[...]
</config>
2. Definiamo la classe dell'Observer e il metodo before()
class Vendor_Modulo_Model_Observer { public function before($observer) {$obj = $observer->getDataObject();
if ($obj) {
$label = Mage::getModel('modulo/pdf_etichetta');
$obj->setShippingLabel($label->render($obj));
}
}
}
3. Creiamo il Model
Nominiamo il Model modulo/pdf_etichetta che si occupa di creare il PDF dai dati dello Shipping passati al metodo render(). Noi abbiamo scelto di utilizzare la libreria Zend_Pdf, forse un po' primitiva ma sicuramente efficace per la creazione di semplici etichette.Ci addentreremo più nel dettaglio di questo processo in un prossimo articolo, per ora ci limitiamo ad indicare gli elementi principali all'interno del metodo render():
Costanti di classe
const BIGX = 595; #A5, misure prese dalle wikipedia
const BIGY = 421;
Creare gli oggetti dei font che useremo, se usiamo font standard (definiti in Zend_Pdf_Font) non ci sarà la necessita di fare un fontembedding e quindi avremo PDF dalle dimensioni più contenute.
$f_arial = Zend_Pdf_Font::fontWithName( Zend_Pdf_Font::FONT_HELVETICA );
$f_courier = Zend_Pdf_Font::fontWithName( Zend_Pdf_Font::FONT_COURIER );
$f_arialbold = Zend_Pdf_Font::fontWithName( Zend_Pdf_Font::FONT_HELVETICA_BOLD );
Creare l'oggetto PDF
$this->_pdf = new Zend_Pdf();
Creare una pagina, se servono più pagine da qui in poi dovremo essere in un ciclo.
$this->_page = $this->_pdf->newPage(self::BIGX . ':' . self::BIGY . ':');
Aggiungere la pagina al PDF, se ce ne dimentichiamo non otterremo alcun output.
$this->_pdf->pages[] = $this->_page;
Da qui in poi possiamo usare le funzione della Zend_Pdf per stampare del testo sulla pagina.
$this->_page->setFont($f_arialbold, 10);
$this->_page->drawText('Sender', 200, 200);
Generare l'output finale con
return $this->_pdf->render();
giovedì 19 luglio 2012
Uso di un Custom Router per creare una pagina di Maintenance
Studiando i Router di Magento, ci siamo posti la domanda "Quale può essere un esempio pratico che generi la necessità di scrivere un router per le richieste a Magento?"
<events>
<controller_front_init_before>
<observers>
<aliasmodulo>
<class>Vendor_Nomemodulo_Controller_Router</class>
<method>initControllerRouters</method>
</aliasmodulo>
</observers>
</controller_front_init_before>
</events>
Successivamente definiamo la funzione initControllerRouters() nella nostra classe Vendor_Nomemodulo_Controller_Router.
Il metodo match() torna FALSE, passando il controllo ai Router successivi ma, prima di farlo e nel caso non sia una richiesta di admin, imposta il Module, il Controller e la Action a aliasmodulo/index/index.
In questo modo avverrà il match con il Router Mage_Core_Controller_Varien_Router_Standard che chiamerà il dispatch() della Action definita nel nostro Controller.
Il Controller che serve la pagina di cortesia può essere implementato molto semplicemente come segue:
#file app/code/local/Vendor/Nomemodulo/controllers/IndexController.php
class Vendor_Nomemodulo_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
$this->getResponse()->setBody('Under Maintenance');
}
}
Ovviamente è possibile implementare una Action più raffinata che utilizzi il motore di templating per restituire l'output appropriato; nell'articolo ci interessava soprattutto mostrare il meccanismo che consente di aggiungere un proprio Router in Magento e un suo possibile utilizzo.
Una prima risposta che ci siamo dati è stata: "Un Router che rediriga tutte le richieste, eccetto quelle di admin, ad una pagina di cortesia."
In questo articolo ne mostriamo dunque una possibile implementazione.
Un primo tentativo (fallito) è stato quello di abilitare il nuovo Router attraverso il file di configurazione, specificando nel config.xml il seguente path:
config
In questo articolo ne mostriamo dunque una possibile implementazione.
Un primo tentativo (fallito) è stato quello di abilitare il nuovo Router attraverso il file di configurazione, specificando nel config.xml il seguente path:
config
default
web
routers
e definendo successviamente un Router prendendo spunto da app/code/core/Mage/Core/etc/config.xml
Il problema, in questo caso, è che il Router viene sì definito, ma è collocato in fondo alla lista dei Router (in penultima posizione, prima del Default Router deputato alla gestione dei no route) e non riesce pertanto ad intercettare tutte le chiamate come vorremmo.
Il secondo tentativo che ci ha dato il risultato cercato sfrutta invece gli eventi. Agganciandosi all'evento controller_front_init_before possiamo dichiarare un Router e far sì che sia collocato in lista prima di tutti gli altri.
Attenzione: se due Observer agganciassero lo stesso evento in due moduli che non dichiarano le rispettive dipendenze non sarebbe prevedibile quale dei due Router avrebbe la priorità.
Per ottenere quanto descritto specifichiamo nel ramo <global> del config.xml del nostro modulo la configurazione relativa all'Observer che ci occorre:
e definendo successviamente un Router prendendo spunto da app/code/core/Mage/Core/etc/config.xml
Il problema, in questo caso, è che il Router viene sì definito, ma è collocato in fondo alla lista dei Router (in penultima posizione, prima del Default Router deputato alla gestione dei no route) e non riesce pertanto ad intercettare tutte le chiamate come vorremmo.
Il secondo tentativo che ci ha dato il risultato cercato sfrutta invece gli eventi. Agganciandosi all'evento controller_front_init_before possiamo dichiarare un Router e far sì che sia collocato in lista prima di tutti gli altri.
Attenzione: se due Observer agganciassero lo stesso evento in due moduli che non dichiarano le rispettive dipendenze non sarebbe prevedibile quale dei due Router avrebbe la priorità.
Per ottenere quanto descritto specifichiamo nel ramo <global> del config.xml del nostro modulo la configurazione relativa all'Observer che ci occorre:
<events>
<controller_front_init_before>
<observers>
<aliasmodulo>
<class>Vendor_Nomemodulo_Controller_Router</class>
<method>initControllerRouters</method>
</aliasmodulo>
</observers>
</controller_front_init_before>
</events>
Successivamente definiamo la funzione initControllerRouters() nella nostra classe Vendor_Nomemodulo_Controller_Router.
Nota: la cartella Controller non ha nulla a che fare con la cartella controllers in cui vengono specificati i Controller che contengono le Action. In questo caso la nomenclatura può confondere un po' le idee.
class Vendor_Nomemodulo_Controller_Router
class Vendor_Nomemodulo_Controller_Router
extends Mage_Core_Controller_Varien_Router_Abstract
{
/**
* Initialize Controller Router
*
* @param Varien_Event_Observer $observer
*/
public function initControllerRouters($observer)
{
/* @var $front Mage_Core_Controller_Varien_Front */
$front = $observer->getEvent()->getFront();
$front->addRouter('maintenance', $this);
}
}
{
/**
* Initialize Controller Router
*
* @param Varien_Event_Observer $observer
*/
public function initControllerRouters($observer)
{
/* @var $front Mage_Core_Controller_Varien_Front */
$front = $observer->getEvent()->getFront();
$front->addRouter('maintenance', $this);
}
}
L'Observer si limita ad aggiungere se stesso (in quanto è definito come Router) alla lista dei Router che viene utilizzata dal Front Controller di Magento durante il dispatch() della Request per individuare il Controller che farà match.
Ora occorre implementare la funzione match() del Router. Questa dovrà restituire un valore booleano TRUE su tutte le richieste che non siano dirette ad admin e invocare la Action del Controller preposto a servire la pagina di cortesia:
public function match(Zend_Controller_Request_Http $request)
Ora occorre implementare la funzione match() del Router. Questa dovrà restituire un valore booleano TRUE su tutte le richieste che non siano dirette ad admin e invocare la Action del Controller preposto a servire la pagina di cortesia:
public function match(Zend_Controller_Request_Http $request)
{
if ( Mage::getStoreConfig('system/aliasmodulo/active') ) {
$adminFrontName = (string) Mage::getConfig()->getNode('admin/routers/adminhtml/args/frontName');
$str_pos = strpos($request->getPathInfo() , $adminFrontName);
if (Mage::app()->getStore()->isAdmin() || $str_pos === 1) {
return false;
}
$moduleName = 'aliasmodulo';
$controllerName = 'index';
$actionName = 'index';
$request
if ( Mage::getStoreConfig('system/aliasmodulo/active') ) {
$adminFrontName = (string) Mage::getConfig()->getNode('admin/routers/adminhtml/args/frontName');
$str_pos = strpos($request->getPathInfo() , $adminFrontName);
if (Mage::app()->getStore()->isAdmin() || $str_pos === 1) {
return false;
}
$moduleName = 'aliasmodulo';
$controllerName = 'index';
$actionName = 'index';
$request
->setModuleName($moduleName)
->setControllerName($controllerName)
->setActionName($actionName);
}
return false;
}
->setActionName($actionName);
}
return false;
}
Il metodo match() torna FALSE, passando il controllo ai Router successivi ma, prima di farlo e nel caso non sia una richiesta di admin, imposta il Module, il Controller e la Action a aliasmodulo/index/index.
In questo modo avverrà il match con il Router Mage_Core_Controller_Varien_Router_Standard che chiamerà il dispatch() della Action definita nel nostro Controller.
Il Controller che serve la pagina di cortesia può essere implementato molto semplicemente come segue:
#file app/code/local/Vendor/Nomemodulo/controllers/IndexController.php
class Vendor_Nomemodulo_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
$this->getResponse()->setBody('Under Maintenance');
}
}
Ovviamente è possibile implementare una Action più raffinata che utilizzi il motore di templating per restituire l'output appropriato; nell'articolo ci interessava soprattutto mostrare il meccanismo che consente di aggiungere un proprio Router in Magento e un suo possibile utilizzo.
Iscriviti a:
Post (Atom)
