This is a fairly simple repo structure question that I want to get right. Say I have 3 tables: users
, posts
, and bookmarks
. A user can bookmark a post so on whole a post has_many bookmarks
. Simple enough so far.
When I’m rendering the posts#index
view I want to indiciate to a user whether a user has bookmarked a given post. Currently I query for all the posts and bookmarks like so:
# In controller
def index(conn, params) do
posts = Posts.list_all_for_page(params["page"])
bookmarks = Bookmarks.list_all_for_user_and_posts(current_user, posts)
end
# in view (pseudocode)
for post <- @posts do
if Enum.find(bookmarks, nil, fn bookmark -> bookmark.post_id == post.id) do
render saved_as_bookmark
end
end
This isn’t exactly how it’s happening but it’s the same idea. This feels pretty incorrect to me and instead I’d like to have the bookmarking state as an attribute on the post
so I can do something like if posts.bookmark_for_user ...
instead. What’s the conventional way to accomplish this? I was thinking that a in addition to a post having_many bookmarks it could also has_one bookmark_for_user
that gets loaded as part of the list_all_for_page
query.
How about building a query that will preload the bookmark if the bookmark belongs to the user? Something lke:
Repo.all(
from p in Posts,
outer_join: b in Bookmarks,
on: b.post_id == p.id and b.user_id == ^user.id
# preloads...
)
Then in the view you’d check if the bookmark is not nil
to see if the user has bookmarked the post.
Thank you for the help! I was thinking of doing something like this, but the issue I kept coming back to is that post.bookmarks
would be a list type, whereas this new post.bookmark_for_user
would be of type Bookmark | nil
.
I’m trying to reconcile this by adding field :bookmark_for_user, Bookmark, virtual: true
in the posts
schema, but it looks like you can’t use Bookmark
as a type in this case. Do you have any suggestions for how to represent this association within the schema itself?
Imo all the little “issues” you’re hitting here are a result of trying to make an improper abstraction work. The Post
schema should be blissfully unaware of the fact that it’s going to be listed on a page you show to one special user and that the page wants to know if that one special user has bookmarked the post. At most the post should be aware of the fact that there are bookmarks pointing to users having placed the bookmarks.
If you really want to do post.is_bookmarked
or something similar in the templates/view, then it’s the job of a more specialized model. You could create a additional (read only) schema and consolidate the data in the core of the application (akin to read models e.g. in event sourcing) or you could fetch the data like before in the controller, but transform it to a view model, which is only known to the view/web layer.