Como utilizar generators no Phoenix
Tags: elixir, phoenix, generators, iniciante
Published: 2022-10-27 19:22:00

---
title: Como utilizar generators no Phoenix
published: true
description: Existem formas de sermos mais rápidos no desenvolvimento web e o phoenix nos ajuda desde os primeiros passos. Vamos gerar códigos por linha de comando.
tags: elixir, phoenix, generators, iniciante

cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gcw3mq9n4juomff3c0b.png

published_at: 2022-10-27 19:22
---

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](https://www.phoenixframework.org/). 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

* [Instalar Elixir](https://elixir-lang.org/install.html)
* [Instalar Phoenix](https://hexdocs.pm/phoenix/installation.html)

## 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:

```shell
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]
```

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é.

```shell
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
```

Finalizando o processo devemos entrar na pasta do projeto.

```shell
cd my_movies 
```
 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.

```elixir
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
```

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)

```shell
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
```

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:

```shell
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..
```

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


![Pagina inicial da aplicacao](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8b4jd9owupo71rvbv2qs.png)

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

e ai que entra nosso primeiro generate.

<img width="100%" style="width:100%" src="https://cdn-images-1.medium.com/max/800/1*z6Wq622lYQ1y2_qIaDNwJA.gif">

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

Na documentação do Phoenix temos [outros generators](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.html) 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](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Html.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:

```shell
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
```

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)

```elixir
# ...
scope "/", MyMoviesWeb do
  pipe_through :browser

  get "/", PageController, :index
  resources "/movies", MovieController # adicione essa linha
end
# ...
```

* rodar a migração que o gerador criou para nós. 

```shell
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
```

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.

```shell
mix phx.server
```

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

![tela](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pi7qi2lig2l4yt538yxx.png)

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](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Auth.html) 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.

```shell
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.
```

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:

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

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fz0sh9pe8g0d33tyos6.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6j3l3acq8abazwheck5.png)

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](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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yy9ieuj8f3wccz5mu43l.png)

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:

```elixir
# lib/my_movies_web/router.ex
# ...

scope "/admin", MyMoviesWeb do
  pipe_through [:browser, :require_authenticated_user]
  
  resources "/movies", MovieController
end

# ...
```

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](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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o873yid93oncbsbqvaet.png)

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

![Listagem de filmes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tyo48i1wiyhqg7rtewub.png)

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](https://www.getrevue.co/profile/iagoeffting) uma newsletter semanal de Elixir focado em conteúdos da comunidade brasileira.

Me sigam também no twitter [@iagoEffting](https://twitter.com/iagoEffting) lá sou mais ativo =D

E se inscrevam no canal no [youtube](https://www.youtube.com/c/IagoEffting)