Categoria: magento

Atualização Magento via Magento Connect

Toda vez que sai uma nova versão do Magento, todos ficam ansiosos em ver as novas funcionalidades e em colocar na sua loja.

Pensando no conceito do Magento Connect, é algo fácil não é? Basta acessar o Connect, inserir a chave (key) http://connect20.magentocommerce.com/community/Mage_All_Latest e pronto!

Bem, se fosse dessa forma, seria perfeito não é mesmo?

Não vamos discutir aqui os (possíveis) problemas decorrentes dessa atualização, mas apenas o processo em si.

O primeiro grande problema que podemos ter, é esse:

  
OBS: essa imagem é da versão 1.0 do connect, mas serve para ilustrar o problema.

Seu Magento connect reclama que há algum problema com as permissões da sua instalação.

Em alguns casos, é aí que começa o pesadelo!

Se você possui acesso SSH é bem simples resolver.

Entre no diretório da sua instalação e execute os seguintes comandos:

find . -type f -exec chmod 644 {} \;
find . -type d -exec chmod 755 {} \;
chmod 550 pear
chmod 550 mage #for magento 1.5+

Se preferir (e seu provedor deixar), utilize a ferramenta Magento Cleanup, que irá acertas as permissões para você. Dúvidas? Clique aqui e veja mais detalhes e onde baixar o arquivo.

MAS, se o seu provedor não te dá acesso a SSH e quando tenta executar o Magento Cleanup é inundado de mensagens “Permissão Negada”,  além de trocar de servidor resta uma última alternativa: Via FTP, altere todo o conteúdo da pasta downloader para 777.

Se mesmo assim não funcionar, altere apenas a permissão da pasta de instalação para 777 (não todos os arquivos de forma recursiva).

Pronto, nesse momento é então para você já conseguir inserir a chave http://connect20.magentocommerce.com/community/Mage_All_Latest e mandar instalar!

MAS, acontece outro erro chato!

Na “telinha preta” parece que está indo tudo bem, tá fazendo os downloads, quando de repente para tudo e você vê a seguinte mensagem:

CONNECT ERROR: Package 'Mage_All_Latest' is invalid
'./pkginfo/Mage_All_Latest.txt' already exists
Package 'Interface_Adminhtml_Default' is invalid
'./app/design/adminhtml/default/default/layout/admin.xml' already exists.. (mais um monte de mensagens similares)

Bem,  não sei se é algum BUG, pois esse problema existe faz tempo e na versão 1.7.0.0 ainda consta o mesmo problema.

Fato é que ele irá dar esse erro em qualquer atualização que já conste arquivos antigos. Para “acabar” com esse erro e deixar a atualização ocorrer com tranquilidade, altere o  arquivo {{diretório de instalação magento}}/downloader/lib/Mage/Connect/Validator.php:

Comente/Substitua o método public function validateContents(array $contents, $config){…}

encontrado no final do arquivo por:

 public function validateContents(array $contents, $config)
    {
        if (!count($contents)) {
            $this->addError('Empty package contents section');
            return false;
        }

        return true;
    }

Bem, espero que ajude alguém!

Magento: Módulo free para Itaushopline

Olá Pessoal, tudo bem??

Tenhos um módulo itaushopline que foi desenvolvido ha um bom tempo (versão 1.2 do Magento se não me engano!).
Bem, foi feita uma atualização do módulo mas precisamos de usuários que possam nos ajudar a testar.

Interessados em ajudar, por favor enviar adicionar o skype portalwebgp ou mandar um email para contato@webgp.com.br.

Futuramente serão solicitados usuários para o Bradesco Fácil tambem!

Magento: Erro ao adicionar produto no carrinho – Regra de Preço

Pessoal,

Há algum tempo atrás fiz um post sobre um erro bobo, aparentemente um bug do magento: Após criar uma regra de desconto para uma forma de pagamento, se você adicionasse o grupo de clientes NOT LOGGED IN, era impossível incluir o produto no carrinho.

Aparecia a mensagem:

cannot add items to the shopping cart.

Bem, uma das soluções apresentadas foi alterar o arquivo app/code/core/Mage/Sales/Model/Quote.php.

Em app/code/Core / Mage / Sales / Model / Quote.php Iinsira o seguinte código no método _afterSave() :

if (null !== $this->_payments) {
           // inserted code start
            $this->getPaymentsCollection();
            if ($this->getId()) {
                foreach ($this->_payments as $payment) {
                    $payment->setQuote($this);
                }
            }
           // inserted code end
            $this->getPaymentsCollection()->save();
        }

Lembrando que o ideal é criar uma cópia do arquivo acima e colocar em app/code/local. Dessa forma se houver alguma atualização você não perderá sua configuração.

Magento: Módulo Offline dos Correios

Olá Pessoal,

Como já avisamos antes, o nosso site está em manutenção, pois estamos integrando o Magento com o WordPress.

Mas, a partir de amanhã (05/out/2010) o site já estará totalmente funcional e iremos disponibilizar para download gratuito o módulo offline dos correios.

Magento: Cuidado com o CACHE!!!!

Bom dia pessoal,

Esse post é destinado à todos que se aventuram no mundo Magento, incluindo quem tá começando a trabalhar no desenvolvimento, assim como o usuário final que apenas gerencia a sua loja.

Esse post não é para falar sobre as maravilhas do cache nem sobre seus problemas. Na verdade, nem detalhes sobre o seu funcionamento!

Em todos os tutoriais que coloco aqui, sempre chamo a atenção para DESABILITAREM o Cache e até mesmo dar um refresh. Somente dessa forma você poderá ver alterações em seu site!
Não adianta você trocar a configuração de template, se você não atualizar seu cache!

Há um tempo atrás, tive o seguinte problema com um cliente:
Desenvolvi um módulo personalizado e fui fazer a instalação. Como sempre, desabilitei o cache e instalei. Apesar disso, o módulo não aparecia no admin. Sendo assim, dei um refresh no cache e esperei para ver o que acontecia….

Bem, o módulo apareceu no admin mas toda a parte de template ficou bagunçada. Partes do template não estava mais aparecendo, ficou horrivel e impossível de navegar pela loja.

O cliente então, passou a falar que o problema devia ser o módulo instalado! Após afirmar repetidas vezes que o problema não era esse, o cliente ainda não estava convencido. Dizia que fazia tempos qeu não mexia lá e que o simples fato de eu ter atualizado o cache não deveria ter causado problemas.

Bem, aí comecei então a depurar a loja, para descobrir o que havia acontecido.

A primeira coisa que me ocorreu foi: o template não está configurado certo. De alguma maneira ele configurou errado e só agora com o refresh apareceu. Ao olhar na configuração do cliente, estava configurado um template chamado f002.
Estava correto, pois eu tinah visto esse pacote no servidor.
Sendo assim, habilitei o template path hints do Magento, e qual a minha “surpresa” quando vi que os principais arquivos do template estavam sendo “substituidos” hierarquicamente pelos arquivos da pasta default.
Mas isso era inacreditável, já que a pasta estava lá e a configuração estava correta.
Após mais de uma hora buscando as mais loucas explicações, uma luz surgiu ao final do túnel:

Fui passar uns dados para o cliente via msn, e coloquei o caminho de determinado arquivo de cabeça. Passado alguns minutos, fui olhar no servidor e vi que tinha passado errado, pois passei f002 e na verdade era f001. EUREKA!

O cliente tinha em seu servidor uma pasta chamada f001 e havia configurado no admin f002!!!!

Mas como a loja estava funcionando corretamente? Como eu não havia percebido antes?

Bem, eu não havia percebido pois estou cansada de trabalhar com o f001 e o f002, que são dois templates gratuitos disponibilizados em um blog. Além disso, ele realmente tinha uma pasta chamada f002 que não tinha nada a ver com os templates.
Por cansado ou por besteira, apesar de saber qual era o erro, não conseguia “enxergá-lo”.

A loja estava funcionando ha tempos perfeitamente por causa do cache! O cache estava “errado” e ao forçar sua atualização, a “casa caiu”.

Somente após passar todas essas informações, o cliente viu que o problema estava nas alterações que fazia em sua loja e não dava refresh no cache!

Após esse incidente, antes de fazer qualquer coisa em uma loja peço para o cliente dar refresh e ver se está tudo ok. Dessa forma, o próprio cliente pode verificar se sua loja está funcionando corretamente ou não.

Bem, semana passada fui fazer a instalação de um módulo para outro cliente. Solicitei ao mesmo que desse um refresh no cache e ele não sabia nem onde ir. Apesar de explicar, ele não conseguiu.
Entrei no admin e dei um refresh … Adivinhem o que aconteceu? O template, como de costume, ficou distorcido.

Lógico, que o cliente veio irado, falando queeu tinha causado o problema!
Até você explicar que nariz de porco não é tomada….

Então, aí vai mais um alerta, principalmente para os desenvolvedores:

Antes de atualizar o cache em uma loja, deixe bem claro para o cliente o que isso pode acarretar e faça ele assinar uma autorização para o procedimento….rsrs.

Modelagem de Dados no Magento – Parte III

Parte I
Parte II

Nas partes anteriores do nosso tutorial já vimos como manipulamos certos tipos de dados no BD.
Na verdade, vimos a parte simples, onde os dados estão persistidos em uma única tabela. Mas e o caso do EAV?
Bem, acredito que na maioria das vezes que você precisar criar uma tabela específica para um módulo, você irá optar pelo jeito mais simples, onde cada “objeto” reflete o registro de uma única tabela.
Mas, mesmo que você não for criar um conjunto de tabelas EAV para representar um objeto, você precisa saber o básico para conseguir lidar bem com os Produtos.
Como buscar produtos no BD que tenham apenas uma determinada cor? Busco da mesma forma que estivesse buscando um registro qualquer?

É isso que veremos nessa parte do tutorial.

Bem, mas o que seria o modelo EAV (Entidade – Atributo – Valor)?
Para quem “arranha” no inglês, você poder ver uma discussão sobre o assunto aqui.

Não vou me aprofundar muito, mas seria algo mais ou menos assim:

Quando você tem um “objeto” que pode ter uma quantidade muito grande de dados, é interesse você dividir essas informações em várias tabelas.

Vamos usar o exemplo do Produto, dentro do próprio Magento.

Você pode criar quantos atributos você quiser para um Produto e cada atributo pode ser de N tipos (string, inteiro, texto, decimal, data e etc). Qual a forma mais fácil de modelar esse objeto Produto no nosso BD?

Bem, seria interessente criar uma tabela que define o nosso produto (nossa entidade, vamos assim dizer), tabelas que definam os atrbutos que essa entidade irá ter, e por fim, tabelas com os valores desse atributo.

É mais ou menos assim que estão estruturadas as tabelas no nosso BD do Magento: Há uma tabela de definição do produto, outra que define quais atributos este produto tem, e tabelas próprias para cada tipo (texto, data, inteiro, etc) que irão guardar os valores do atributo.

Para entrarmos a fundo nessa questão da modelagem, vamos mudar um pouco o exemplo (módulo para meucomentario) que temos seguido até aqui. Acho que ficará mais fácil assimilar o “drama”.

Vamos utilizar um exemplo real, de um módulo que iremos disponibilizar logo logo aqui no blog: Forma de Pagamento – Financiamento.

Um cliente uma vez solicitou um módulo que permitisse que o cliente preenchesse uma ficha de financiamento, em vez de simplesmente pagar com boleto ou etc.

Ou seja, no momento do checkout o cliente escolheria o método “Financiamento”, preencheria um formulário que posteriormente seria avaliado.

A primeira pergunta que tive foi a seguinte: Mas quais campos esse formulário vai ter?
A resposta foi: “Depende”.

Na verdade o cliente poderia escolher entre Pessoa Física e Jurídica e de acordo com sua escolha, o formulário teria itens diferenciados.

Além disso, para que o módulo em questão fosse utilizado por mais de uma loja, seria interessante que esses campos fossem configuráveis.

Mas como fazer isso em relação banco de dados, já que cada campo poderia ser de um tipo diferente (data, texto, inteiro e etc)?
O jeito seria criar uma tabela para campos do tipo texto, outra para data e assim por diante.

Bem, o cadastro se parece muito com o cadastro de um produto certo? Pq então não utilizar o mesmo modelo de dados?

Assim foi definida a utilização do EAV!

Como esse tutorial não tem como objetivo a criação de novos módulos, só vou citar as configurações específicas para o modelo de dados utilizado.

Se achar interessante, modifique o exemplo anterior para salvar os comentários em um modelo EAV.

CONFIG.XML

   <!-- ... -->
    <global>
        <models>
            <financiamentooffline>
                <class>Webgp_FinanciamentoOffline_Model</class>
                <resourceModel>financiamentooffline_resource_eav_mysql4</resourceModel>
            </financiamentooffline>
            <financiamentooffline_resource_eav_mysql4>
                <class>Webgp_FinanciamentoOffline_Model_Resource_Eav_Mysql4</class>
                <entities>
                    <proposta>
                        <table>financiamentooffline_proposta</table>
                    </proposta>
                    <eav_attribute><table>financiamentooffline_eav_attribute</table></eav_attribute>
                </entities>
            </financiamentooffline_resource_eav_mysql4>
        </models>
        <resources>
            <financiamentooffline_setup>
                <setup>
                    <module>Webgp_FinanciamentoOffline</module>
                    <class>Webgp_FinanciamentoOffline_Model_Entity_Setup</class>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </financiamentooffline_setup>
            <financiamentooffline_write>
                <connection>
                    <use>core_write</use>
                </connection>
            </financiamentooffline_write>
            <financiamentooffline_read>
                <connection>
                    <use>core_read</use>
                </connection>
            </financiamentooffline_read>
        </resources>
        <!-- ... -->
    <global>
<!-- ... -->

No nossa definição de modelo notamos que a diferença está no nosso resourceModel, que passa a utilizar a estrutura resource_eav_mysql4.
Na configuração do nosso novo resourceModel, temos que listar nossas entidades. Nesse caso, temos uma entidade chamada proposta (será chamada como Mage::getModel(‘financiamentooffline/proposta’)), que representa o nosso Model principal (seria como Produto, por exemplo) e também uma entidade chamada eav_attribute, que irá guardar as definições dos novos atributos.
Caso você queira utilizar alguma tabela FLAT para representar uma entidade, configure-a normalmente no config.xml. A diferenciação do seu real resource iremos fazer na hora de definir a classe.
Uma diferença também nessa configuração é a definição de uma classe específica para o nosso Setup (Webgp_FinanciamentoOffline_Model_Entity_Setup), que veremos adianta para que ela serve!

mysql4-install-0.1.0.php

