Looking for help with context implementation (for polymorphic associations)

Thanks, that’s actually a big help for the list functions. I can actually get away without the protocol now.

Do you have any idea how to do the same for build_assoc with a many to many association without resorting to a protocol? When I call build_assoc, it only builds a blank post record. I end up having to add the record in the initiative_posts join table manually using insert_all in an Ecto.Multi transaction.

Below is what I have for the Feeds context now. Way more slick, so thanks for that!

defmodule Hydroplane.Feeds do
  import Ecto.Query

  alias Hydroplane.Repo
  alias Hydroplane.Feeds.Feed

  def list_feed_items(feedable), do: list_posts(feedable)

  def list_posts(feedable) do
    Ecto.assoc(feedable, :posts)
    |> order_by_date()
    |> Repo.all()
    |> Repo.preload(:author)
  end

  def list_polls(feedable) do
    Ecto.assoc(feedable, :polls)
    |> order_by_date()
    |> Repo.all()
    |> Repo.preload(:author)
  end

  def create_post(feedable, author, attrs) do
    case Feed.create_post(feedable, author, attrs) do
      {:ok, %{post: post}} -> {:ok, post}
      _ -> {:error, "Could not create post"}
    end
  end

  defp order_by_date(query) do
    from p in query, order_by: [desc: p.inserted_at]
  end
end

Below is the protocol implementation for Initiative, which I’m not a fan of. I’d much rather be able to automatically create the record in the join table when I insert the post.

defimpl Hydroplane.Feeds.Feed, for: Hydroplane.Initiatives.Initiative do
  alias Ecto.Multi
  alias Hydroplane.Feeds.Post
  alias Hydroplane.Repo

  def create_post(initiative, author, attrs) do
    Multi.new()
    |> Multi.insert(:post, Post.changeset(%Post{author: author}, attrs))
    |> Multi.run(:initiative_post, fn _repo, %{post: post} ->
      insert_post_join(initiative, post)
    end)
    |> Repo.transaction()
  end

  defp insert_post_join(initiative, post) do
    case Repo.insert_all("initiative_posts", [%{initiative_id: initiative.id, post_id: post.id}]) do
      {1, _} -> {:ok, "Associated post with initiative"}
      _ -> {:error, "Unable to associate post with initiative"}
    end
  end
end

Is there any way that I can dynamically build a changeset that inserts two records, one in post and one in the join table the many-to-many association uses?