@@ -47,6 +47,81 @@ def self.get_serializer_name(active_record_class)
4747 end
4848 end
4949
50+ # duplicate method from Serializer
51+ ForestAdmin ::JSONAPI ::Serializer . singleton_class . send ( :define_method , :find_recursive_relationships ) do |root_object , root_inclusion_tree , results , options |
52+ ActiveSupport ::Notifications . instrument (
53+ 'render.jsonapi_serializers.find_recursive_relationships' ,
54+ { class_name : root_object . class . name } ,
55+ ) do
56+ root_inclusion_tree . each do |attribute_name , child_inclusion_tree |
57+ next if attribute_name == :_include
58+
59+ serializer = ForestAdmin ::JSONAPI ::Serializer . find_serializer ( root_object , options )
60+ unformatted_attr_name = serializer . unformat_name ( attribute_name ) . to_sym
61+ object = nil
62+ is_collection = false
63+ is_valid_attr = false
64+ if serializer . has_one_relationships . has_key? ( unformatted_attr_name )
65+ # only added this condition
66+ if root_object . class . reflect_on_association ( unformatted_attr_name ) &.polymorphic?
67+ options [ :context ] [ :unoptimized ] = true
68+ end
69+
70+ is_valid_attr = true
71+ attr_data = serializer . has_one_relationships [ unformatted_attr_name ]
72+ object = serializer . has_one_relationship ( unformatted_attr_name , attr_data )
73+ elsif serializer . has_many_relationships . has_key? ( unformatted_attr_name )
74+ is_valid_attr = true
75+ is_collection = true
76+ attr_data = serializer . has_many_relationships [ unformatted_attr_name ]
77+ object = serializer . has_many_relationship ( unformatted_attr_name , attr_data )
78+ end
79+
80+ if !is_valid_attr
81+ raise ForestAdmin ::JSONAPI ::Serializer ::InvalidIncludeError . new (
82+ "'#{ attribute_name } ' is not a valid include." )
83+ end
84+
85+ if attribute_name != serializer . format_name ( attribute_name )
86+ expected_name = serializer . format_name ( attribute_name )
87+
88+ raise ForestAdmin ::JSONAPI ::Serializer ::InvalidIncludeError . new (
89+ "'#{ attribute_name } ' is not a valid include. Did you mean '#{ expected_name } ' ?"
90+ )
91+ end
92+
93+ next if object . nil?
94+
95+ objects = is_collection ? object : [ object ]
96+ if child_inclusion_tree [ :_include ] == true
97+ objects . each do |obj |
98+ obj_serializer = ForestAdmin ::JSONAPI ::Serializer . find_serializer ( obj , options )
99+ key = [ obj_serializer . type , obj_serializer . id ]
100+
101+ current_child_includes = [ ]
102+ inclusion_names = child_inclusion_tree . keys . reject { |k | k == :_include }
103+ inclusion_names . each do |inclusion_name |
104+ if child_inclusion_tree [ inclusion_name ] [ :_include ]
105+ current_child_includes << inclusion_name
106+ end
107+ end
108+
109+ current_child_includes += results [ key ] && results [ key ] [ :include_linkages ] || [ ]
110+ current_child_includes . uniq!
111+ results [ key ] = { object : obj , include_linkages : current_child_includes }
112+ end
113+ end
114+
115+ if !child_inclusion_tree . empty?
116+ objects . each do |obj |
117+ find_recursive_relationships ( obj , child_inclusion_tree , results , options )
118+ end
119+ end
120+ end
121+ end
122+ nil
123+ end
124+
50125 def initialize ( is_smart_collection = false )
51126 @is_smart_collection = is_smart_collection
52127 end
@@ -131,6 +206,51 @@ def relationship_related_link(attribute_name)
131206 ret
132207 end
133208
209+ def has_one_relationships
210+ return { } if self . class . to_one_associations . nil?
211+ data = { }
212+ self . class . to_one_associations . each do |attribute_name , attr_data |
213+ relation = object . class . reflect_on_all_associations . find { |a | a . name == attribute_name }
214+ next if !should_include_attr? ( attribute_name , attr_data )
215+
216+ if relation && relation . belongs_to? && relation . polymorphic? . nil?
217+ reflection_primary_key = relation . options [ :primary_key ] &.to_sym || :id
218+ klass_primary_key = relation . klass . primary_key . to_sym
219+
220+ if reflection_primary_key != klass_primary_key
221+ data [ attribute_name ] = attr_data . merge ( {
222+ attr_or_block : proc {
223+ relation . klass . find_by ( reflection_primary_key => object . send ( relation . foreign_key ) )
224+ }
225+ } )
226+ next
227+ end
228+ end
229+
230+ data [ attribute_name ] = attr_data
231+ end
232+
233+ data
234+ end
235+
236+ def should_include_attr? ( attribute_name , attr_data )
237+ collection = self . type
238+
239+ unless @options . dig ( :context , :unoptimized )
240+ return false unless @options [ :fields ] [ collection ] &.include? ( attribute_name . to_sym )
241+ end
242+
243+ # Allow "if: :show_title?" and "unless: :hide_title?" attribute options.
244+ if_method_name = attr_data [ :options ] [ :if ]
245+ unless_method_name = attr_data [ :options ] [ :unless ]
246+ formatted_attribute_name = format_name ( attribute_name ) . to_sym
247+ show_attr = true
248+ show_attr &&= send ( if_method_name ) if if_method_name
249+ show_attr &&= !send ( unless_method_name ) if unless_method_name
250+ show_attr &&= @_fields [ type . to_s ] . include? ( formatted_attribute_name ) if @_fields [ type . to_s ]
251+ show_attr
252+ end
253+
134254 private
135255
136256 def intercom_integration?
0 commit comments