💧 Elixir: Trabalhando com Ecto Embeds (Campos Json)
Tags: elixir, ecto, braziliandevs, database
Published: 2022-03-20 20:09:24

OBS: Esse post é uma continuação das [associações](https://dev.to/maiquitome/elixir-trabalhando-com-associacoes-no-ecto-d34). Se você não fez o setup do projeto, você pode voltar para o post anterior para fazê-lo.

Além das associações, o Ecto também suporta `embeds (incorporações)` em alguns bancos de dados. Com `embeds`, o filho é incorporado no pai, em vez de ser armazenado em outra tabela.

Bancos de dados como o PostgreSQL usa uma combinação de colunas __JSONB (embeds_one/3)__ e __ARRAY__ para fornecer esta funcionalidade (tanto JSONB como ARRAY são suportados por padrão e são cidadãos de primeira classe no Ecto).

[![A Jornada do Autodidata em Inglês](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkoqkmh9hic17tnddjf6.png)](https://go.hotmart.com/R93865620U)

Trabalhar com `embeds` é na maioria das vezes o mesmo que trabalhar com outro campo em um `schema`, exceto quando se trata de manipulá-los. Vamos ver um exemplo:

Crie um arquivo com o conteúdo:
```elixir
defmodule Blog.Permalink do
  use Ecto.Schema

  embedded_schema do
    field :url
    timestamps
  end
end
```

Altere o schema `Blog.Post`:
```elixir
defmodule Blog.Post do
  use Ecto.Schema

  schema "posts" do
    field :title
    field :body
    has_many :comments, Blog.Comment

    # adicione essa linha
    embeds_many :permalinks, Blog.Permalink

    timestamps()
  end
end
```

Gere uma migração pra criar o campo `permalinks`:
```bash
$ mix ecto.gen.migration add_permalinks_to_post
```

Altere o arquivo de migração:
```elixir
defmodule Blog.Repo.Migrations.AddPermalinksToPost do
  use Ecto.Migration

  def change do
    # adicionando o campo `permalinks` na tabela `posts`
    alter table(:posts) do
      add :permalinks, :jsonb
    end
  end
end
```

O `:jsonb` já aceita uma lista, então não precisa colocar `{:array, :jsonb}`.

```bash
$ mix ecto.migrate
```

É possível inserir diretamente um post com múltiplos permalinks:

```elixir
iex> Blog.Repo.insert!(%Blog.Post{
  title: "Hello",
  permalinks: [
    %Blog.Permalink{url: "example.com/thebest"},
    %Blog.Permalink{url: "another.com/mostaccessed"}
  ]
})
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlvnrqfqj16l0n4judwk.png)
 
Vamos ver como ficou no banco de dados:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b97sm5o5x8ikebzge2mi.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sgrr4yo14udx0y67irof.png)
 
Semelhante às associações, você também pode gerenciar essas entradas usando `changesets (conjuntos de alterações)`:

Preste atenção no `Ecto.Changeset.put_embed`.

```elixir
# Generate a changeset for the post
changeset = Ecto.Changeset.change(post)

# Let's track the new permalinks
changeset = Ecto.Changeset.put_embed(changeset, :permalinks,
  [%Permalink{url: "example.com/thebest"},
   %Permalink{url: "another.com/mostaccessed"}]
)

# Now insert the post with permalinks at once
post = Repo.insert!(changeset)
```

Se você quiser substituir ou remover um permalink em particular, você pode trabalhar com permalinks como coleção e depois simplesmente colocá-lo como uma mudança novamente:

```elixir
# Remove all permalinks from example.com
permalinks = Enum.reject post.permalinks, fn permalink ->
  permalink.url =~ "example.com"
end

# Let's create a new changeset
changeset =
  post
  |> Ecto.Changeset.change
  |> Ecto.Changeset.put_embed(:permalinks, permalinks)

# And update the entry
post = Repo.update!(changeset)
```
Se você não conhece `Enum.reject`, [veja aqui como funciona de forma visual](https://superruzafa.github.io/visual-elixir-reference/Enum/reject/2/).


A beleza de trabalhar com os changesets é que eles acompanham todas as mudanças que serão enviadas para o banco de dados e nós podemos introspectá-los a qualquer momento. Por exemplo, se chamássemos antes `Repo.update!/3`:

```elixir
IO.inspect(changeset.changes.permalinks)
```

Nós veríamos algo parecido:

```elixir
[
    %Ecto.Changeset{
       action: :delete, 
       changes: %{},
       model: %Permalink{url: "example.com/thebest"}
    },
    %Ecto.Changeset{
       action: :update, 
       changes: %{},
       model: %Permalink{url: "another.com/mostaccessed"}
    }
]
```

Se, por acaso, também estivéssemos inserindo um permalink nesta operação, veríamos ali outro changeset com a ação `:inserir`.

Os changesets contêm uma visão completa do que está mudando, como eles estão mudando e você pode manipulá-los diretamente.