defmodule ExTeal.Fields.HasOne do
@moduledoc """
The `HasOne` field corresponds to a `has_one` ecto relationship. For example,
let's assume a `Post` schema `has_one` `PermaLink` schema. We may add the relationship
to our `Post` ExTeal resource like so:
alias ExTeal.Fields.HasOne
HasOne.make(:permalink)
# Title Attributes
When a `HasOne` field is shown on an associated `BelongsTo` resources index/detail screen, the associated
row will have a link to the `HasOne` and will display the "title" of the resource. For example, a
`permalink` resource may display the `address` attribute as it's title. Then, when the resource
is shown as an association on the belongs_to index or detail views, that attribute will be displayed, supported attributes are `name`, `title`, or `address`.
"""
use ExTeal.Field
alias ExTeal.{Field, Resource}
def component, do: "has-one"
def show_on_new, do: false
def show_on_edit, do: false
@impl true
def make(relationship_name, module, label \\ nil) do
__MODULE__
|> Field.struct_from_field(relationship_name, label)
|> Map.put(:relationship, module)
end
@impl true
def value_for(field, model, type) do
schema = Field.value_for(field, model, type)
case ExTeal.resource_for_model(field.relationship) do
{:ok, resource} ->
resource.title_for_schema(schema)
{:error, :not_found} ->
nil
end
end
@impl true
def apply_options_for(field, model, conn, _type) do
rel = model.__struct__.__schema__(:association, field.field)
with {:ok, resource} <- ExTeal.resource_for_model(rel.queryable) do
related = Map.get(model, field.field, %{})
opts =
Map.merge(field.options, %{
has_one_relationship: field.field,
has_one_id: id_for_related(related),
listable: true
})
Map.put(field, :options, Map.merge(Resource.to_json(resource, conn), opts))
end
end
defp id_for_related(nil), do: nil
defp id_for_related(rel), do: Map.get(rel, :id)
end