DEV Community

Cover image for Como utilizar generators no Phoenix
Iago Effting
Iago Effting

Posted on • Edited on

Como utilizar generators no Phoenix

Quando acordamos de manhã cedo e pensamos em fazer nossa mais nova ideia milionária, sempre vem na cabeça (pelo menos na minha) em colocar esse produto em uma aplicação web, para facilitar o acesso.

No mundo Elixir uma das melhores ferramentas para construir isso é o Phoenix Framework. Ele possui todas as funcionalidades que precisamos e muito mais.

Convenhamos que ideias vem o tempo todo e queremos faze-las e valida-las o mais rápido possível antes que percamos o foco (ou a vontade). Mas, como podemos acelerar o processo de criação?

Phoenix possui uma gama de generators que podem te ajudam a gerar código de problemas comuns, que basicamente se repetem ao longo da história e é sobre os generators que eu falarei aqui.

O que veremos?

  • Gerando uma nova aplicação web com mix phx.new ...
  • Gerando contexto, controller, views e html com mix phx.gen.html
  • Gerando autenticação com mix phx.gen.auth
  • Protegendo rotas com autenticação

Para iniciar, essa é a ideia milionária:

Desafio de hoje: Na nossa mais nova ideia, precisamos criar um painel administrativo onde possamos cadastrar filmes. Os campos necessários são title e description. Nossos usuários precisarão estar logado para interagir com o sistema.

Requisitos tecnológicos

Criando nossa aplicação

Nossa aplicação terá a necessidade de um banco de dados (utilizaremos o postgres), também o suporte a HTML.

O primeiro gerador que utilizaremos dará conta dissoPodemos passar comandos simples para o cli do phoenix configurar isso para nós:

mix phx.new my_movies --database postgres --no-live

...
* creating my_movies/lib/my_movies/mailer.ex
* creating my_movies/lib/my_movies_web/gettext.ex
* creating my_movies/priv/gettext/en/LC_MESSAGES/errors.po
* creating my_movies/priv/gettext/errors.pot
* creating my_movies/assets/css/phoenix.css
* creating my_movies/assets/css/app.css
* creating my_movies/assets/js/app.js
* creating my_movies/priv/static/robots.txt
* creating my_movies/priv/static/images/phoenix.png
* creating my_movies/priv/static/favicon.ico

Fetch and install dependencies? [Yn]
Enter fullscreen mode Exit fullscreen mode

O --no-live diz para nosso gerador que não utilizaremos live view. O --database seta para nós o banco que utilizaremos (potgres é o default, então não precisaria dele ali, mas para deixar mais claro, adicionei. Também da suporte a mysql a mssql, sqlite3 e outros.

No fim da operação o terminal ira perguntar se quer instalar as dependências. Pode ser feito depois, mas eu ja apertei Y aqui enquanto tomava um gole de café.

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix deps.compile

We are almost there! The following steps are missing:

    $ cd my_movies

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server
Enter fullscreen mode Exit fullscreen mode

Finalizando o processo devemos entrar na pasta do projeto.

cd my_movies 
Enter fullscreen mode Exit fullscreen mode

Agora falta so configurar o banco de dados em config/dev.exs. Eu uso para fins de estudo o default, então meu está igual ao gerado.

config :my_movies, MyMovies.Repo,
  username: "postgres",
  password: "postgres",
  hostname: "localhost",
  database: "my_movies_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10
Enter fullscreen mode Exit fullscreen mode

De volta ao terminal, precisamos rodar o setup da aplicação para conseguir executar ela. Utilizarei o comando mix setup onde ele ja vai fazer para mim o mix deps.get e mix ecto.setup (que vai criar o banco para nós e rodar a migration caso tenha)

mix setup

Compiling 14 files (.ex)
Generated my_movies app
The database for MyMovies.Repo has already been created

14:25:38.948 [info] Migrations already up
Enter fullscreen mode Exit fullscreen mode

Não se preocupe com o warning do gettext ele não morde. Porém se voce se incomodar com o warning pode ir no mix.exs e remover ele do compilers.

Pronto, tudo configurado e bom para ir. Para garantir vitória so executar o comando de inicializar o nosso server:

mix phx.server

[info] Running MyMoviesWeb.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)
[debug] Downloading esbuild from https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.29.tgz
[info] Access MyMoviesWeb.Endpoint at http://localhost:4000
[watch] build finished, watching for changes..
Enter fullscreen mode Exit fullscreen mode

Podemos então abrir nossa aplicação no browser http://localhost:4000. Vai, clica ai, confia.

Pagina inicial da aplicacao

Proximo passo: Criar nossa página para cadastrar, ler, editar e deletar um filme. (famoso CRUD)

e ai que entra nosso primeiro generate.

Segura ai, ainda não iremos tocar no código.

Na documentação do Phoenix temos outros generators que podem nos ser útil. Em nosso CRUD precisamos do pacote completo:

  • schema: A estrutura de nosso Filme
  • migration: O comando para criar a sua tabela no banco de dados
  • context: um lugar para ele viver
  • controller: um lugar para acessar ele
  • view: uma forma de alterar sua exibição

Dois generator atendem a esse requisito:

  • phx.gen.json
  • phx.gen.html

Agora vai de você escolher como voce vai expor seu programa. Nós aqui iremos utilizar HTML, logo phx.gen.html será utilizado.

Nossa regra para isso será:

  1. Temos um catalogo
  2. No catalogo podemos adicionar, ver, listar e deletar filmes
  3. os filmes terão titulo e descrição apenas
  4. os filmes serão salvos na tabela movies no banco de dados

Bora para o terminal:

mix phx.gen.html Catalog Movie movies title:string description:string

* creating lib/my_movies_web/controllers/movie_controller.ex
* creating lib/my_movies_web/templates/movie/edit.html.heex
* creating lib/my_movies_web/templates/movie/form.html.heex
* creating lib/my_movies_web/templates/movie/index.html.heex
* creating lib/my_movies_web/templates/movie/new.html.heex
* creating lib/my_movies_web/templates/movie/show.html.heex
* creating lib/my_movies_web/views/movie_view.ex
* creating test/my_movies_web/controllers/movie_controller_test.exs
* creating lib/my_movies/catalog/movie.ex
* creating priv/repo/migrations/20221027161657_create_movies.exs
* creating lib/my_movies/catalog.ex
* injecting lib/my_movies/catalog.ex
* creating test/my_movies/catalog_test.exs
* injecting test/my_movies/catalog_test.exs
* creating test/support/fixtures/catalog_fixtures.ex
* injecting test/support/fixtures/catalog_fixtures.ex

Add the resource to your browser scope in lib/my_movies_web/router.ex:

    resources "/movies", MovieController


Remember to update your repository by running migrations:

    $ mix ecto.migrate
Enter fullscreen mode Exit fullscreen mode

No final do comando ele pede para fazer algumas ações.

  • Adicionar o resources "/movies", MovieController em nossas rotas lib/my_movies_web/router.ex. Iremos colocar em acesso publico (sem restrição por não estar logado)
# ...
scope "/", MyMoviesWeb do
  pipe_through :browser

  get "/", PageController, :index
  resources "/movies", MovieController # adicione essa linha
end
# ...
Enter fullscreen mode Exit fullscreen mode
  • rodar a migração que o gerador criou para nós.
mix ecto.migrate

13:26:53.578 [info] == Running 20221027161657 MyMovies.Repo.Migrations.CreateMovies.change/0 forward

13:26:53.581 [info] create table movies

13:26:53.597 [info] == Migrated 20221027161657 in 0.0s
Enter fullscreen mode Exit fullscreen mode

O gerador fez bastante coisa né? Sugiro dar uma olhada no que foi adicionado e criado.

Uma nota: nem sempre vem como queremos, tendo muitas vezes que fazer mudanças. Os generators são uma base para adiantarmos nosso trabalho, ele não vai te entregar exatamente o que você precisa.

A não ser que você queira um CRUD simples e esse é o nosso caso! Rode a aplicação para ver o que aconteceu.

mix phx.server
Enter fullscreen mode Exit fullscreen mode

E vamos entrar na nova rota criada: http://localhost:4000/books, aperta ai ;)

tela

A segunda etapa do nosso sistema esta pronto. Podemos criar, listar. editar e deletar filmes. 🎉

Mas está meio estranho esse admin ter acesso por todos sem um login e senha não? 🤔

Sim e vamos resolver agora!

Gerando autenticação

Podemos também gerar essa parte do sistema, adiantando e muito nosso sistema. Utilizaremos o phx.gen.auth para isso.

A regra para isso sera:

  1. Precisamos que nosso usuário tenha uma conta com email e senha
  2. Precisamos validar via link se o e-mail existe de fato.
  3. Precisamos realizar o loggin do usuario
  4. Precisamos dar a possibilidade do logout
  5. Precisamos proteger nossa rota de filmes.

Let's go

O phx.gen.auth funciona muito parecido com o phx.gen.html que usamos. Primeiro passamos o contexto, depois o schema e por fim a tabela.

mix phx.gen.auth Account user users

...
* creating lib/my_movies_web/controllers/user_settings_controller.ex
* creating test/my_movies_web/controllers/user_settings_controller_test.exs
* creating lib/my_movies/accounts.ex
* injecting lib/my_movies/accounts.ex
* creating test/my_movies/accounts_test.exs
* injecting test/my_movies/accounts_test.exs
* creating test/support/fixtures/accounts_fixtures.ex
* injecting test/support/fixtures/accounts_fixtures.ex
* injecting test/support/conn_case.ex
* injecting config/test.exs
* injecting mix.exs
* injecting lib/my_movies_web/router.ex
* injecting lib/my_movies_web/router.ex - imports
* injecting lib/my_movies_web/router.ex - plug
* injecting lib/my_movies_web/templates/layout/root.html.heex

Please re-fetch your dependencies with the following command:

    $ mix deps.get

Remember to update your repository by running migrations:

    $ mix ecto.migrate

Once you are ready, visit "/users/register"
to create your account and then access "/dev/mailbox" to
see the account confirmation email.
Enter fullscreen mode Exit fullscreen mode

Duas ações são necessárias

  1. gerir dependencias mix deps.get
  2. rodar migrations mix ecto.migrate

e voce esta bem para ir.

Mas vamos analisar a parte final que é interessante:

...
Once you are ready, visit "/users/register"
to create your account and then access "/dev/mailbox" to
see the account confirmation email.
Enter fullscreen mode Exit fullscreen mode

O generate ja trás para nossas as funções de register, login e também confirmation para saber que o e-mail está correto e assim ativar sua conta.

Existe diversas configurações que podem ser feitas aqui, recomendo a leitura da documentação e procurar na internet outras formas de customizar isso, é bem interessante e prático.

continuando...

Rode novamente o servidor phoenix mix phx.server e voce verá algumas mudanças no cabeçalho.

pagina inicial com login e registro

Agora você pode ver no lado direito o register e o Log in e server para exatamente isso =D

Faça o register para ver o que acontece. Uma vez cadastrado as opções la em cima mudam:

home logado

Você já possui um sistema de autenticação para usar. Os arquivos gerados estão na sua codebase e você pode alterar a vontade, para suprir suas necessidades.

Um detalhe interessante, quando se faz o cadastro é enviado um e-mail, mas já que estamos local ele não vai para o mundo externo. Em vez disso ele é pego no meio do caminho e você pode ver ele aqui http://localhost:4000/dev/mailbox e fazer a confirmação sem precisar mandar para um provedor de e-mail. Muito util e rápido para testar, não?

dev mailbox

Booa, bastante coisa pronta não? Mas faltou uma ultima etapa. Se voce acessar o /movies sem estar logado ainda poderá realizar as operações, vamos resolver isso.

Protegendo rotas privadas

Ao ter gerado a autenticação o seu arquivo router.ex mudou e agora você tem la alguns plugs e escopos novos.

Para os recursos de filmes estarem configurados de forma a não deixar pessoas não autenticadas acessar, você precisa usar o plug :require_authenticated_user no pipe do escopo.

Depois de gerar a autenticação, foi configurado novas rotas automaticamente (que ja acessamos, como register e Log in). Basta agora mover o resources "/movies", MovieController para o escopo que já está utilizando o plug ou criar um novo escopo para você. Irei optar por isso:

# lib/my_movies_web/router.ex
# ...

scope "/admin", MyMoviesWeb do
  pipe_through [:browser, :require_authenticated_user]

  resources "/movies", MovieController
end

# ...
Enter fullscreen mode Exit fullscreen mode

No código acima, colocamos o recurso de filmes abaixo do escopo admin, então o acesso na URL mudou sendo agora http://localhost:4000/admin/movies

Caso esteja logado você conseguirá ver a página normalmente, mas caso não esteja, você será redirecionado para a página de login e uma mensagem será exibida:

redirect login

Basta fazer o login e você será enviado novamente para a listagem de filmes.

Listagem de filmes

Agora sim! Tudo pronto 🎉🎉


Conclusão

Geradores podem causar grandes discussões no mundo do desenvolvimento e por uma boa razão.

Quando os utilizamos só por utilizar, sem uma consciência do que esta acontecendo, tende a deixar as coisas mais confusas e criar funcionalidades que nunca serão utilizadas. Isso é ruim a longo prazo, onde se torna difícil manter.

Mas se você está fazendo as escolhas consciente, analisando os impactos que isso possa causar e alterando o código gerado para se adequar ao seu produto, não a mau em utiliza-lo, eles te pouparão um grande tempo.

Espero que tenham gostado ;)


Já que você chegou até aqui, se inscreva no Café com Elixir uma newsletter semanal de Elixir focado em conteúdos da comunidade brasileira.

Me sigam também no twitter @iagoEffting lá sou mais ativo =D

E se inscrevam no canal no youtube

Top comments (0)