Magento é uma incrível plataforma para criação de desenvolvimento de lojas virtuais. Além disso, o Magento é construído em cima de um incrível framework PHP que pode ser explorado para desenvolver aplicações web dinâmicas e poderosas.

O objetivo desse artigo é esclarecer o básico desse framework para que os desenvolvedores possam explorar cada vez mais desses recursos para construir melhor e de maneira mais eficaz suas aplicações. No início as coisas podem parecer bem complicadas, mas quando você começar a estudar a fundo o sistema logo elas irão se esclarecer.

Código Organizado em Módulos

Magento utiliza o seu código em módulos individuais muito parecido com o padrão HMVC. Diferente do típico padrão MVC utilizado na maioria dos frameworks conhecidos onde todos os controllers estão em uma pasta, models em outra, etc. No Magento os arquivos são agrupados por suas funcionalidades, criamos pacotinhos que são “independentes” uns dos outros e denominamos de módulos.

Códigos do Magento

Por exemplo, você encontrará todos os arquivos relacionados ao módulo de check-out do Magento (Controllers, Models, Helpers, Blocks, etc) no seguinte diretório:

app / code / core / Mage / Checkout

Código da Comunidade

Sempre que você instalar uma extensão disponibilizada na comunidade pelo Magento Connect ele será colocado em um local específico, para não ser interferido por atualizações do core ou modificações de seus próprios módulos:

app / code / community / Package / Modulename

Seu Código

Quando você quiser personalizar ou extender as funcionalidades do Magento, em vez de editar os arquivos diretamente no Core do sistema você deve trabalhar em um local específico para seus módulos em desenvolvimento:

app / code / local / Package / Modulename

O Package (que é também muitas vezes se referida como Namespace) é um nome único que identifica a empresa (ou responsável pelo desenvolvimento). A intenção é que cada membro da comunidade utilize seu próprio nome em seus Packages para não haver conflitos com módulos de outros desenvolvedores.

Quando criamos um novo módulo no Magento, precisamos informá-lo sobre isso. Isso é feito simplesmente inserindo um arquivo XML na pasta:

app / etc / modules

Existem dois tipos de arquivos nessa pasta, o primeiro informa ao Magento um módulo individual e é nomeado nessa forma: Packagename_ModuleName.xml. O segundo tipo é um arquivo que informa vários módulos de um mesmo Package / Namespace, e é nomeado da seguinte forma: Packagename_All.xml

 MVC baseado em Configuração (Configuration-Based MVC)

Conheço duas ótimas maneiras de trabalharmos com MVC. A primeira é a convention-based MVC (MVC baseado em convenção), onde se você quiser adicionar, por exemplo, um novo controller ou model, basta você criar o arquivo e o sistema irá reconhecê-lo automaticamente.

O segundo tipo é o Configuration-Based MVC (MCV baseado em configuração), que é como o Magento é baseado, além de adicionarmos um novo arquivo / classe, muitas vezes temos que informar explicitamente ao sistema sobre os novos recursos incluídos no sistema. No Magento, cada módulo tem um arquivo de configuração chamado config.xml. Esse arquivo tem todas as configurações relevantes para um módulo Magento. Todos esses arquivos serão carregados em uma arvore de configurações em tempo de execução.

Por exemplo, para utilizar um Model em seu módulo você terá que informar isso em seu config.xml, bem como o nome base das classes que todos os seus Models devem ter.

<models>
<packagename>
<class> Package_Modulename_Model</class>
</packagename>
</models>

O mesmo vale para Helpers, Blocks, Routers para os Controllers, Event Handlers, etc. Quase sempre que for preciso adicionar um novo recurso no nosso módulo Magento teremos que modificar o arquivo config.xml.

Controllers

Quem conhece Zend Framework ou qualquer outro framework PHP sabe que a grande maioria deles iniciam com um arquivo de Bootstrap, como o Magento é baseado em ZF não poderia ser diferente.

Para entendermos melhor, vejamos qual o processo que se realiza quando fazemos uma chamada no Magento:

1 – É examinada a URL
2 – Com base em uma série de regras, essa URL é transformada em uma Classe Controller e um Método Action (o que chamamos de roteamento).
3 – É instanciado essa Classe Controller e executado o Método Action (o que chamamos de dispatching).

Analisemos, por exemplo, a seguinte URL:

http://www.example.com/catalog/category/view/id/25

catalog – front name

A primeira parte da URL é chamada de front name. Isso informa, mais ou menos ao Magento em qual módulo será encontrado o Controller. No exemplo acima o front name é catalog, que corresponde ao módulo localizado em:

app / code / core / Mage / catalog

category – Controller Name

A segunda parte da URL informa ao Magento qual Controller deverá ser utilizado. Cada Módulo possui uma pasta (controllers) que contém todos os controllers do módulo. No exemplo acima, o nome category será traduzido para a chamada do arquivo correto:

