Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eager load associated records #1674

Merged
merged 11 commits into from
Nov 20, 2019
25 changes: 23 additions & 2 deletions app/controllers/alchemy/admin/elements_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ class ElementsController < Alchemy::Admin::BaseController

def index
@page = Page.find(params[:page_id])
@elements = @page.all_elements.not_nested.unfixed.not_trashed
@fixed_elements = @page.all_elements.fixed.not_trashed
@elements = @page.all_elements.not_nested.unfixed.not_trashed.includes(*element_includes)
@fixed_elements = @page.all_elements.fixed.not_trashed.includes(*element_includes)
end

def list
Expand Down Expand Up @@ -104,6 +104,27 @@ def fold

private

def element_includes
[
{
contents: {
essence: :ingredient_association
}
},
:tags,
{
all_nested_elements: [
{
contents: {
essence: :ingredient_association
}
},
:tags
]
}
]
end

def load_element
@element = Element.find(params[:id])
end
Expand Down
19 changes: 17 additions & 2 deletions app/controllers/alchemy/api/contents_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def index
if params[:element_id].present?
@contents = @contents.where(element_id: params[:element_id])
end
@contents = @contents.includes(*content_includes)

render json: @contents, adapter: :json, root: 'contents'
end

Expand All @@ -30,12 +32,25 @@ def index
#
def show
if params[:id]
@content = Content.find(params[:id])
@content = Content.where(id: params[:id]).includes(:essence).first
elsif params[:element_id] && params[:name]
@content = Content.find_by!(element_id: params[:element_id], name: params[:name])
@content = Content.where(
element_id: params[:element_id],
name: params[:name]
).includes(*content_includes).first || raise(ActiveRecord::RecordNotFound)
end
authorize! :show, @content
respond_with @content
end

private

def content_includes
[
{
essence: :ingredient_association
}
]
end
end
end
27 changes: 26 additions & 1 deletion app/controllers/alchemy/api/elements_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,40 @@ def index
if params[:named].present?
@elements = @elements.named(params[:named])
end
@elements = @elements.includes(*element_includes)

render json: @elements, adapter: :json, root: 'elements'
end

# Returns a json object for element
#
def show
@element = Element.find(params[:id])
@element = Element.where(id: params[:id]).includes(*element_includes).first
authorize! :show, @element
respond_with @element
end

private

def element_includes
[
{
nested_elements: [
{
contents: {
essence: :ingredient_association
}
},
:tags
]
},
{
contents: {
essence: :ingredient_association
}
},
:tags
]
end
end
end
27 changes: 19 additions & 8 deletions app/controllers/alchemy/api/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,19 @@ def show
private

def load_page
@page = Page.find_by(id: params[:id]) ||
Language.current.pages.find_by(
urlname: params[:urlname],
language_code: params[:locale] || Language.current.code
) ||
raise(ActiveRecord::RecordNotFound)
@page = load_page_by_id || load_page_by_urlname || raise(ActiveRecord::RecordNotFound)
end

def load_page_by_id
# The route param is called :urlname although it might be an integer
Page.where(id: params[:urlname]).includes(page_includes).first
end

def load_page_by_urlname
Language.current.pages.where(
urlname: params[:urlname],
language_code: params[:locale] || Language.current.code
).includes(page_includes).first
end

def meta_data
Expand Down Expand Up @@ -101,13 +108,17 @@ def page_includes
{
nested_elements: [
{
contents: :essence
contents: {
essence: :ingredient_association
}
},
:tags
]
},
{
contents: :essence
contents: {
essence: :ingredient_association
}
},
:tags
]
Expand Down
11 changes: 6 additions & 5 deletions app/models/alchemy/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Element < BaseRecord
has_many :contents, -> { order(:position, :id) }, dependent: :destroy, inverse_of: :element

has_many :all_nested_elements,
-> { order(:position) },
-> { order(:position).not_trashed },
class_name: 'Alchemy::Element',
foreign_key: :parent_element_id,
dependent: :destroy
Expand All @@ -74,15 +74,17 @@ class Element < BaseRecord
-> { order(:position).available },
class_name: 'Alchemy::Element',
foreign_key: :parent_element_id,
dependent: :destroy
dependent: :destroy,
inverse_of: :parent_element

belongs_to :page, touch: true, inverse_of: :all_elements
belongs_to :page, touch: true, inverse_of: :elements

# A nested element belongs to a parent element.
belongs_to :parent_element,
class_name: 'Alchemy::Element',
optional: true,
touch: true
touch: true,
inverse_of: :nested_elements

has_and_belongs_to_many :touchable_pages, -> { distinct },
class_name: 'Alchemy::Page',
Expand Down Expand Up @@ -217,7 +219,6 @@ def store_page(page)
return true if page.nil?
unless touchable_pages.include? page
touchable_pages << page
save
end
end

Expand Down
10 changes: 6 additions & 4 deletions app/models/alchemy/element/element_contents.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ def content_by_type(essence_type)

# All contents from element by given name.
def contents_by_name(name)
contents.where(name: name)
contents.select { |content| content.name == name.to_s }
end
alias_method :all_contents_by_name, :contents_by_name

# All contents from element by given essence type.
def contents_by_type(essence_type)
contents.where(essence_type: Content.normalize_essence_type(essence_type))
contents.select do |content|
content.essence_type == Content.normalize_essence_type(essence_type)
end
end
alias_method :all_contents_by_type, :contents_by_type

