Skip to content

Commit

Permalink
Add support for autoloading nested related objects on ingredients
Browse files Browse the repository at this point in the history
This takes the capabilities from AlchemyCMS/alchemy_cms#2523 and allows
pre-loading object graphs on ingredient's related objects.
  • Loading branch information
mamhoff committed Jul 19, 2023
1 parent 44784da commit aab986e
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
27 changes: 22 additions & 5 deletions app/controllers/alchemy/json_api/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ def show
def render_pages_json(allowed)
# Only load pages with all includes when browser cache is stale
jsonapi_filter(page_scope_with_includes, allowed) do |filtered|
# decorate with our page model that has a eager loaded elements collection
filtered_pages = filtered.result.map { |page| api_page(page) }
jsonapi_paginate(filtered_pages) do |paginated|
render jsonapi: paginated
jsonapi_paginate(filtered.result) do |paginated|
# decorate with our page model that has a eager loaded elements collection
decorated_pages = preload_ingredient_relations(paginated).map { |page| api_page(page) }
render jsonapi: decorated_pages
end
end
end
Expand Down Expand Up @@ -74,7 +74,9 @@ def jsonapi_meta(pages)
end

def load_page
@page = load_page_by_id || load_page_by_urlname || raise(ActiveRecord::RecordNotFound)
@page = preload_ingredient_relations(
[load_page_by_id || load_page_by_urlname || raise(ActiveRecord::RecordNotFound)]
).first
end

def load_page_by_id
Expand Down Expand Up @@ -109,6 +111,21 @@ def page_scope_with_includes
)
end

def preload_ingredient_relations(pages)
pages.map { |page| page.send(page_version_type) }.flat_map(&:elements).flat_map(&:ingredients).group_by(&:preload_relations).each do |preload_relations, ingredients|
preload(records: ingredients.map(&:related_object).compact, associations: preload_relations)
end
pages
end

def preload(records:, associations:)
if Rails::VERSION::MAJOR >= 7
ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call
else
ActiveRecord::Associations::Preloader.new.preload(records, associations)
end
end

def page_version_type
:public_version
end
Expand Down
12 changes: 9 additions & 3 deletions app/serializers/alchemy/json_api/page_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,21 @@ class PageSerializer < BaseSerializer

# All public elements of this page regardless of if they are fixed or nested.
# Used for eager loading and should be used as the +include+ parameter of your query
has_many :all_elements, record_type: :element, serializer: ELEMENT_SERIALIZER
has_many :all_elements, record_type: :element, serializer: ELEMENT_SERIALIZER do |record|
record.public_version.element_repository.visible
end

# The top level public, non-fixed elements of this page that - if present -
# contains their nested_elements.
has_many :elements, record_type: :element, serializer: ELEMENT_SERIALIZER
has_many :elements, record_type: :element, serializer: ELEMENT_SERIALIZER do |record|
record.public_version.element_repository.visible.unfixed
end

# The top level public, fixed elements of this page that - if present -
# contains their nested_elements.
has_many :fixed_elements, record_type: :element, serializer: ELEMENT_SERIALIZER
has_many :fixed_elements, record_type: :element, serializer: ELEMENT_SERIALIZER do |record|
record.public_version.element_repository.visible.unfixed
end
end
end
end

0 comments on commit aab986e

Please sign in to comment.