Tutorial ZF2 + Doctrine + ZfcUser + Los

Olá pessoal!

Nesta sequencia de posts, vou explicar passo a passo como criar uma aplicação completa usando ZF2, Doctrine, ZfcUser e alguns dos meus módulos.

Vamos ter uma tela de login,  layout usando LosUi (bootstrap, jQuery), menu e breadcrumbs usando Navigation, cadastro simples de cliente e usuário, os dados sendo salvos em MySQL usando Doctrine 2.

É importante ressaltar que não existe uma única forma de usar o Doctrine e ZF2. Este tutorial não se propõe a ditar como você deve fazer seu projeto, mas ajudar as pessoas a iniciar o desenvolvimento usando um caso de uso pronto, funcionando e que uso em quase todos os meus projetos. Também serve para quem já usa as ferramentas, mas que está sempre em busca de melhorar e ver como outras pessoas as usam, pois as vezes uma maneira se encaixa melhor num projeto que outras.

Parte 1: Instalação e configuração

Aplicação base pronta

Todo projeto inicio da mesma maneira, na maioria uso os mesmos módulos, mesmas configurações, então até montar a mesma estrutura levaria um bom tempo.

Por isso, estou disponibilizando um repositório no github com esta base já pronta:

https://github.com/Lansoweb/LosSkeletonApplication/tree/demo

Apesar de já estar pronta, recomendo fortemente, principalmente para quem não domina o ZF2 e Doctrine, que acompanhe este tutorial, pois ao longo dele vou dar explicações e dicas que vão melhorar o entendimento das ferramentas usadas.

O repositório acima contem uma aplicação de demonstração, com cadastro de cliente (nome e crédito) e usuário, apenas para mostrar a ligação entre os módulos envolvidos e as facilidades de se usar o LosBase e LosUi.

Para a LosSkeletonApplication que uso para iniciar um projeto, usem o link abaixo, que é parecido com o de demonstração, mas sem o cadastro de cliente.

https://github.com/Lansoweb/LosSkeletonApplication

Seguindo este tutorial, vamos chegar até a LosSkeletonApplication complete e nas últimas partes até a parte de demonstração, com explicações passo-a-passo.

Instalando do zero

Acredito que a melhor maneira de aprender alguma coisa é fazendo, então vamos à obra. Nesta primeira parte vamos começar fazendo a instalação do que precisamos.

Vamos começar pelo composer:

curl -s https://getcomposer.org/installer | php --

Ou se você não possui o curl instalado:

php -r "readfile('https://getcomposer.org/installer');" | php

E a Zend Framework 2:

$ php composer.phar create-project -sdev --repository-url="https://packages.zendframework.com" zendframework/skeleton-application caminho/projeto

Installing zendframework/skeleton-application (dev-master 45ec2f82ab6aadb7d7c8f92773050ce90bc388e1)
  - Installing zendframework/skeleton-application (dev-master master)
    Cloning master

Created project in zf
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing zendframework/zendxml (1.0.0)
    Loading from cache

  - Installing zendframework/zendframework (2.3.4)
    Loading from cache

Writing lock file
Generating autoload files
Do you want to remove the existing VCS (.git, .svn..) history? [Y,n]? Y

Vamos abrir o composer.json para adicionar os módulos necessários.

{
  "name" : "los/tutorial",
  "description" : "Tutorial ZF2, Doctrine e pacotes Los",
  "require" : {
    "php" : ">=5.4.0",
    "zendframework/zendframework" : "~2.3",
    "doctrine/doctrine-orm-module" : "~0.8",
    "los/losbase" : "~2.5",
    "los/losui" : "~1.0",
    "zf-commons/zfc-user-doctrine-orm" : "~1.0",
    "zf-commons/zfc-rbac" : "~2.4"
  },
  "require-dev" : {
    "zendframework/zend-developer-tools" : "=0.0.2",
    "los/loslog" : "~1.0"
  },
  "keywords" : [ "framework", "zf2" ],
  "license" : "BSD-3-Clause",
  "homepage" : "http://framework.zend.com/"
}

Neste modelo não usei versões fixas, por exemplo 2.3.4 para a zf2, 1.0.12 para LosUi, mas deixei o último número da versão para ser atualizado. A maioria dos módulos (inclusive os meus) segue a Semver, então quando se altera apenas o último da versão, seu código não deverá ter problemas. Sempre uso desta maneira durante o desenvolvimento, mas em produção é importante termos certeza que nosso sistema funciona sem problemas. Então primeiro atualize a versão de um módulo num ambiente de teste, valide o sistema e depois sim atualize em produção.

O DoctrineOrmModule é um módulo do Doctrine2 para ZF2 que já instala também todas as dependências do doctrine, por isso não precisamos especificar cada uma, o que mantém nosso composer limpo.

Costumo usar o ZfcUser para gerenciar usuários e acesso ao sistema e o ZfcRbac para gerenciamento de permissões e papéis. As vezes acho o ZfcUser pesado para algumas aplicações que faço, pois ele vem com opção de se registrar e vários outros recursos, quando preciso apenas de um login e checar se a pessoa está logada ou não. Então tenho um módulo simples de usuário que em breve vou disponibilizar para a comunidade.

O LosUi oferece facilidades para a parte visual do projeto, já trazendo as últimas versões das principais bibliotecas javascript e de estilo que costumo usar, como Bootstrap, Jquery e FontAwesome.

Já o LosBase, nos oferece várias coisas prontas, como um Controller Crud, Entidade Doctrine e Serviço, datas doctrine convertidas de UTC para BRST e vice-versa, entre vários outros recursos.

Vamos agora instalar/atualizar os módulos:

$ php composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing doctrine/lexer (v1.0.1)
    Loading from cache

  - Installing doctrine/annotations (v1.2.3)
    Loading from cache

  - Installing doctrine/collections (v1.2)
    Loading from cache

  - Installing doctrine/cache (v1.4.0)
    Loading from cache

  - Installing doctrine/inflector (v1.0.1)
    Loading from cache

  - Installing doctrine/common (v2.4.2)
    Loading from cache

  - Installing doctrine/dbal (v2.5.1)
    Loading from cache

  - Installing symfony/console (v2.6.3)
    Loading from cache

  - Installing doctrine/orm (v2.4.7)
    Loading from cache

  - Installing doctrine/doctrine-module (0.8.0)
    Loading from cache

  - Installing doctrine/doctrine-orm-module (0.8.0)
    Loading from cache

  - Installing zf-commons/zfc-base (v0.1.2)
    Loading from cache

  - Installing zf-commons/zfc-user (1.2.2)
    Loading from cache

  - Installing zf-commons/zfc-user-doctrine-orm (1.0.0)
    Loading from cache

  - Installing zendframework/zend-developer-tools (0.0.2)
    Loading from cache

  - Installing los/losbase (2.5.1)
    Downloading: 100%         

  - Installing symfony/process (v2.6.3)
    Loading from cache

  - Installing kriswallsmith/assetic (v1.1.3)
    Loading from cache

  - Installing rwoverdijk/assetmanager (1.4.1)
    Loading from cache

  - Installing los/losui (1.0.12)
    Downloading: 100%         

  - Installing zfr/rbac (1.2.0)
    Loading from cache

  - Installing zf-commons/zfc-rbac (2.4.2)
    Downloading: 100%         

  - Installing los/loslog (1.0.12)
    Downloading: 100%         

Writing lock file
Generating autoload files

Se o composer reclamar que está desatualizado (normalmente acontece quando se instala a zf2), basta atualiza-lo:

$ php composer.phar self-update
Updating to version be23cbfa6e9aa7acd59653e0090b5db045a4c572.
    Downloading: 100%         
Use composer self-update --rollback to return to version 7adc41d02c3536b3e19a6b906cf0c4cf6d3beb70

Configurando

Vamos adicionar os módulos à nossa aplicação, editando o config/application.config.php (retirei os comentários para ficar mais claro):

<?php
return array(
    'modules' => array(
        'DoctrineModule','DoctrineORMModule',
        'ZfcBase',
        'ZfcUser','ZfcRbac',
        'ZfcUserDoctrineORM',
        'AssetManager',
        'LosBase','LosUi','LosLog',
        'Application',
    ),

    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor',
        ),

        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
    ),
);

Vamos adicionar os arquivos de configuração dos módulos e configurar nossos global.php e local.php. Para também ficar claro, removi a maioria dos comentários.

config/autoload/zfcuser.global.php

<?php
$settings = array(
    //'zend_db_adapter' => 'Zend\Db\Adapter\Adapter',
    'user_entity_class' => 'Usuario\Entity\Usuario',
    'enable_default_entities' => false,
    'enable_registration' => false,
    'enable_username' => false,
    'auth_adapters' => array( 100 => 'ZfcUser\Authentication\Adapter\Db' ),
    'enable_display_name' => true,
    'auth_identity_fields' => array( 'email' ),
    'login_form_timeout' => 300,
    'user_form_timeout' => 300,
    'use_redirect_parameter_if_present' => true,
    //'user_login_widget_view_template' => 'zfc-user/user/login.phtml',
    'login_redirect_route' => 'dashboard',
    'logout_redirect_route' => 'home',
    //'password_cost' => 14,
    'enable_user_state' => false,
    'table_name' => 'user',
);

return array(
    'zfcuser' => $settings,
    'service_manager' => array(
        'aliases' => array(
            'zfcuser_zend_db_adapter' => (isset($settings['zend_db_adapter'])) ? $settings['zend_db_adapter']: 'Zend\Db\Adapter\Adapter',
        ),
    ),
);

config/autoload/zfcrbac.global.php

<?php
return [
    'zfc_rbac' => [
        // 'identity_provider' => 'ZfcRbac\Identity\AuthenticationIdentityProvider',
        'guest_role' => 'visitante',
        'guards' => [
            'ZfcRbac\Guard\RouteGuard' => [
                'home' => ['usuario'],
                'usuario/*' => ['admin'],
            ],
            'ZfcRbac\Guard\ControllerGuard' => [
                [
                    'controller' => 'Usuario\Controller\Usuario',
                    'roles'      => ['admin']
                ],
            ]
        ],

        // 'protection_policy' => \ZfcRbac\Guard\GuardInterface::POLICY_ALLOW,
        'role_provider' => [
            'ZfcRbac\Role\InMemoryRoleProvider' => [
                'admin' => [
                    'children'    => ['usuario'],
                    'permissions' => ['admin','deleteOthers','autorizar']
                ],
                'usuario' => [
                    'children'    => ['visitante'],
                    'permissions' => ['usuario','delete']
                ],
                'visitante'
            ]
        ],

        'unauthorized_strategy' => [
            'template' => 'error/403'
        ],

        'redirect_strategy' => [
            'redirect_when_connected' => true,
            'redirect_to_route_connected' => 'error/403',
            'redirect_to_route_disconnected' => 'zfcuser/login',
            'append_previous_uri' => false,
            'previous_uri_query_key' => 'redirect'
        ],
        // 'guard_manager'               => [],
        // 'role_provider_manager'       => []
    ]
];

config/autoload/global.php: ATENÇÃO! Evite colocar senhas no global.php, pois normalmente este arquivo vai estar no controle de versão e pode ser visto por outras pessoas. Senhas devem ficar no local.php, como mostrado abaixo.

<?php
return array(
    'service_manager' => array(
        'invokables' => array(
            'Zend\Session\SessionManager' => 'Zend\Session\SessionManager',
        ),
        'aliases' => [
            'Zend\Authentication\AuthenticationService' => 'zfcuser_auth_service'
        ]
    ),
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
                'params' => array(
                    'host' => 'localhost',
                    'port' => '3306',
                    'user' => 'leandro',
                    'dbname' => 'tutorial',
                    'charset' => 'UTF8',
                    'driverOptions' => array(
                        'charset' => 'UTF8'
                    )
                )
            )
        ),
        'entitymanager' => array(
            'orm_default' => array(
                'connection' => 'orm_default',
                'configuration' => 'orm_default'
            )
        ),
        'configuration' => array(
            'orm_default' => array(
                'query_cache' => 'apc',
                'result_cache' => 'apc',
                'metadata_cache' => 'apc'
            )
        )
    ),
    'view_manager' => array(
        'template_map' => array(
            'error/403' => __DIR__ . '/../../module/Application/view/error/403.phtml',
        ),
    ),
);

config/autoload/local.php: Sempre configure o cache do doctrine para array durante o desenvolvimento, senão pode acontecer de você não ver as alterações no banco de dados.

<?php
return array(
    'doctrine' => array(
        'configuration' => array(
            'orm_default' => array(
                'query_cache' => 'array',
                'result_cache' => 'array',
                'metadata_cache' => 'array'
            )
        ),
        'connection' => array(
            'orm_default' => array(
                'params' => array(
                    'password' => 'minhasenha'
                )
            )
        )
    )
);

Vamos criar os diretórios de cache e logs:

mkdir -p data/cache data/logs data/DoctrineORMModule
chmod -R 777 data/cache data/logs data/DoctrineORMModule

Pronto. Já podemos abrir o projeto no navegador e deve aparecer a página padrão do ZF2:

ZF2 InicialNo próximo post, vamos ver toda a parte do Doctrine, criar nossas tabelas, inserir usuários, etc.

Referência:

Parte 1: Instalação e configuração

Parte 2: Banco de dados (Doctrine)

Parte 3: Visual e Layout (LosUi)

Parte 4: Autenticação e Autorização (ZfcUser e ZfcRbac)

Parte 5: Usando o sistema

 

Até a próxima!

Leandro Silva

