zimt28

zimt28

In my application I want to have a single entities table, which stores type a and type b entities. type a and type b share the exact same fields, I just set a type field can be "type_a" or "type_b".

For some reasons I don’t want to work with %Entity{} structs, but %TypeA{} and %TypeB{} structs, and I’m now wondering what might be the best way to do this.

In the end I’m looking for a way to create “aliases” for my %Entity{} struct. Here’s what I’ve tried so far:

1. Just define the schema multiple times

defmodule App.Entities.Entity do
  # ...

  schema "entities" do
    field :title, :string
    field :type, :string
    has_one :address, Address
  end

  @doc false
  def changeset(%Entity{} = entity, attrs) do
    entity
    |> cast(attrs, [:title, :type])
    |> validate_required([:title, :type])
  end
end

defmodule App.Entities.TypeA/B do
  # ...

  schema "entities" do
    field :title, :string
    field :type, :string
    has_one :address, Address, foreign_key: :entity_id
  end

  @doc false
  def changeset(%TypeA/B{} = entity, attrs) do
    entity
    |> cast(attrs, [:title, :type])
    |> validate_required([:title, :type])
    |> put_change(:type, "type_a/b")
  end
end

This works fine, but I don’t like the repetitiveness of this approach.

2. Use a separate schema module

defmodule App.Entities.EntitySchema do
  defmacro __using__(_) do
    quote do
      # ...

      schema "entities" do
        field :title, :string
        field :type, :string
        has_one :address, Address, foreign_key: :entity_id
      end
    end
  end
end

defmodule App.Entities.Entity do
  use App.Entities.EntitySchema

  @doc false
  def changeset(%Entity{} = entity, attrs) do
    entity
    |> cast(attrs, [:title, :type])
    |> validate_required([:title, :type])
  end
end

defmodule App.Entities.TypeA/B do
  use App.Entities.EntitySchema

  @doc false
  def changeset(%TypeA/B{} = entity, attrs) do
    entity
    |> Map.put(:__struct__, Entity)
    |> Entity.changeset(attrs)
    |> put_change(:type, "type_a/b")
  end
end

I prefer this approach, but it still seems to be a bit overkill for a “simple alias”.

3. Define structs

I have not tried this so far but it might be possible to use regular Elixir structs:

defmodule App.Entities.TypeA/B do
  defstruct [:id, :title, :type, :address, :created_at, :updated_at]
end

defmodule App.Entities.Entity do
  # ...

  schema "entities" do
    field :title, :string
    field :type, :string
    has_one :address, Address
  end

  @doc false
  def changeset(%TypeA/B{} = entity, attrs) do
    entity
    |> Map.put(:__struct__, Entity)
    |> changeset(attrs)
    |> put_change(:type, "type_a/b")
  end
  
  def changeset(%Entity{} = entity, attrs) do
    entity
    |> cast(attrs, [:title, :type])
    |> validate_required([:title, :type])
  end
end

This seems to be a clean approach as my %TypeA/B{} structs are pure data containers,
but I’m not sure if I would lose some Ecto functionality, say preloads etc.?

Alternatives, Feedback?

How would you solve this problem and why? Maybe there’s a better approach that I haven’t thought of yet :slight_smile:

Showing Posts 1 to 1

zimt28

zimt28 OP

I’m still not sure how I want to do it, but no. 3 – defining structs – is also possible:

defmodule App.Entities.TypeA/B do
  @entity App.Entities.Entity.__struct__()

  defstruct @entity
            |> Map.keys()
            |> Enum.reduce([], fn key, acc ->
              val = Map.get(@entity, key)
              Keyword.put_new(acc, key, val)
            end)

  def __schema__(key) do
    App.Entities.Entity.__schema__(key)
  end

  def __schema__(key, arg) do
    App.Entities.Entity.__schema__(key, arg)
  end

  def __changeset__ do
    App.Entities.Entity.__changeset__()
  end
end

I’ve moved this into a separate module so I can just do

defmodule App.Entities.TypeA/B do
  use App.Alias, for: App.Entities.Entity
end

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews