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