app / code / core / Mage / catalog / CategoryController.php

Que se parece com isso:

class Mage_Catalog_CategoryController extends Mage_Core_Controller_Front_Action
{
}

view – Action Name

O terceiro lugar da nossa URL estará o nome da Action que deverá ser executada. No nosso exemplo esse nome é “view”, então a palavra view será transformada no método Action que será executado. Teremos então a execução do método “viewAction”:

class Mage_Catalog_CategoryController extends Mage_Core_Controller_Front_Action
{
public function viewAction()
{
}
}

Quem estiver familiarizado com o Zend Framework entenderá essa nomenclatura.

id/25 – Parametro/Valor

Quaisquer porções da URL após a definição do método será considerada um par chave / valor para variáveis do tipo GET. Assim, em nosso exemplo, o “id/25″ significa que teremos uma variável “id” com valor “25″.

Como mencionado anteriormente, para usarmos controllers no nosso módulo teremos que configurá-los em nosso arquivo config.xml. Abaixo temos um trecho de configuração dos controllers do módulo Catalog do Magento:

<frontend>
<routers>
<catalog>
<use>standard</use>
<args>
<module>Mage_Catalog</module>
<frontName>catalog</frontName>
</args>
</catalog>
</routers>
</frontend>

Não se preocupe muito em entender todos os detalhes agora, mas observe o nó <frontName> é ele que faz a ligação do módulo com o Frontname da URL. A maioria dos módulos do Core do Magento escolhem um frontname igual ao nome do módulo, mas isso não é exatamente necessário, você pode escolher qualquer frontname para seus módulos.

A rota descrita acima é a para a aplicação de carrinho de compras do Magento. Se o Magento não encontrar um Controller / Action válido ele tentará novamente dessa vez utilizando um segundo conjunto de regras para o Admin. Se ainda assim o Magento não encontrar um Controller / Action válido ele usará um Controller especial chamado Mage_Cms_IndexController.

Esse Controller irá verificar o CMS do Magento que definirá se há algum conteúdo que deva ser carregado. Se encontrar algum ele carregará, caso não ele trará uma página 404. Um bom exemplo disso é o próprio Index do Magento que utiliza essa página

Carregando Models baseado no contexto da URL

Agora que já descobrimos como o Magento encontra e executa nossos métodos, vamos começar a querer escrever nosso código, instanciar outras classes, etc. O Magento oferece uma maneira especial para instanciarmos Models, Helper e Blocks usanfo alguns métodos estáticos da classe Mage. Por exemplo:

Mage::getModel('catalog/product');
Mage::helper('catalog/product');

A string ‘catalog/product’ é chamada de Grouped Class Name. A primeira parte da String determina em qual módulo ela se encontra, já a segunda parte determina qual classe deverá ser carregada. Assim, em nossos exemplo a primeira parte determina o módulo catálogo (app / code / Mage / catalog). E isso significa que no nome da classe irá começar com Mage_Catalog, e então a segunda parte determina o nome final da classe:

Mage::getModel('catalog/product');
// Mage_Catalog_Model_Product
Mage::hleper('catalog/product');
// Mage_Catalog_Helper_Product

Essa regras são regidas pelo que está escrito no arquivo de configuração de cada módulo. Quando você criar seu módulo, criará também seu nome de classe agrupado para trabalhar com Mage::getModel(‘meuprefixo/modelname’);

Não precisamos usar esses “Grouped Class Names” para instanciar nossas classes, no entando veremos que existem inúmeras vantagens para isso.

Models no Magento

O Magento, como a maioria dos frameworks modernos, nos oferece um ORM(Object Relational Mapping) para trabalharmos. ORMs nos ajudam a deixar de escrever comandos SQLs e permitem que manipulemos dados do banco utilizando simplesmente códigos PHP. Por exemplo:

$model = Mage::getModel('catalog/product')->load( 27 );
$price = $model->getPrice();
$price += 5;
$model->setPrice($price)->setSku('SK325724578');
$model->save();

No exemplo acima chamados os métodos “getPrice” e “setPrice” para nosso produto. No entanto, se você abrir a classe Mage_Catalog_Model_Product você não verá métodos com esses nomes. Isso porque o ORM do Magento utiliza os métodos mágicos __get() e __set() do PHP.

Para que isso aconteça pressupõe-se que não teremos os métodos getPrice e setPrice no Model Product. Se isso acontecer os métodos mágicos serão ignorados e serão executados os métodos da classe instanciada. Se tiver mais interessado em saber como isso funciona, dê uma olhada na classe Varien_Object, todos os Models devem herdar dela.

Uma ótima dica é se você quiser obter todos os dados disponíveis em um Model, você pode chamar o método $product->getData() e ele lhe retornará um array com todos os atributos.

