Desenvolvendo um módulo com CRUD para PrestasShop

Nesse artigo, desenvolveremos um módulo para ser utilizado no painel administrativo da loja. Esse módulo permitirá possuirá as quatro operações CRUD básicas, e mostrará como isso pode ser feito reaproveitando o código do core do PrestaShop. Dessa forma, nossos formulários e a listagem das informações cadastradas possuirão um visual compatível com o padrão da plataforma. Além disso, isso deve fazer com que o módulo seja compatível com futuras versões da loja, até um ponto em que o core seja modificado significativamente.

Os seguintes tópicos serão abordados:

  1. Criação automática do banco de dados;
  2. Reparação (até um certo ponto) automática do banco de dados na reinstalação do módulo;
  3. Listagem dos dados cadastrado no banco em uma grid seguindo o padrão da plataforma;
  4. Criação do formulário de cadastro/edição;
  5. Deleção de registros utilizando ação em massa e individual.

Para os itens 1. e 2., utilizarei a classe CustomObjectModel, que desenvolvi para adicionar alguns recursos à classe ObjectModel do Prestashop.  Para que essa classe funcione bem, os models precisam ser definidos da mesma forma que os models padrão do PrestaShop. Explicarei como fazer essa definição no decorrer do artigo.

Uma vez que os models foram definidos de maneira adequada, os itens 3., 4. e 5. são feitos de maneira praticamente automática pelo próprio PrestaShop. Portanto, nosso trabalho principal será a criação dos models com as definições corretas.

O código-fonte completo do módulo pode ser encontrado neste repositório no GitHub. Para vê-lo em funcionamento, basta extrair todo o código presente no repositório para a pasta /modules/crud/. Recomendo que você siga esse artigo visualizando o código completo do módulo, modificando alguns trechos de seu código para melhor visualização do significado de cada uma das configurações feitas.

Configuração do módulo

Logo no início do arquivo crud.php, temos a linha

A propriedade $models deve conter o nome de todos os models que serão utilizados no módulo. Isso não é um requisito do PrestaShop, mas será utilizado na criação automática do banco de dados. A seguir, temos a definição dasabas que serão criadas no menu do painel administrativo da loja.

Isso fará com que seja criado um menu no painel administrativo da loja com o nome Complete CRUD , com um submenu Custom Operation, conforme a imagem abaixo. Isso também não é feito automaticamente pelo PrestaShop, mas sim com a funçao addTab() que será apresentada a seguir.

Na linha 17, adicionamos o texto a ser exibido no menu (e no submenu) das abas em cada um dos idiomas da loja. Na linha 25, usamos a mesma função addTab() para criar o submenu Custom Operation. A função removeTab(), que será mostrada abaixo, remove os itens que foram criados no menu.

Com essas funções, podemos criar o construtor de nosso módulo, para depois fazer a sua instalação.

Os parâmetros configurados no construtor são auto-explicativos. Sendo assim, seguiremos adiante para a função que instalará o módulo na loja.

Primeiramente, criamos no banco de dados as tabelas referente a cada um dos models utilizados no módulo. Caso as tabelas já existam, a instalação verifica se há alguma coluna faltando nessas tabelas. Isso é util para corrigir eventuais erros provocados por alguma atualização mal-sucedida do módulo. A seguir, instalamos o módulo na loja, e, por fim, criamos o menu do painel referente ao módulo.

Por fim, segue a função uninstall(), que removerá o menu anteriormente criado.

Eu não costumo remover nenhuma informação do banco de dados (muito menos excluir tabelas) na desinstalação do módulo, para evitar perdas acidentais de dados. Ao invés disso, você pode adicionar um botão na tela de configurações do módulo para oferecer essa opção ao usuário, avisando-o das possíveis consequências. Isso não será abordado nesse artigo,

Criação do Model

Na criação do model, muito pouco precisa ser feito. Devemos, apenas, especificar todas as colunas que devem ser criadas no banco de dados, e criar um atributo para cada uma dessas colunas na classe CrudModel. Isso é feito com o código abaixo, que está no arquivo classes/CrudModel.php.

O parâmetro primary refere-se à chave primária da tabela utilizada, e é o campo que será utilizado para identificar unicamente cada registro dessa tabela pelo PrestaShop. As constantes TYPE_INTTYPE_STRING, etc, estão definidas no arquivo ObjectModel.php, dentro da pasta classes da sua loja. validate refere-se ao nome de uma das funções da classe Validate que será utilizada para validar os dados antes de serem salvos no banco de dados. Por fim, o parâmetro db_type não é padrão do PrestaShop, mas acrescentei ele para ser utilizado pela CrudCustomObjectModel, na criação automática do banco de dados.

Criação dos controllers

controller também é bastante simples, assim como foi o model. Devemos apenas configurar quais colunas desejamos que sejam apresentadas na tabela que apresentará os dados. A tabela em si será gerada automaticamente pelo PrestaShop. Do mesmo modo, especificaremos quais os campos presentes nos formulários de cadastro/edição, e o formulário será gerado automaticamente. Não escreveremos uma linha da camada de apresentação (view), nem da parte pesada do controller. O PrestaShop se encarregará de chamar os métodos adequados da classe CrudModel quando o usuário utilizar algum filtro na tabela, ou quando ele editar algum dos registros dela, ou até mesmo quando excluir uma de suas linhas do banco de dados.

As imagens abaixo mostram as telas de listagem e de cadastro que são geradas automaticamente pelo prestashop, no tema padrão do painel administrativo. Para que elas sejam geradas, devemos criar o arquivo controllers/admin/AdminCrudModels.php. Todos os parâmetros necessários para a geração das telas serão configurados no construtor da classe. Assim, comecemos a declaração básica do controller.

Na linha 7, dizemos que esse controller requer que o bootstrap seja carregado para que as views sejam exibidas corretamente. A seguir, especificamos os parâmetros do model cujos dados desejamos manipularPor fim, devemos especificar quais campos desejamos que sejam apresentados na listagem dos dados. Isso é feito através do código abaixo.

As chaves do array acima correspondem aos nomes dos campos que foram configurados no nosso model. Os parâmetros utilizados no código acima seguem a documentação da classe HelperList, então não farei maiores comentários. Na linha 14, fazemos com que a coluna active ganhe a funcionalidade de modificar o status do objeto apenas clicando nos ícones “v” ou “x“. Além disso, a tabela ganha automaticamente as ações em massa “Ativar Aelecionados” e “Desativar Selecionados”.

Para a ação em massa de deleção, devemos adicionar o código abaixo.

Para finalizar nossa listagem, vamos adicionar os botões de ação individual, como mostra a figura abaixo.

Isso é feito facilmente com o código abaixo.

Quando o usuário clicar em Editar, ele verá o formulário abaixo, já preenchido com os dados do objeto escolhido.

Ele pode, ainda, adicionar uma nova linha à tabela com o botão de Adicionar Novo, na parte de cima da tabela. Ele verá o mesmo formulário, mas sem nenhum dado preenchido.

btn_new

Para que esse formulário seja gerado automaticamente, devemos especificar todos os campos que serão exibidos, através do código abaixo.

Essa especificação é feita conforme a documentação da classe HelperForm.

Feito isso, nosso CRUD está finalizado, e totalmente funcional. Note que, como as telas são geradas automaticamente, alguns aspectos da interface não podem ser modificados tão facilmente. Para fazer qualquer modificação não coberta nesse artigo, recomendo uma análise da classe AdminController, bem como as classes HelperFormHelperList, que são as classes base para a geração das telas.

17 pensamentos em “Desenvolvendo um módulo com CRUD para PrestasShop”

  1. Uau, lindo artigo amigo, eu encontrei seu poste em inglês a procura de uma soluçao para o meu problema, depois de ler boa parte do conteudo em ingles me dei conta que você é brasileiro (vi as imagens com palavras em portugues e o seu “sorry”) rss, eu tentei acompanhar seu método, coloquei o CustomObjectModel, mas nao estou coneguindo adicionar/editar, consigo deletar, e nem visualizar o conteudo inteiro (a tabela), você poderia me ajudar? na verdade eu queria exibir essa lista e opçoes de visualizar os detalhes e modificar dentro de uma template espefica no meu modulo, teria como você me da uma luz?

    Obrigado!

    1. Olá, obrigado pelo comentário!

      O formulário de cadastro/edição é ao menos exibido? Você sobrescreveu alguma das funções das classes AdminController ou ModuleAdminController?

      Para visualizar o conteúdo de um elemento da sua tabela, você deve adicionar a ação “view” à propriedade “actions” do seu controlador. Algo como $this->actions = ['view', 'edit', 'delete'];.
      O template padrão utilizado pelo PrestaShop fica em “views/templates/admin/nome_do_seu_controlador_na_notacao_underscore/helpers/view/view.tpl”, dentro da pasta do seu módulo. Para passar parâmetros para esse template, a única forma que encontrei foi sobrescrever a função initContent() do controlador. A função ficará algo como

      public function initContent()
      {
      parent::initcontent();
      if ($this->display === 'view') {
      $this->loadObject();
      $this->context->smarty->assign(array(
      'objeto' => $this->object,
      'outroobjeto' => $foo
      ));
      }
      }

      1. Ja consegui resolver a questao do editar, como eu poderia apresentar os detalhes desse elemento? tem como eu enviar esse formulario a um tpl dentro de uma variavel por exemplo? você sabe o procedimento, eu ja tentei usando $this->context-smarty->setTemplate(); // sem sucesso.
        vou tentar com o createTemplate, especificando uma template dentro do /admin/helpers/list/list_request.tpl e depois incluir essa “list_request.tpl” dentro de uma categoria do menu que criei na interface do meu module.

        você ja tentou esse método?

        Obrigado pela reposta!

        1. Bem, se você deseja exibir o formulário em um arquivo de template personalizado, acredito que a melhor solução seja usar diretamente a classe HelperForm do PrestaShop. Internamente, os controllers do PrestaShop utilizam este helper, então você pode gerar o formulário com de uma forma bem parecida à que eu expliquei no artigo.

          Para renderizar o template personalizado, você pode utilizar o método setTemplate() dentro da função initContent() do controlador, após a chamada do método parent::initContent(). Algo como

          parent::initContent();
          $this->smarty->assign([‘form’ => $this->renderCustomForm()]);
          $this->setTemplate(‘meutemplate.tpl’);

          Você deve salvar o template em views/templates/admin/nome_do_seu_controller/meutemplate.tpl, dentro da pasta do seu módulo.

          Utilizo este método sempre que preciso de telas mais complexas do que aquelas que o PrestaShop gera automaticamente.

          1. Pois e, eu criei um meno na pagina de configuração do meu modulo, em que navegando pelo menu, voce tem a parte de configuração, a parte de documentação e queto incluir agora a lista dos pedidos feitos no front atraves de um formulário, quero exibir esses registros nessa em uma aba do menu, para isso eu teria que direcionar a lista dentro da template, irei perder a estrutura fazendo isso?

          2. Na página de configuração, creio que não seja possível utilizar inteiramente a estrutura que eu descrevi no artigo. Essa estrutura depende de a sua página estar em um controlador próprio, enquanto que a página de configurações dos módulos reside no controlador AdminModulesController, do PrestaShop. Para utilizar formulários na página de configuração do módulo, eu recomendo que você faça diretamente através da classe HelperForm mesmo, ou até mesmo que você gere o formulário manualmente em um arquivo de template, dependendo do grau de personalização que você quiser atingir.

      2. em relaçao a visualizaçao do elemento, eu preciso criar essa visualizaçao na template, ou ele cria uma template automaticamente? apenas carregando o objeto e dando um return em renderView() por exemplo? eu estou conseguindo acessar a tpl, faltava eu adicionar a pasta do controlador, mas nao aparece nada no BO, por exemplo eu tentei abrir os dados da minha tabela em uma variavel dentro da template, if ($this->display === ‘view’ ) { return $this->displayRequestList() }

        public function displayRequestList()
        {
        $tpl = $this->createTemplate(‘helpers/list/request_list.tpl’);
        $tpl->assign(array(
        ‘list’ => $this->fields_list
        ));

        return $tpl->fetch();

        }

        o conteudo nao aparece, ta em branco, so fica o titulo da sessao ‘view’ os menus, mas tem nada de conteudo! fiz um {$list|var_dump} e tudo que fala que é uma ‘Array’, creio que eu tenha que renderizar esse $this->fields_list no renderForm() e depois chamar o renderForm() no lugar dele, pode me da uma luz??

        desde ja obrigado!

        1. Sobre a renderização, o PrestaShop procurará o arquivo “views/templates/admin/nome_do_seu_controlador_na_notacao_underscore/helpers/view/view.tpl”, dentro da pasta do seu módulo. Assim, você precisará criar este arquivo para que a view seja renderizada.

          Veja que $this->fields_list é um vetor que você deve declarar dentro do seu controlador. Esse vetor contém a declaração dos campos que serão apresentados na listagem do PrestaShop. Contudo, se você não seguir fielmente à estrutura que eu descrevi neste artigo, o PrestaShop não renderizará a grid automaticamente. Se você quiser gerar uma grid fora da estrutura que eu passei neste artigo, você pode utilizar a classe HelperList do PrestaShop.
          Isso funciona bem se a sua grid for simples (sem filtros e ordenação), mas dá uma bela dor de cabeça se você quiser utilizar a grid completa, com filtros, ordenação, paginação, caches, etc. Se você precisar de uma grid completa,recomendo que você crie um controlador próprio para essa listagem, ao invés de utilizar a página de configurações do PrestaShop.

          1. Obrigado pela resposta amigo, eu entendi o que você quis dizer, eu fiz alguns teste e vi que eu precisava criar a tpl para ser exibida, eu sei que me daria uma dor de cabeça bem grande criar uma template customizada para o CRUD no qual você apresentou aqui, que atualmente em um teste esta funcionando perfeitamente, mas o problema que encontro no meu modulo principal é:

            ele tem um menu (Documents, Settings, Form, List Quote), para cada um desses elementos eu chamo uma template que esta hidden na pagina de configuraçao do modulo, entao eu peguei o renderForm() do modulo coloquei em uma variavel e enviar para uma tpl, com isso consigo abrir a parte de configuraçao do modulo dentro do elemento “Settings” agora estou querendo fazer o mesmo com a lista que estou pegando do database e colocar no “List Quote”, eu terei que recriar tudo? ou posso enviar a variavel para a list.tpl que o prestashop vai se ocupar de exibila? porque assim: $this->fields_list esta me retorando o vetor que atualmente renderiza essa lista no controlador:

            $this->fields_list = array (
            ‘id_quote’ => array(
            ‘title’ => $this->l(‘ID’),
            ‘type’ => ‘int’,
            ‘align’ => ‘center’,
            ),
            ‘reference’ => array(
            ‘title’ => $this->l(‘Reference’),
            ‘type’ => ‘text’,
            ‘align’ => ‘center’,
            ),
            ‘name’ => array( ….
            so nao sei que irei conseguir exibir esse vetor da mesma forma que o prestashop esta me renderizando, vou da uma olhadinha nas tpls do presta e verifica como os blocks estao disseminando o vetor dentro da template.

            Se você tiver uma luz que poderia me ajudar nisso, eu ficaria muito grato!

          2. Olha, dê uma olhada na função AdminController::renderList(). É essa a função que o PrestaShop utiliza para renderizar a grid. Como eu disse, se for uma grid simples, sem paginação, você deve conseguir fazer funcionar sem muita dificuldades. Caso queira a listagem completa, só fuçando mais a fundo no código do PrestaShop. Quando precisei disso, não achei nenhuma forma muito simples de fazer.

          3. Eu finalmente consegui depois de alguns teste!!
            irei compartilhar com você o método!

            public function renderList()
            {

            $this->fields_list = array (
            ‘id_devis_request’ => array(
            ‘title’ => $this->l(‘ID’),
            ‘type’ => ‘int’,
            ‘align’ => ‘center’,
            ),
            ‘reference’ => array(
            ‘title’ => $this->l(‘Reference’),
            ‘type’ => ‘text’,
            ‘align’ => ‘center’,
            ),
            ‘name’ => array(
            ‘title’ => $this->l(‘Product’),
            ‘type’ => ‘int’,
            ‘align’ => ‘center’,
            ),

            }

            public renderView()
            {

            // Set your tpl vars
            $this->tpl_view_vars = array(
            ‘list’ => $this->renderList(),
            );

            return parent::renderView();

            }

            o mesmo procedimento vale para o renderForm()
            faltava eu chamar o renderView() para inserir a variavel e retorna a funçao.

            Obrigado, você com o seu poste abrir a minha mente para muitas coisas!

            Ahhh, dentro da tpl eu fiz um extends e chamei a variavel dentro de um block personalizado, eu ja tinha feito um procedimento parecido no symfony, ainda bem que deu certo!! vou poder aproveitar o final de semana agora rss… ^_^

          4. Hehe, isso aí, fico feliz que tenha dado certo. Acho a documentação do PrestaShop bastante ruim, para descobrir essas coisas é só procurando no core mesmo…
            Obrigado pela contribuição!

  2. Eu poderia simplesmente chamar o meu modelo dentro da pagina do meu módulo, estanciar e chamar a função que pega esses dados e criar uma lista na munheca, mas quero fazer da melhor forma possivel, seguindo os padrões de código e de forma bem limpa e visivel! ??

  3. Olha eu de novo aqui, primeiramente bom dia!

    Eu gostaria de saber como eu poderia retirar o botao “+” ADD, que por padrao se encontra ao lado do botao REFRESH!! de qualquer forma irei da uma olhadinha na template na qual eu estou extendendo.

    tenha um otimo final de semana!
    Ahhh, so para compartilhar, eu fui mais além na questao de aprensetar os layouts, e agora eu consigo colocar no conteudo no meu controlador a template de configuraçao do meu modulo!! ficou bem interessante dessa forma, ja que eu usava antes um “redirect” para a pagina de configuraçao do modulo.

    1. Olá, bom dia.

      Bem, quando eu preciso remover esse botão, eu faço via javascript mesmo. Não é muito elegante, mas não encontrei nenhuma outra forma de fazê-lo…

      Isso é bom, ajuda a melhorar a qualidade dos módulos nacionais :)

  4. Bom dia Amauri,
    estou aqui com intuito de sanar uma duvida que encontrei, para que eu possa usufruir da possibilidade de modificar/visualizar os elementos de uma tabela, eu tenho que chamar o meu model dentro do controlador? por exemplo: require_once ‘MyClassModel.php’;

    ou isso nao é necessario?
    senao:
    como eu faço para carregar o objecto dentro do controlador
    eu uso o $this->loadObject(true);
    mas se a minha classeModel, nao for chamada dentro do controlador, ele nao carrega o objeto, o mais engraçado é que para visualizar a lista eu nao preciso carrega a classe dentro do controlador!!

    por isso surgiu a duvida, caso você ache pertinente você pode me adicionar no chat do gmail: [email protected]

    ficaria mais facil para gente trocar informaçoes, creio eu!

    PS: Obrigado por compartilhar um pouco do seu conhecimento, principalmente dentro do E-CMS prestashop, é bem dificil de encontrar bons artigos referente ao mesmo.

    Atenciosamente,
    Bruno

    1. Bom dia, Bruno.

      Eu sempre crio uma função loadClasses no arquivo principal do meu módulo, que carrega todas as suas classes. Uma espécie de bootstrap. Essa função é chamada no construtor do módulo, então sempre que uma dessas classes for necessária, elas já estarão carregadas.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *