Configure custom fields dynamically on your Phoenix models
- Add
attribrutex
to your list of dependencies inmix.exs
:
def deps do
[{:attribrutex, "~> 0.1.0"}]
end
- Configure Attribrutex to use your repo in
config/config.exs
:
config :attribrutex, repo: ApplicationName.Repo
- Install your dependencies:
mix deps.get
- Generate the migrations:
mix attribrutex.install
- Run the migrations:
mix ecto.migrate
- Generate necessary migration for each table where you want to allow dinamyc fields and run it:
mix attribrutex.migrate <table_name>
mix ecto.migrate
- Add the field to the schema in your models:
field :custom_fields, :map, default: %{}
Attribrutex works with three basic functions:
Using create_custom_field(key, type, module, opts \\ []
you can
create a new field for a specific module
i.e.:
Attribrutex.create_custom_field("location", :string, User)
As you can see you can specify a set of supported data types:
:string
:integer
:boolean
:float
If you need to create a field on specific context, for example, you need every user to have their own fields, not accessible for any other user, then, you can use the opts to make the new field belongs to a specific resource:
Attribrutex.create_custom_field("location", :string, Job,
context_id: 1, context_type: "User")
After that, you will have a "location" string field on the Job
model
and only accessible by the user with the id 1
Use list_custom_fields_for(module, opts \\ %{})
to list the fields for
a module. If you want to get the fields for a context you can use opts
field and set a :context_id
and :context_type
keys.
It also supports a :mode
param to return the fields in different ways:
:keys
- Only returns the key values for every entry.:fields
- Returns a list of maps with:key
andtype
keys
If :mode
key doesn't exist, all the struct will be returned.
You can use prepare_custom_fields(changeset, params, opts \\ %{})
to
add the changes to your changeset.
We recommend adding the function to your changeset function to deal with custom_fields:
defmodule AttribrutexUser do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :email, :string
field :custom_fields, :map, default: %{}
timestamps()
end
def changeset(struct, params \\ %{}, opts \\ %{}) do
struct
|> cast(params, [:email])
|> validate_required([:email])
|> Attribrutex.prepare_custom_fields(params, opts)
end
end
In case that a param value doesn't match with the type, an error will be set on the changeset:
#Ecto.Changeset<action: nil, changes: %{email: "[email protected]"},
errors: [custom_fields: {"Bad data type", [custom_field: :location]}],
data: #AttribrutexUser<>, valid?: false>
Here we expose a full workflow example where we create a field, list it and add some value.
Attribrutex.create_custom_field("sample", :string, AttribrutexUser)
# {:ok,
# %Attribrutex.CustomField{__meta__: #Ecto.Schema.Metadata<:loaded, "custom_fields">,
# context_id: nil, context_type: nil, field_type: :string,
# fieldable_type: "AttribrutexUser", id: 1231,
# inserted_at: ~N[2017-07-20 13:08:35.134738], key: "sample",
# updated_at: ~N[2017-07-20 13:08:35.134761]}}
Attribrutex.list_custom_fields_for(AttribrutexUser)
# [%Attribrutex.CustomField{__meta__: #Ecto.Schema.Metadata<:loaded, "custom_fields">,
# context_id: nil, context_type: nil, field_type: :string,
# fieldable_type: "AttribrutexUser", id: 1248,
# inserted_at: ~N[2017-07-20 13:14:25.997483], key: "sample",
# updated_at: ~N[2017-07-20 13:14:25.997505]}]
Attribrutex.list_custom_fields_for(AttribrutexUser, %{mode: :fields})
# [%{key: "sample", type: :string}]
Attribrutex.list_custom_fields_for(AttribrutexUser, %{mode: :keys})
# ["sample"]
AttribrutexUser.changeset(%AttribrutexUser{}, %{email: "[email protected]", location: "Paris"})
# Ecto.Changeset<action: nil,
# changes: %{custom_fields: %{location: "Madrid"}, email: "[email protected]"},
# errors: [], data: #AttribrutexUser<>, valid?: true>
Clone the repo and fetch dependencies with mix deps.get
Executing mix test
should run all the tests.
See LICENSE