<?php
$installer = $this;
$installer->startSetup();
$installer->run("
CREATE TABLE `{$installer->getTable('financiamentooffline/eav_attribute')}` (
  `attribute_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `frontend_input_renderer` varchar(255) DEFAULT NULL,
  `is_global` tinyint(1) unsigned NOT NULL DEFAULT '1',
  `is_visible` tinyint(1) unsigned NOT NULL DEFAULT '1',
  `position` int(11) NOT NULL,
  PRIMARY KEY (`attribute_id`),
  CONSTRAINT `FK_FINANCIAMENTOOFFILINE_EAV_ATTRIBUTE_ID` FOREIGN KEY (`attribute_id`) REFERENCES `{$installer->getTable('eav/attribute')}` (`attribute_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
$installer->endSetup();
$installer->addEntityType('financiamentooffline_proposta',
          Array(
              'entity_model'          =>'financiamentooffline/proposta',
              'attribute_model'       =>'financiamentooffline/resource_eav_attribute',
              'table'         =>'financiamentooffline/proposta',
              'increment_model'       =>'eav/entity_increment_numeric',
              'increment_per_store'   =>'1'
));
<p>$installer->createEntityTables($this->getTable('financiamentooffline/proposta'));
$installer->installEntities();
$describe = $installer->getConnection()->describeTable($installer->getTable('financiamentooffline/eav_attribute'));
foreach ($describe as $columnData) {
    if ($columnData['COLUMN_NAME'] == 'attribute_id') {
        continue;
    }
    $installer->getConnection()->dropColumn($installer->getTable('eav/attribute'), $columnData['COLUMN_NAME']);
}

O nosso arquivo de setup cria a tabela que definimos em config.xml para a entidade ‘financiamentooffline/eav_attribute’.
Além disso, ele cria todas as tabelas para a modelagem EAV:
Antes de tudo precisamos criar um novo tipo de entidade. Como, por exemplo, iremos saber se um conjunto de atributos faz parte de Produtos ou Propostas?
Você pode ver os tipos já cadastrados na tabela eav_entity_type.

Depois que criamos o nosso novo tipo, precisamos criar as tabelas para os tipos de dados (date, varchar, int e etc). Para isso chamamos o método createEntityTables(..).

Quando instalamos o magento, vemos que os produtos já possuem alguns atributos pré cadastrados certo? Na nossa classe de Setup iremos definir os atributos que devem ser instalados inicialmente. Para cadastrá-los no banco precisamos chamar o método installEntities().

Pronto!
Quando nosso módulo for instalado, você verá que as seguintes tabelas foram criadas:

Setup.php

<?php
class Webgp_FinanciamentoOffline_Model_Entity_Setup extends Mage_Eav_Model_Entity_Setup {
    public function getDefaultEntities()
    {
        return array (
            'financiamentooffline_proposta' => array(
                'entity_model'      => 'financiamentooffline/proposta',
                'attribute_model'   => 'financiamentooffline/resource_eav_attribute',
                'table'             => 'financiamentooffline/proposta',
                'increment_model'       =>'eav/entity_increment_numeric',
                'additional_attribute_table' => 'financiamentooffline/eav_attribute',
                'entity_attribute_collection' => 'financiamentooffline/eav_attribute',
                'attributes'        => array(
                    'nome' => array(
                        'type'              => 'varchar',
                        'backend'           => '',
                        'frontend'          => '',
                        'label'             => 'Nome',
                        'input'             => 'text',
                        'class'             => '',
                        'source'            => '',
                        // store scope == 0
                        // global scope == 1
                        // website scope == 2
                        'global'            => 0,
                        'visible'           => true,
                        'required'          => true,
                        'user_defined'      => true,
                        'default'           => '',
                        'unique'            => false,
                    ),
                ),
            )
        );
    }

Neste ponto do tutorial você já deve ser capaz de identificar a localização desse arquivo apenas olhando o nome da classe (Webgp/FinanciamentoOffline/Model/Entity/Setup.php).

Quando chamamos o método installEntities() no arquivo mysql4-install-0.1.0.php estamos mandando que as entidades definidas em getDefaultEntities() sejam instaladas.
No nosso exemplo, estamos definindo a entidade financiamento_proposta. O conteúdo do array é bem intuitivo. Apenas como exemplo, estamos cadastrando o atributo NOME.

Vamos mostrar agora as classes referentes ao nosso modelo Proposta e seus respectivos resources.

<?php
class Webgp_FinanciamentoOffline_Model_Resource_Eav_Mysql4_Proposta extends Mage_Eav_Model_Entity_Abstract
{
    public function _construct()
    {
        $resource = Mage::getSingleton('core/resource');
        $this->setType('financiamentooffline_proposta');
          $this->setConnection(
                $resource->getConnection('financiamentooffline_read'),
                $resource->getConnection('financiamentooffline_write')
            );
    }
       /**
     * Default proposta attributes
     *
     * @return array
     */
    protected function _getDefaultAttributes()
    {
        return array('entity_id', 'entity_type_id', 'attribute_set_id', 'order_id', 'is_active', 'created_at', 'updated_at');
    }
<?php
class Webgp_FinanciamentoOffline_Model_Resource_Eav_Mysql4_Proposta_Attribute_Collection extends Mage_Eav_Model_Mysql4_Entity_Attribute_Collection
{
    /**
     * Resource model initialization
     */
    public function _construct()
    {
        $this->_init('financiamentooffline/resource_eav_attribute', 'eav/entity_attribute');
    }
    protected function _initSelect()
    {
        $this->getSelect()->from(array('main_table' => $this->getResource()->getMainTable()))
            ->where('main_table.entity_type_id=?', Mage::getModel('eav/entity')->setType('financiamentooffline_proposta')->getTypeId())
            ->join(
                array('additional_table' => $this->getTable('financiamentooffline/eav_attribute')),
                'additional_table.attribute_id=main_table.attribute_id'
            );
        return $this;
    }
    /**
     * Specify attribute entity type filter
     *
     * @param   int $typeId
     */
    public function setEntityTypeFilter($typeId)
    {
        return $this;
    }
    /**
     * Return array of fields to load attribute values
     *
     * @return array
     */
    protected function _getLoadDataFields()
    {
        $fields = parent::_getLoadDataFields();
        $fields = array_merge($fields, array('additional_table.is_global'));
        return $fields;
    }
    /**
     * Specify filter by "is_visible" field
     *
     */
    public function addVisibleFilter()
    {
        $this->getSelect()->where('additional_table.is_visible=?', 1);
        return $this;
    }
}
<?php
class Webgp_FinanciamentoOffline_Model_Resource_Eav_Mysql4_Proposta_Collection extends Mage_Eav_Model_Entity_Collection_Abstract
{
    protected function _construct()
    {
        $this->_init('financiamentooffline/proposta');
    }
}
<?php
class Webgp_FinanciamentoOffline_Model_Resource_Eav_Attribute extends Mage_Eav_Model_Entity_Attribute
{
    const SCOPE_STORE   = 0;
    const SCOPE_GLOBAL  = 1;
    const SCOPE_WEBSITE = 2;
    const MODULE_NAME   = 'Webgp_FinanciamentoOffline';
    const ENTITY        = 'financiamentooffline_eav_attribute';
    protected $_eventPrefix = 'financiamentooffline_entity_attribute';
    protected $_eventObject = 'attribute';
    /**
     * Array with labels
     *
     * @var array
     */
    static protected $_labels = null;
    protected function _construct()
    {
        $this->_init('financiamentooffline/attribute');
    }
    /**
     * Processing object before save data
     *
     * @return Mage_Core_Model_Abstract
     */
    protected function _beforeSave()
    {
        $this->setData('modulePrefix', self::MODULE_NAME);
        if (isset($this->_origData['is_global'])) {
            if (!isset($this->_data['is_global'])) {
                Mage::throwException('0_o');
            }
        }
        return parent::_beforeSave();
    }
    /**
     * Processing object after save data
     *
     * @return Mage_Core_Model_Abstract
     */
    protected function _afterSave()
    {
        /**
         * Fix saving attribute in admin
         */
        Mage::getSingleton('eav/config')->clear();
        return parent::_afterSave();
    }
    /**
     * Init indexing process after attribute data commit
     *
     */
    public function afterCommitCallback()
    {
        parent::afterCommitCallback();
        return $this;
    }
    /**
     * Register indexing event before delete financiamentooffline eav attribute
     *
     */
    protected function _beforeDelete()
    {
        return parent::_beforeDelete();
    }
    /**
     * Init indexing process after catalog eav attribute delete commit
     *
     */
    protected function _afterDeleteCommit()
    {
        parent::_afterDeleteCommit();
    }
    /**
     * Return is attribute global
     *
     * @return integer
     */
    public function getIsGlobal()
    {
        return $this->_getData('is_global');
    }
    /**
     * Retrieve attribute is global scope flag
     *
     * @return bool
     */
    public function isScopeGlobal()
    {
        return $this->getIsGlobal() == self::SCOPE_GLOBAL;
    }
    /**
     * Retrieve attribute is website scope website
     *
     * @return bool
     */
    public function isScopeWebsite()
    {
        return $this->getIsGlobal() == self::SCOPE_WEBSITE;
    }
    /**
     * Retrieve attribute is store scope flag
     *
     * @return bool
     */
    public function isScopeStore()
    {
        return !$this->isScopeGlobal() && !$this->isScopeWebsite();
    }
    /**
     * Retrieve store id
     *
     * @return int
     */
    public function getStoreId()
    {
        if ($dataObject = $this->getDataObject()) {
            return $dataObject->getStoreId();
        }
        return $this->getData('store_id');
    }
    /**
     * Retrieve source model
     *
     * @return Mage_Eav_Model_Entity_Attribute_Source_Abstract
     */
    public function getSourceModel()
    {
        $model = $this->getData('source_model');
        if (empty($model)) {
            if ($this->getBackendType() == 'int' && $this->getFrontendInput() == 'select') {
                return 'eav/entity_attribute_source_table';
            }
        }
        return $model;
    }
    /**
     * Retrieve don't translated frontend label
     *
     * @return string
     */
    public function getFrontendLabel()
    {
        return $this->_getData('frontend_label');
    }
    /**
     * Get Attribute translated label for store
     *
     * @deprecated
     * @return string
     */
    protected function _getLabelForStore()
    {
        return $this->getFrontendLabel();
    }
    /**
     * Initialize store Labels for attributes
     *
     * @deprecated
     * @param int $storeId
     */
    public static function initLabels($storeId = null)
    {
        if (is_null(self::$_labels)) {
            if (is_null($storeId)) {
                $storeId = Mage::app()->getStore()->getId();
            }
            $attributeLabels = array();
            $attributes = Mage::getResourceSingleton('financiamentooffline/proposta')->getAttributesByCode();
            foreach ($attributes as $attribute) {
                if (strlen($attribute->getData('frontend_label')) > 0) {
                    $attributeLabels[] = $attribute->getData('frontend_label');
                }
            }
            self::$_labels = Mage::app()->getTranslator()->getResource()->getTranslationArrayByStrings($attributeLabels, $storeId);
        }
    }
    /**
     * Get default attribute source model
     *
     * @return string
     */
    public function _getDefaultSourceModel()
    {
        return 'eav/entity_attribute_source_table';
    }
    /**
     * Check is an attribute used in EAV index
     *
     * @return bool
     */
    public function isIndexable()
    {
        $backendType    = $this->getBackendType();
        $frontendInput  = $this->getFrontendInput();
        if ($backendType == 'int' && $frontendInput == 'select') {
            return true;
        } else if ($backendType == 'varchar' && $frontendInput == 'multiselect') {
            return true;
        } else if ($backendType == 'decimal') {
            return true;
        }
        return false;
    }
    /**
     * Retrieve index type for indexable attribute
     *
     * @return string|false
     */
    public function getIndexType()
    {
        if (!$this->isIndexable()) {
            return false;
        }
        if ($this->getBackendType() == 'decimal') {
            return 'decimal';
        }
        return 'source';
    }
}
<?php
class Webgp_FinanciamentoOffline_Model_Proposta extends Mage_Core_Model_Abstract {
    protected function _construct()
    {
        $this->_init('financiamentooffline/proposta');
    }
   public function getAttributesBySetAndGroup($groupId = 0, $setId = 0)
    {
        return  $this->getAttributesBySet($setId)->setAttributeGroupFilter($groupId);
    }
    public function getAttributesBySet($setId = 0)
    {
        return  Mage::getResourceModel('financiamentooffline/proposta_attribute_collection')->setAttributeSetFilter($setId);
    }
    /**
     *
     * @param   int $attributeId
     * @return  Mage_Eav_Model_Entity_Attribute_Abstract
     */
    public function getAttributeById($attributeId, $setId = 0)
    {
        foreach ($this->getAttributesBySet($setId) as $attribute) {
            if ($attribute->getId() == $attributeId) {
                return $attribute;
            }
        }
        return null;
    }

Vamos lembrar que cada atributo pertence a um grupo e a um conjunto de atributos. Ao instalar as entidades, automaticamente o Magento já cria um Conjunto chamado Default e um grupo também chamado Default.
O Conjunto seria os conjuntos de atributos que você cria para os produtos.

Essa parte de EAV é muito extensa e o assunto parece não ter fim!
Espero que tenha dado para vocês entenderem! Só para fechar..


//instanciar uma proposta com id = 21
        $proposta    = Mage::getModel('financiamentooffline/proposta')->load(21);
//recuperar conjunto de atributos cadastrados para as propostas
        $collection = Mage::getResourceModel('financiamentooffline/proposta_attribute_collection');
//salvar um atributo
        $model = Mage::getModel('financiamentooffline/resource_eav_attribute');
        $model->setEntityTypeId(Mage::getModel('eav/entity')->setType('financiamentooffline_proposta')->getTypeId());
         ...
        $model->save();

Tags:, , , ,

Modelagem de Dados no Magento – Parte II

Dando continuidade ao nosso tutorial iniciado aqui, vamos criar um módulo bem simples para exemplificar a utilização doe Models dentro do Magento.

Como o intuito desse tutorial não é a criação de módulos, que já foi visto em posts anteriores, irei considerar que os leitores já sabem como criá-los.
Caso tenha alguma dúvida, siga este artigo.

Vamos criar um módulo que permita um usuário postar comentários sobre a loja, utilizando um Model que represente uma única tabela.

Passo 1)
Crie um módulo com o seguinte esqueleto em app/code/local:

Aqui é Namespace que escolhi e o meu módulo chama-se MeuComentario.

* Em Aqui/MeuComentario/Helper/Data.php, coloque o seguinte código:

&lt;?php
class Aqui_MeuComentario_Helper_Data extends Mage_Core_Helper_Abstract
{

}

* Em Aqui/MeuComentario/controllers/IndexController.php, coloque o seguinte código:

class Aqui_MeuComentario_IndexController extends Mage_Core_Controller_Front_Action {
    public function testeAction() {
        echo 'Ola!';
    }
}

* Em Aqui/MeuComentario/etc/config.xml, vamos incluir o seguinte código:

&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;config&gt;
    &lt;modules&gt;
        &lt;Aqui_MeuComentario&gt;
            &lt;version&gt;1.0.0&lt;/version&gt;
        &lt;/Aqui_MeuComentario&gt;
    &lt;/modules&gt;
    &lt;global&gt;
    	  &lt;models&gt;
            &lt;meucomentario&gt;
                    &lt;class&gt;Aqui_MeuComentario_Model&lt;/class&gt;
                    &lt;resourceModel&gt;meucomentario_mysql4&lt;/resourceModel&gt;
            &lt;/meucomentario&gt;
            &lt;meucomentario_mysql4&gt;
                &lt;class&gt;Aqui_MeuComentario_Model_Mysql4&lt;/class&gt;
                &lt;entities&gt;
                    &lt;autor&gt;
                        &lt;table&gt;webgp_meucomentario_autor&lt;/table&gt;
                    &lt;/autor&gt;
                    &lt;comentario&gt;
                        &lt;table&gt;webgp_meucomentario_comentario&lt;/table&gt;
                    &lt;/comentario&gt;
                &lt;/entities&gt;
            &lt;/meucomentario_mysql4&gt;
    	  &lt;/models&gt;
        &lt;resources&gt;
            &lt;meucomentario_setup&gt;
                &lt;setup&gt;
                    &lt;module&gt;Aqui_MeuComentario&lt;/module&gt;
                &lt;/setup&gt;
                &lt;connection&gt;
                    &lt;use&gt;core_setup&lt;/use&gt;
                &lt;/connection&gt;
            &lt;/meucomentario_setup&gt;
           &lt;meucomentario_write&gt;
                &lt;connection&gt;
                    &lt;use&gt;core_write&lt;/use&gt;
                &lt;/connection&gt;
            &lt;/meucomentario_write&gt;
            &lt;meucomentarioe_read&gt;
                &lt;connection&gt;
                    &lt;use&gt;core_read&lt;/use&gt;
                &lt;/connection&gt;
            &lt;/meucomentario_read&gt;
        &lt;/resources&gt;
&lt;!--
/**
 * Em blocks, definimos o &quot;padrão&quot;, a &quot;localização&quot; dos blocos em nosso módulo.
 * Por enquanto, nenhum bloco foi ainda definido.
 */
--&gt;
        &lt;blocks&gt;
        &lt;meucomentario&gt;&lt;class&gt;Aqui_MeuComentario_Block&lt;/class&gt;&lt;/meucomentario&gt;
        &lt;/blocks&gt;

&lt;!--
/**
 * Em helpers, definimos o &quot;padrão&quot;, a &quot;localização&quot; dos Helpers em nosso módulo.
 * Por enquanto, apenas a classe Aqui_MeuComentario_Helper_Data foi definida.
 */
--&gt;
        &lt;helpers&gt;
            &lt;meucomentario&gt;
                    &lt;class&gt;Aqui_MeuComentario_Helper&lt;/class&gt;
            &lt;/meucomentario&gt;
        &lt;/helpers&gt;
    &lt;/global&gt;
    &lt;frontend&gt;
&lt;!--
/**
 * Em frontend/routers, definimos a &quot;url&quot; que deverá ser direcionada para nosso módulo.
 * Ou seja, se o usuário acessar http://www.meusite.com.br/meucomentario/index/teste ele irá chamar o método testeAction definido em Aqui_MeuComentario_IndexController.
 */
--&gt;
        &lt;routers&gt;
            &lt;meucomentario&gt;
                &lt;use&gt;standard&lt;/use&gt;
                &lt;args&gt;
                    &lt;module&gt;Aqui_MeuComentario&lt;/module&gt;
                    &lt;frontName&gt;meucomentario&lt;/frontName&gt;
                &lt;/args&gt;
            &lt;/meucomentario&gt;
        &lt;/routers&gt;
    &lt;/frontend&gt;
&lt;/config&gt;

Os items não relacionados com o Models foram comentados no arquivo anterior. Vamos agora explicar o significado de cada configuração em global/models:

&lt;global&gt;
    &lt;!-- ... --&gt;
    &lt;models&gt;
        &lt;meucomentario&gt;
            &lt;class&gt;Aqui_MeuComentario_Model&lt;/class&gt;
            &lt;resourceModel&gt;meucomentario_mysql4&lt;/resourceModel&gt;
        &lt;/meucomentario&gt;
    &lt;/models&gt;
    &lt;!-- ... --&gt;
&lt;/global&gt;

A tag CLASS indica qual o nome BASE (que reflete a localização dos arquivos também) para todos os modelos do nosso módulo. Ou seja, todas as classes que representam os modelos irão iniciar com Aqui_MeuComentario_Model e estarão na pasta Aqui/MeuComentario/Model.
Vamos supor que seja necessário criar o modelo Autor. Iremos criar o arquivo Autor.php em Aqui/MeuComentario/Model, com o nome de classe Aqui_MeuComentario_Model_Autor.
Caso você queira criar algumas pastas, a fim de organizar seu código, não esqueça de incluí-la no nome da classe. Exemplo:

A classe Default.php terá o seguinte nome : Aqui_MeuComentario_Model_Comentario_Default.

&lt;?php
class Aqui_MeuComentario_Model_Comentario_Default extends Mage_Core_Model_Abstract
{
    public function _construct()
    {
        parent::_construct();
        $this-&gt;_init('meucomentario/comentario_default');
    }

A classe do Model deve estender Mage_Core_Model_Abstract e implementar o método construtor, definindo a “representação” deste Model.
Neste caso, quando houver a necessidade de instanciar o modelo Default, deve-se chamar por:

$model = Mage::getModel('meucomentario/comentario_default');

A tag RESOURCEMODEL indica “qual” Resource Model (responsável pela manipulação dos dados no banco) será utilizado.
Logo abaixo, no arquivo config.xml, configuramos o próprio resourceModel:

&lt;global&gt;
    &lt;!-- ... --&gt;
    &lt;models&gt;
        &lt;!-- ... --&gt;
        &lt;meucomentario_mysql4&gt;
            &lt;class&gt;Aqui_MeuComentario_Model_Mysql4&lt;/class&gt;
                &lt;entities&gt;
                    &lt;autor&gt;
                        &lt;table&gt;webgp_meucomentario_autor&lt;/table&gt;
                    &lt;/autor&gt;
                &lt;/entities&gt;
        &lt;/meucomentario_mysql4&gt;
    &lt;/models&gt;
&lt;/global&gt;

Bem, acabamos de definir com a tag CLASS qual o nome/localização BASE dos nossos arquivos de Resource.
Ou seja, dentro da pasta Aqui/MeuComentario/Model devemos criar uma outra pasta chamada Mysql4.

&lt;?php
class Aqui_MeuComentario_Model_Mysql4_Comentario_Default extends Mage_Core_Model_Mysql4_Abstract
{
    public function _construct()
    {
        $this-&gt;_init('meucomentario/comentario_default', 'id');
    }

A classe do ResourceModel deve estender Mage_Core_Model_Mysql4_Abstract e implementar o método construtor, definindo a “representação” deste Model e indicando qual a chave primária da tabela correspondente.
Ou seja, quando for carregado um objeto default, por exemplo Mage::getModel(‘meucomentario/comentario_default’)->load(7), será buscado no banco o registro na tabela com o id = 7.
Perceba que neste construtor, diferente do construtor em Model, ele não chama por parent::_construct();

Na tag ENTITIES fazemos o mapeamento entre Models e tabelas.
No exemplo acima, definimos que a entidade “autor” (‘meucomentario/autor’) representa os dados da tabela webgp_meucomentario_autor.
As tabelas não precisam seguir nenhum padrão de nomenclatura, mas para deixar o código organizado, aconselha-se utilizar um nome significativo.

Quando for preciso executar alguma query customizada, por exemplo, devemos instanciar o ResourceModel e não o Model.

Um ResourceModel deve ser chamado da seguinte forma:

Mage::getResourceModel('meucomentario/comentario_default');

Logo mais daremos alguns exemplos de utilização de um ResourceModel.

Acho que é meio intuitivo que: Quando criamos um Model que possui uma tabela no banco, o ResourceModel correspondente deve ser criado!
Se o seu Model não é persistido no banco, não se aplica essa criação.

&lt;global&gt;
    &lt;!-- ... --&gt;
    &lt;resources&gt;
        &lt;meucomentario_setup&gt;
                &lt;setup&gt;
                    &lt;module&gt;Aqui_MeuComentario&lt;/module&gt;
                &lt;/setup&gt;
                &lt;connection&gt;
                    &lt;use&gt;core_setup&lt;/use&gt;
                &lt;/connection&gt;
        &lt;/meucomentario_setup&gt;
        &lt;meucomentario_write&gt;
            &lt;connection&gt;
                &lt;use&gt;core_write&lt;/use&gt;
            &lt;/connection&gt;
        &lt;/meucomentario_write&gt;
        &lt;meucomentario_read&gt;
            &lt;connection&gt;
                &lt;use&gt;core_read&lt;/use&gt;
            &lt;/connection&gt;
        &lt;/meucomentario_read&gt;
    &lt;/resources&gt;
&lt;/global&gt;

Para finalizar precisamos definir as nossas tabelas e os nossos adapters para leitura e escrita no banco.
A tag MEUCOMENTARIO_SETUP indica que haverá um setup inicial do nosso módulo, geralmente a execução de uma query para criação das tabelas.
Onde então está o nosso script a ser executado?

Crie dentro da pasta Aqui/MeuComentario/sql a pasta meucomentario_set. Dentro desta pasta crie um arquivo chamado mysql4-install-1.0.0.php (mysql4-install-{{versao configurada no inicio do config.xml}}.php).

$installer = $this;
$installer-&gt;startSetup();

$installer-&gt;run(&quot;
-- DROP TABLE IF EXISTS {$this-&gt;getTable('webgp_meucomentario_comentario')};
CREATE TABLE {$this-&gt;getTable('webgp_meucomentario_comentario')} (
  `id` int(11) unsigned NOT NULL auto_increment,
  `comentario` varchar(100) NOT NULL default '',
  `created_time` timestamp NOT NULL default CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    &quot;);

$installer-&gt;endSetup();
?&gt;

Sobre os adapters…
No nosso exemplo estamos utilizando os adapters padrões do Magento, mas caso haja necessidade você pode criar o seu próprio.

ACABOU????

NÃOOOOOOO! Falta falarmos sobre as Collections de dados!

Bem, para carregarmos todos os registros de determinado Model basta chamarmos o seguinte código:

$autores = Mage::getModel('meucomentario/autor')-&gt;getCollection();

Neste exemplo estamos chamamos todos os autores cadastrados no nosso banco (pelo que já vimos até aqui, você pode facilmente criar as classes referente a esse modelo e incluir no setup e no config.xml as configurações necessárias).

Mas como definimos esse “método” getCollection()?
Na verdade precisamos criar uma classe (arquivo Collection.php) na nossa pasta de Resource.

Preste atenção na seguinte regra:

Model: Autor => Aqui/MeuComentario/Model/Autor.php
ResourceModel: Autor => Aqui/MeuComentario/Model/Mysql4/Autor.php
ResourceModel: COLLECTION Autor => Aqui/MeuComentario/Model/Mysql4/Autor/Collection.php

Ou seja, criamos uma pasta com o nome do nosso Model e dentro dela criamos o arquivo Collection.php.


&lt;?php

class Aqui_MeuComentario_Model_Mysql4_Autor_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
    public function _construct()
    {
        parent::_construct();
        $this-&gt;_init('meucomentario/autor');
    }
}

Bem Pessoal, acho que já deu para entender bem como funciona o Models do Magento.
Amanhã vou postar a parte mais interessante e complicada desse tutorial, que explica passo a passo como criar um módulo utilizando modelos EAV.
Em alguns casos específicos a utilização de uma simples tabela FLAT não resolve um problema de modelagem e temos que recorrer ao EAV.

Espero que o tutorial seja util para vocês!
Para dúvidas, utilize a seção de comentários!

referências para esse artigo:
http://alanstorm.com/magento_models_orm
http://www.magentocommerce.com/knowledge-base/entry/magento-for-dev-part-1-introduction-to-magento/

Tags:, , ,

Modelagem de Dados no Magento – Parte I

Já tem um tempo que alguns leitores cobram um tutorial sobre a modelagem de dados no Magento.
O assunto é bem extenso e acho que isso explica a demora na postagem deste tutorial. Rsrsrs. Possivelmente o assunto será divididos em 2 ou 3 partes.

Vou tentar cobrir o assunto com exemplos práticos.

Vamos lá então, começando pelo básico…

O Magento é uma típica aplicação MVC (Model – View – Controller). Ou seja, as regras de negócio, assim como os modelos e a apresentação são codificados separadamente.

Podemos definir um módulo do Magento como sendo um conjunto de arquivos, estruturados de forma padrão, que representam uma funcionalidade. Exemplo: o módulo Catalog é responsável pelas principais funcionalidades relacionadas aos Produtos e às Categorias.

Os módulos básicos do Magento são encontrados em

app/code/core/Mage/

Módulos Básicos Magento

Dentro de cada módulo, temos os controladores separados em uma pasta, assim como os modelos em outra e os arquivos responsáveis pela apresentação dos dados em outra.

Estrutura de Diretórios - Módulo Magento

Estrutura de Diretórios - Módulo Magento

O Magento utiliza o Zend Framework, que é baseado em configurações. Ou seja, quando você desejar adicionar um novo arquivo/classe ao código, não adianta apenas criá-lo. Você deve configurar o Magento para que ele “veja” esse novo arquivo/classe.
Cada módulo do Magento possui um arquivo de configuração chamado config.xml, que fica dentro da pasta etc/. Como visto em tutoriais anteriores, é nesse arquivo que configuramos a localização dos nossos Models.

Os Models do Magento não possuem código para manipular diretamente o Banco de Dados (utiliza-se sistema ORM Object-Relational-Mapping, que permite a manipulação dos dados através de código PHP). A manipulação do banco ocorre em 2 classes associadas (Resources – uma para leitura e outra para escrita).

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

No código acima vemos como um model representando o produto com id = 27 é instanciado e como seus atributos são manipulados.

A classe que representa nosso modelo Product é Mage_Catalog_Model_Product, que pode ser encontrada em app/code/core/Mage/Catalog/Model/Product.php.
Ao analisarmos o seu código, podemos perceber que apesar de chamarmos no nosso exemplo acima os métodos “getPrice” e “setPrice”, eles não existem na nossa classe. Isto acontece pois o ORM implementado no Magento utiliza os métodos _get e _set do PHP.
Ao chamarmos o método $product->getPrice(); estamos dando um “get” no atributo “price”.
Para obtermos todos os dados disponíveis em um Model, basta chamarmos o método getData() ($product->getPrice();). Ele irá retornar um array com todos os atributos do nosso Model.
Para obter o valor de um atributo, além do método getNomeAtributo(), você também pode utilizar o método getData(‘nomeAtributo’);

Uma observação importante é que cada método “set” retorna uma instância do Model. Sendo assim, você irá se deparar várias vezes com o seguinte código:

$model-&gt;setPrice($price)-&gt;setSku('SK83293432');

Ou seja, várias chamadas do método “set” “enfileiradas”.

Como não poderia deixar de ser, a implementação ORM do Magento permite que carregue-se vários objetos através de uma interface de Collections.

$products_collection = Mage::getModel('catalog/product')
-&gt;getCollection()
-&gt;addAttributeToSelect('*')
-&gt;addFieldToFilter('price','5.00');

No código acima carregamos todos os produtos (com todos os seus atributos) que possuem o preço de R$5.00.
A interface Collection permite que os items sejam iterados:

foreach($products_collection as $product)
{
    echo $product-&gt;getName();
}

Bem, ao final desse tutorial vou passar alguns exemplos de queries customizadas, utilizadas para buscar diversos tipos de produtos.

Vamos aproveitar o gancho do exemplo para carregar uma coleção de dados, para falarmos sobre os 2 “tipos” de Models existentes no Magento, que variam de acordo com a sua modelagem no banco:

1) Modelos que representam um único registro de alguma tabela e,
2) Modelos que representam um conjuntos de registros salvos em várias tabelas no banco de dados (modelo EAV – Entidade – Atributo – Valor).

O modelo 1 é obviamente o mais simples para trabalhar, mas os principais modelos do Magento, como Produto, utilizam o modelo EAV. Por isso utilizamos o método addAttributeToSelect(*) na construção da nossa query.

Nas próximas partes do nosso tutorial, vamos criar um pequeno módulo para exemplificarmos como utilizar os Models.

Parte 2

Curso Magento : Nova Turma Online

Olá Pessoal, tudo bem?

Aproveitando o meu retorno ao Blog, vamos abrir uma nova turma para o curso online Introdução ao Magento.
Este curso será disponibilizado gratuitamente pela plataforma de elearning eFront.

O curso terá início no dia 02/09 e a cada 2 dias serão liberadas novas aulas. Além de textos e vídeos serão realizados encontros ao vivo.

Os dados de acesso ao curso serão disponibilizados no Blog.

Magento: Fatal error: Call to a member function setSaveParametersInSession() on a non-object in Container.php on line 59

Quem já criou ou está criando um módulo magento, já deve ter se deparado com esse erro:

Fatal error: Call to a member function setSaveParametersInSession() on a non-object in /magento/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Container.php on line 59.

O que exatamente significa esse erro?

Quando você está trabalhando com um módulo que possui alguma tela no painel administrativo, você acaba criando algumas classes (blocos) que estedem a classe Mage_Adminhtml_Block_Widget_Grid_Container.

Segue abaixo um exemplo:

&lt;?php
class Webgp_FinanciamentoOffline_Block_Adminhtml_Proposta_Attribute extends Mage_Adminhtml_Block_Widget_Grid_Container
{

    public function __construct()
    {
        $this-&gt;_controller = 'adminhtml_proposta_attribute';
        $this-&gt;_headerText = Mage::helper('financiamentooffline')-&gt;__('Formularios');
        $this-&gt;_addButtonLabel = Mage::helper('financiamentooffline')-&gt;__('Adicionar Novo Campo');
        parent::__construct();
    }

}

Essa classe faz parte de um módulo de financiamento que logo será disponibilizado aqui no blog.

Ao adicionarmos esse bloco ao nosso layout, o erro citado irá aparecer na nossa tela!
Podemos confirmar com a inclusão de um log (Mage::log(“teste”)) dentro do construtor (__construct) que nossa classe é chamada, mas por algum motivo ela joga esse erro.

Ao analisarmos a classe /magento/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Container.php, proximo a linha 59, vemos o seguinte código:

    protected function _prepareLayout()
    {
        $this-&gt;setChild( 'grid',
            $this-&gt;getLayout()-&gt;createBlock( $this-&gt;_blockGroup.'/' . $this-&gt;_controller . '_grid',
            $this-&gt;_controller . '.grid')-&gt;setSaveParametersInSession(true) );
        return parent::_prepareLayout();
    }

Bem, para entendermos o código acima, precisamos entender como estruturamos as “páginas” do nosso painel administrativo. Irei explicar de forma simplificada, pois depois iremos disponibilizar um tutorial completo sobre módulos.

Com exceção das páginas de configuração (menu System), quase todas as outras páginas seguem um mesmo padrão:

1) Ao acessar o menu (exemplo: Catalog->Products) você irá visualizar uma página em forma de Grid, com os items cadastrados. No caso de Produtos, você verá um grid de produtos. Além da lista de items, você verá botões para adicionar um novo item e até mesmo algumas opções como “Exclusão, Atualização”.

2) Ao clicar em qualquer item do Grid, você será redirecionado para uma página de edição.

Para visualizarmos a página com o Grid de items, renderizamos um bloco que estende Mage_Adminhtml_Block_Widget_Grid_Container: Webgp_FinanciamentoOffline_Block_Adminhtml_Proposta_Attribute.

Nesta classe definimos onde encontrar nosso arquivo Grid.php, que é o responsável pela renderização dos dados na página. Ou seja:

1) o atributo $this->_blockGroup indica o módulo;
2) o atributo $this->_controller indica o caminho do arquivo (da classe), dentro do módulo.

Sendo assim, no nosso exemplo estamos dizendo que o nosso arquivo Grid.php, responsável pela exibição dos dados, está em financiamentooffline/adminhtml_proposta_attribute (FinanciamentoOffline/Block/Adminhtml/Proposta/Attribute/Grid.php).

Opa, mas meu arquivo não está nessa localização!!!Bingo!!!!

Essa longa explicação é apenas para constatarmos que o problema está em $this->_controller. Não confunda como sendo a localização do seu controller!

Solicite seu módulo

Gostaria de incluir alguma funcionalidade para seus clientes? Facilitar a administração das suas vendas? Integrar sua loja com algum serviço/sistema?

solicitar seu módulo agora

Últimos Artigos

Fique por dentro das nossas novidades!!

Quer ficar sabendo em primeira mão quando novos tutoriais forem postados? Assine nosso RSS.