Você notará que poderá encadear várias chamadas para o método set:

$model->setPrice($price)->setSku('SK83293432');

Isso porque cada método retorna a instância do próprio modelo. Esse é um padrão que poderemos observar na maioria do codebase do Magento.

O ORM do Magento também possui uma maneira de consultar multiplos objetos através de uma Interface Collections. O exemplo abaixo nos mostra como pegar todos os produtos que custam R$ 5,00:

$products_collections = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addFieldToFilter('price','5.00');
foreach( $products_collection as $product )
{
echo $product->getName();
}

Você deve está se perguntando para que o método addAttributeToSelect() serve. O Magento tem dois grandes tipos de objetos Model. Um deles é o tradicional “um Objeto = uma Tabela”. Quando instanciamos esse tipo de objeto, todos os atributos são automaticamente selecionados.

O segundo tipo é o Entity Attribute Value (EAV). Modelos EAV disseminam os dados em várias tabelas do banco de dados. Isso permite ao sistema proposcionar uma flexibibildade suficiente para oferecer a criação de diversos atributos para cada produto sem ter que fazer qualquer alteração no esquema do banco de dados. Ao criar um collection de objetos EAV o Magento é bastante cauteloso em relação as colunas que irão para a consulta, assim podemos utilizar o método addAttributeToSelect() para obter somente as colunas que precisamos, ou utilizar addAttributeToSelect(‘*’) para obter todas as colunas.

Helpers

Classes Helpers no Magento contém métodos utilitários que nos permite executar tarefas comuns em diversos objetos. Por exemplo:

$helper = Mage::helper('catalog');

Você vai perceber que deixamos de lado a segunda parte do nome da classe agrupada. Cada módulo tem sua própria classe Helper default. O código seguinte é equivalente ao de cima:

$helper = Mage::helper('catalog/data');

A maioria dos Helpers herdam da classe Mage_Core_Helper_Abstract, que trás vários métodos úteis por padrão.

Layouts

Bem, já vimos como funciona os Controllers, Models e Helpers. E em um sistema MVC típico, após manipularmos nossos dados na camada de Model nós:

1 – Definimos algumas variáveis para nossas Views.
2 – O sistema irá criar a saída HTML padrão.
3 – O sistema irá carregar nossa View dentro do layout padrão.

No entanto, se olharmos um Controller Action no Magento não veremos nada disso:

/**
* View product gallery action
**/

public function galleryAction()
{
if(!$this->_initProduct()){
if (isset($_GET['store']) && !$this->getResponse()->isRedirect()) {
$this->_redirect(”);
}elseif (!$this->getResponse()->isRedirect()) {
$this->_forward(‘noRoute’);
}
return;
}

$this->loadLayout();
$this->renderLayout();
}

Observe os dois últimos comandos executados no Controller Action. Veja que o “V” do MVC no Magento já é diferente do que estamos acostumados, aqui precisamos explicitamente executar a renderização do layout.

O layout também é bem diferente. Um layout no Magento é constituído por uma objeto que contém uma coleção aninhada de objetos do tipo “Block”. Cada objeto Block irá renderizar um pedaço de HTML. Objetos Block fazem isso combinando código PHP e incluindo arquivos .phtml de templates.

O objetos Block destinam-se a interagir com Magento buscando dados dos Models enquanto os arquivos .phtml se responsabilizam em em produzir o HTML necessário para uma página. Por exemplo, o Block do cabeçalho (app/code/core/Mage/Page/Block/Html/Head.php) usa o arquivo head.phtml (/html/head.phtml).

Outra maneira de entender isso é pensar que as classes Block funcionam como mini-controllers enquanto os arquivos .phtml funcionam como as Views. Por padrão, quando chamamos

$this->loadLayout();
$this->renderLayout();

Magento irá carregar um estrutura como um esqueleto do site. Esses blocos estruturais trazem o básico do HTML, header, body, bem como a configuração para quantidade de colunas do layout e além disso, alguns blocos de conteúdo como navegação, mensagem padrão de boas vindas, etc.

Blocos estruturais e Blocos de conteúdo são designações arbitrárias no sistema de layout do Magento. Não temos como saber, programaticamente, se um bloco é do tipo estrutura ou conteúdo, mas é útil pensar neles como um ou outro.

Existem duas maneiras básicas de se adicionar conteúdo no Layout do Magento. A primeira maneira pode ser feita por programação em um Controller Action:

public function indexAction()
{
$block = $this-&gt;getLayout()-&gt;createBlock('adminhtml/system_account_edit');
$this-&gt;getLayout()-&gt;getBlock('content')-&gt;append($block);
}
mas mais frequentemente (pelo ao menos da aplicação do carrinho de compras), usamos o sistema de XML do Layout.
Os arquivos de XML do Layout em um tema, permitem que retiremos blocos que normalmente seriam renderizados ou adicionar novos blocos no esqueleto padrão do layout. Por exemplo, considere o layout do arquivo XML:
[xml]
&lt;catalog_category_default&gt;
&lt;reference name="left"&gt;
&lt;block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml" /&gt;
&lt;/reference&gt;
&lt;/catalog_category_default&gt;
[/xml]
O XML informa que no Módulo Catalog, no Controller Category, no indexAction seja inserido o Block <em>catalog/navigation</em> dentro do Block de estrutura left, usando o <em>catalog/navigation/left.phtml</em>
Uma última coisa importante sobre Blocks. Muitas vezes veremos códigos em templates que se parecem com isso:
[php]
$this-&gt;getChildHtml('order_items');

Isso é como um Block renderiza um outro Block aninhado. No entanto, um Block só poderá renderizar um Block aninhado se o mesmo tiver sido declarado como um nó filho dentro do arquivo XML do layout. No exemplo acima nosso Block catalog/navigation não possui Blocks aninhados, isso significa que qualquer chamada $this->getChildHtml() retornará em branco. Porém, se tivermos algo como:

<catalog_category_default>
<reference name="left">
<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml">
<block type="core/template" name="foobar" template="foo/baz/bar.phtml" />
</block>
</reference>
</catalog_category_default>

do Block category/navigation seriamos capaz de chamar $this->getChidHtml(‘foobar’);.

Observers

Como todo bom sistema orientado a objetos, Magento implementa um padrão Event/Observer para os usuários finais fazerem suas ligações. Como certas ações acontecem durante a requisição de uma página (um Model salva alguma coisa, um usuário pode fazer um login, etc.), então quando isso acontece o Magento envia um sinal de evento.

Ao desenvolver nossos módulos podemos criar métodos para “ouvir” esses eventos. Digamos que seja preciso enviar um e-mail toda vez que um determinado usuário efetue login na loja. Para isso basta ouvirmos o evento “customer_login”(setup no config.xml):

<events>
<customer_login>
<observers>
<unique_name>
<type>singleton</type>
<class>mymodule/observer</class>
<method>iSpyWithMyLittleEye</method>
</unique_name>
</observers>
</customer_login>
</events>

e escrever o método que será executado toda vez que um usuário efetuar login:

class PackageName_Mymodule_Model_Observer
{
public function iSpyWithMyLitleEye($observer)
{
$data = $observer-&gt;getData();
// Código para verificar se é o seu usuário
// e enviar o e-mail se necessário
}
}
<h4><span style="color: #ffcc00;">Sobrescritas de Classes</span></h4>
Finalmente, o Magento oferece a possibilidade de sobrescrever Models, Helpers e Blocks dos módulos do Core pelos códigos dos seus próprios Módulos. Essa é uma característica que é semelhante ao "Duck Typing" ou "Patching Monkey" na linguagem Ruby ou Python.
Aqui está um exemplo para nos ajudar a entender. A classe Model do Módulo product é Mage_Catalog_Model_Product. Sempre que o código a seguir é chamado um objeto Mage_Catalog_Model_Product é criado
[php]
$product = Mage::getModel('catalog/product');

Digamos que o que queremos é sobrescrever essa classe por uma classe do meu módulo. Então, o que temos que fazer é criar nossa classe extendendo da classe original:

class PackageName_ModuleName_Model_Foobazproduct extends Mage_Catalog_Model_Product
{
}

Fazer dessa maneira nos permitirá mudar o comportamento de qualquer método da classe e manter o comportamento dos métodos existentes:

class PackageName_ModuleName_Model_Foobazproduct extends Mage_Catalog_Model_Product
{
public function validate()
{
// adiciona as funcionalidades aqui
return $this;
}
}

E como já é de se imaginar, precisamos informar ao Magento sobre essa sobrescrita, e não há meneira melhor de se fazer isso do que em um arquivo config.xml:

<models>
<!-- Informa ao sistema que esse modulo tem Models -->
<modulename>
<class>Packagename_Modulename_Model</class>
</modulename>
<!-- Aqui informo sobre a sobrescrita -->
<catalog>
<rewrite>
<product>Packagename_Modulename_Model_Foobazproduct</product>
</rewrite>
</catalog>
</models>

Temos que notar algo importante aqui. Quando sobrescrevemos um Model, não estamos sobrescrevendo um módulo inteiro, podemos simplesmente sobrescrever um único método de uma classe e o resto do módulo funcionará normalmente.

Bem, eu sei que é muita informação para se absorver logo de início, mas leia algumas vezes, efetue alguns testes e tenho certeza que logo estará apto a se tornar um brande programador Magento.

Esse é um artigo que eu adaptei do próprio site do Magento, acho válido termos um bom material como esses em português, para quem está iniciando. Deixem os comentários :)

Fonte: Magento Commerce