15 comentários sobre “Tutorial ZF2 + Doctrine + ZfcUser + Los

  1. Faço isso:
    php composer.phar create-project -sdev –repository-url=”https://packages.zendframework.com” zendframework/skeleton-application E:\aulaZF2

    e dá isso:

    [Seld\JsonLint\ParsingException]
    “https://packages.zendframework.com/packages.json” does not contain valid J
    SON
    Parse error on line 1:
    g{ “packages”: {
    ^
    Expected one of: ‘STRING’, ‘NUMBER’, ‘NULL’, ‘TRUE’, ‘FALSE’, ‘{‘, ‘[‘

    create-project [-s|–stability=”…”] [–prefer-source] [–prefer-dist] [–repos
    itory-url=”…”] [–dev] [–no-dev] [–no-plugins] [–no-custom-installers] [–n
    o-scripts] [–no-progress] [–keep-vcs] [–no-install] [–ignore-platform-reqs]
    [package] [directory] [version]

    • Olá Luciano!
      Acabei de testar e funcionou, pode ter sido algo temporário. Mas se você retirar o argumento –repository-url.. também funciona. Experimente com o comando abaixo e veja se funciona:
      php composer.phar create-project -sdev zendframework/skeleton-application E:\aulaZF2

  2. ola Leandro
    sou um visitante do seu blog
    já trabalho com o zend 1, e estou tentando migrar para o ZF2, mas…. não esta sendo facil
    ja segui uns tutoriais do site da zend e ate rodou mas… não intendi muito as diferenças
    comecei as ler seus posts e estou entendendo
    não conhecia o doctrine(paree bruxaria)
    só que…. não consigo rodar o tutorial que estou seguindo
    ja fiz umas 2 vezes
    acho que pode ser problema em alguma dependência que não esta funcionando
    ou .htacess
    não abre no navegador
    erro 500
    mas… os comando funcionam perfeitamente
    cria o crud, tabela, altera tipo de dados
    teria uma luz…..?

    • Leandro Silva disse:

      Bom dia Leandro.
      Sempre que ocorrer um erro 500, verifique no log do php ou do apache (error.log) qual é o erro causado pelo PHP. Erro 500 pode ser um monte de coisa diferente, desde falta de arquivo, permissão de diretório, até erro em arquivo PHP.
      Abraços, Leandro Silva

  3. Weliton disse:

    Leandro tentei rodar a versão completa(LosSkeletonApplication), porém ao tentar criar as tabelas no banco com o doctrine dá o seguinte erro:

    [Doctrine\ORM\Mapping\MappingException]
    The target-entity Cliente\Entity\Cliente cannot be found in ‘Usuario\Entity
    \Usuario#cliente’.

    Pode me dar um help?

    • Leandro Silva disse:

      Bom dia Weliton!
      Faltou o módulo cliente no config/application.conf.php. Acrescente ou atualize a demo pois já acrescentei no repositório.
      Obrigado por apontar o erro!
      Abraços, Leandro

  4. gustavo disse:

    Olá! gostei muito do seu tutorial, porém, estou tendo um problema que acredito que esteja envolvendo o global.php e o trecho do código

    ‘service_manager’ => array(
    ‘invokables’ => array(
    ‘Zend\Session\SessionManager’ => ‘Zend\Session\SessionManager’,
    ),
    ‘aliases’ => [
    ‘Zend\Authentication\AuthenticationService’ => ‘zfcuser_auth_service’
    ]
    ),
    pois a aplicação me retorna sempre : You are not authorized to access this resource.

    o que poderia ser?

    Obrigado e até mais.

    • Olá Gustavo também estou tentando rodar esse tutorial.

      Ainda não finalizei algumas partes mas acho que o seu problema seria no arquivo config/autoload/zfcrbac.global.php

      Altera admin por visitante.

    • Leandro Silva disse:

      Bom dia Gustavo!
      Como o Leandro respondeu, se não quiser ver os error de permissão por enquanto, altere os papéis nas rotas do zfcrbac.global.php para visitante ou *, por exemplo na linha 9:
      ‘cliente/*’ => [‘*’],

      Obrigado aos 2!
      Leandro

      • Obrigado pela dica do de consultar o log do php. encontrei o problema.

        eu nao sabia que poderia consultar la.

        tem como habilitar algum modo de debug no zend para ele mostrar os erros no navegador? o meu quando da erro fica tela branca.

        desde ja obrigado e parabens pelo seu material.

        • Você pode ligar diretamente no php.ini (display_errors = true) ou acrescentando as 2 linhas abaixo no início do index.php do seu projeto:
          error_reporting(E_ALL);
          ini_set(‘display_errors’, true);

          Abraços e obrigado!
          Leandro Silva

  5. francisco disse:

    ola Leandro, estou tentando rodar este tutorial agora em 2016 só que dar um error de instancia de navegação pois o caminho Zend\View\Helper\Navigation\AbstractHelper não existe no zend2.5 .como redeclaro esta caminho já que o composer não instala zend inferior.

    • Olá Francisco! Alguns dos módulos ainda não são compatíveis com as versões mais recentes da framework (em especial maiores que a 2.6 que já são com compatibilidade com a ZF3). O ideal é fixar no composer.json o zendframework 2.4 por enquanto mesmo.

Deixe uma resposta

This site uses Akismet to reduce spam. Learn how your comment data is processed.