Expand Down Expand Up @@ -99,7 +101,7 @@ def content_definition_for(content_name)
log_warning "Element #{name} is missing the content definition for #{content_name}"
nil
else
content_definitions.detect { |d| d['name'] == content_name }
content_definitions.detect { |d| d['name'] == content_name.to_s }
end
end

Expand Down Expand Up @@ -134,7 +136,7 @@ def contents_with_errors
def content_for_rss_meta(type)
definition = content_definitions.detect { |c| c["rss_#{type}"] }
return if definition.blank?
contents.find_by(name: definition['name'])
contents.detect { |content| content.name == definition['name'] }
end

# creates the contents for this element as described in the elements.yml
Expand Down
4 changes: 2 additions & 2 deletions app/models/alchemy/element/presenters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ def preview_content
private

def preview_text_from_nested_elements(maxlength)
return unless nested_elements.present?
nested_elements.first.preview_text(maxlength)
return if all_nested_elements.empty?
all_nested_elements.first.preview_text(maxlength)
end

def preview_text_from_preview_content(maxlength)
Expand Down
10 changes: 7 additions & 3 deletions app/models/alchemy/essence_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ class EssencePage < BaseRecord

acts_as_essence(
ingredient_column: :page,
preview_text_method: :name
preview_text_method: :name,
belongs_to: {
class_name: 'Alchemy::Page',
foreign_key: :page_id,
inverse_of: :essence_pages,
optional: true
}
)

belongs_to :page, class_name: 'Alchemy::Page', optional: true

def ingredient=(page)
case page
when PAGE_ID
Expand Down
8 changes: 6 additions & 2 deletions app/models/alchemy/essence_picture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@

module Alchemy
class EssencePicture < BaseRecord
acts_as_essence ingredient_column: 'picture'
acts_as_essence ingredient_column: :picture, belongs_to: {
class_name: 'Alchemy::Picture',
foreign_key: :picture_id,
inverse_of: :essence_pictures,
optional: true
}

belongs_to :picture, optional: true
delegate :image_file_width, :image_file_height, :image_file, to: :picture
before_save :fix_crop_values
before_save :replace_newlines
Expand Down
12 changes: 8 additions & 4 deletions app/models/alchemy/page/page_elements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@ module Page::PageElements

has_many :all_elements,
-> { order(:position) },
class_name: 'Alchemy::Element'
class_name: 'Alchemy::Element',
inverse_of: :page
has_many :elements,
-> { order(:position).not_nested.unfixed.available },
class_name: 'Alchemy::Element'
class_name: 'Alchemy::Element',
inverse_of: :page
has_many :trashed_elements,
-> { Element.trashed.order(:position) },
class_name: 'Alchemy::Element'
class_name: 'Alchemy::Element',
inverse_of: :page
has_many :fixed_elements,
-> { order(:position).fixed.available },
class_name: 'Alchemy::Element'
class_name: 'Alchemy::Element',
inverse_of: :page
has_many :contents, through: :elements
has_and_belongs_to_many :to_be_swept_elements, -> { distinct },
class_name: 'Alchemy::Element',
Expand Down
6 changes: 5 additions & 1 deletion app/models/alchemy/picture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ class Picture < BaseRecord
include Alchemy::Picture::Transformations
include Alchemy::Picture::Url

has_many :essence_pictures, class_name: 'Alchemy::EssencePicture', foreign_key: 'picture_id'
has_many :essence_pictures,
class_name: 'Alchemy::EssencePicture',
foreign_key: 'picture_id',
inverse_of: :ingredient_association

has_many :contents, through: :essence_pictures
has_many :elements, through: :contents
has_many :pages, through: :elements
Expand Down
2 changes: 1 addition & 1 deletion app/views/alchemy/admin/elements/_element.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
'droppable-elements' => element.nestable_elements.join(' ')
} do %>
<%= render partial: 'alchemy/admin/elements/element',
collection: element.all_nested_elements.not_trashed %>
collection: element.all_nested_elements %>
<% end %>

<% if element.expanded? || element.fixed? %>
Expand Down
31 changes: 17 additions & 14 deletions lib/alchemy/elements_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,15 @@ def elements(page:)

attr_reader :page, :options

def find_elements(page)
elements = Alchemy::Element
.where(page_id: page_ids(page))
.merge(Alchemy::Element.not_nested)
.where(fixed: !!options[:fixed])
.order(position: :asc)
.available
def find_elements(page_or_layout)
@page = get_page(page_or_layout)
return Alchemy::Element.none unless page

if options[:fixed]
elements = page.fixed_elements
else
elements = page.elements
end

if options[:only]
elements = elements.named(options[:only])
Expand All @@ -76,15 +78,16 @@ def find_elements(page)
elements
end

def page_ids(page)
case page
def get_page(page_or_layout)
case page_or_layout
when Alchemy::Page
page_or_layout
when String
Alchemy::Language.current.pages.where(
page_layout: page,
Alchemy::Page.find_by(
language: Alchemy::Language.current,
page_layout: page_or_layout,
restricted: false
).pluck("#{Alchemy::Page.table_name}.id")
when Alchemy::Page
page.id
)
end
end

Expand Down
Loading