Class: Avo::Fields::BelongsToField

Inherits:
BaseField
  • Object
show all
Includes:
Concerns::IsSearchable, Concerns::UseResource
Defined in:
lib/avo/fields/belongs_to_field.rb

Overview

Requirements

  • list
  • ID
  • label
  • Type
  • foreign_key
  • foreign_key for poly type
  • foreign_key for poly id
  • is_disabled?

Defined Under Namespace

Classes: EditComponent, IndexComponent, ShowComponent

Instance Attribute Summary collapse

Attributes inherited from BaseField

#action, #autocomplete, #block, #computable, #computed, #computed_value, #copyable, #default, #for_attribute, #for_presentation_only, #format_display_using, #format_edit_using, #format_form_using, #format_index_using, #format_new_using, #format_show_using, #format_using, #id, #label_help, #null_values, #nullable, #panel_name, #readonly, #record, #required, #size, #sortable, #stacked, #summarizable, #user, #width

Attributes included from Concerns::IsDisabled

#disabled

Attributes included from Concerns::HasHTMLAttributes

#html

Attributes included from Concerns::VisibleInDifferentViews

#show_on_edit, #show_on_index, #show_on_new, #show_on_preview, #show_on_show

Attributes included from Concerns::IsVisible

#visible

Attributes included from Concerns::IsResourceItem

#resource, #view

Instance Method Summary collapse

Methods included from Concerns::UseResource

#use_resource

Methods included from Concerns::IsSearchable

#is_searchable?

Methods inherited from BaseField

#apply_update_using, #assign_value, #attribute_id, #custom?, #custom_name?, #execute_context, #has_attribute?, #has_own_panel?, #help, #hidden_in_reflection?, #meta, #name, #name_override, #options_for_filter, #parent, #placeholder, #plural_name, #record_errors, #resolve_attribute, #resource_scoped_translation_key, #sentence_name, #shared_translation_key, #table_header_class, #table_header_label, #translated_name, #translated_plural_name, #translation_key, #type, #updatable, #width_class, #width_option

Methods included from Concerns::DomId

#model_name, #to_key

Methods included from Concerns::UseViewComponents

#component_for_view, #view_component_name, #view_component_namespace

Methods included from Concerns::IsRequired

#is_required?

Methods included from Concerns::IsDisabled

#is_disabled?

Methods included from Concerns::IsReadonly

#is_readonly?

Methods included from Concerns::HasHTMLAttributes

#get_html

Methods included from Concerns::HasDefault

#computed_default_value

Methods included from Concerns::HasHelpers

#helpers

Methods included from Concerns::VisibleInDifferentViews

#except_on, #hide_on, #initialize_views, #only_on, #post_initialize, #show_on, #show_on_create, #show_on_update, #visible_in_view?

Methods included from Concerns::IsVisible

#visible?

Methods included from Concerns::HasItemType

#is_card?, #is_collaboration?, #is_field?, #is_header?, #is_heading?, #is_panel?, #is_sidebar?, #is_tab?, #is_tab_group?, #is_tool?

Methods included from Concerns::IsResourceItem

#visible?

Methods included from Concerns::Hydration

#hydrate

Constructor Details

#initialize(id, **args, &block) ⇒ BelongsToField

Returns a new instance of BelongsToField.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/avo/fields/belongs_to_field.rb', line 73

def initialize(id, **args, &block)
  super

  @searchable = (args[:searchable] == true || args[:searchable].is_a?(Hash)) ? args[:searchable] : false
  @polymorphic_as = args[:polymorphic_as]
  @types = args[:types]
  @relation_method = attribute_id.to_s.parameterize.underscore
  @allow_via_detaching = args[:allow_via_detaching] == true
  @attach_scope = args[:attach_scope]
  @polymorphic_help = args[:polymorphic_help]
  @target = args[:target]
  @use_resource = args[:use_resource] || nil
  @can_create = args[:can_create].nil? || args[:can_create]
  @link_to_record = args[:link_to_record].present? ? args[:link_to_record] : false
  @link_to_child_resource = args[:link_to_child_resource]
end

Instance Attribute Details

#allow_via_detachingObject (readonly)

Returns the value of attribute allow_via_detaching.



67
68
69
# File 'lib/avo/fields/belongs_to_field.rb', line 67

def allow_via_detaching
  @allow_via_detaching
end

#attach_scopeObject (readonly)

Returns the value of attribute attach_scope.



68
69
70
# File 'lib/avo/fields/belongs_to_field.rb', line 68

def attach_scope
  @attach_scope
end

Returns the value of attribute link_to_child_resource.



71
72
73
# File 'lib/avo/fields/belongs_to_field.rb', line 71

def link_to_child_resource
  @link_to_child_resource
end

Returns the value of attribute link_to_record.



70
71
72
# File 'lib/avo/fields/belongs_to_field.rb', line 70

def link_to_record
  @link_to_record
end

#polymorphic_asObject (readonly)

Returns the value of attribute polymorphic_as.



65
66
67
# File 'lib/avo/fields/belongs_to_field.rb', line 65

def polymorphic_as
  @polymorphic_as
end

#polymorphic_helpObject (readonly)

Returns the value of attribute polymorphic_help.



69
70
71
# File 'lib/avo/fields/belongs_to_field.rb', line 69

def polymorphic_help
  @polymorphic_help
end

#targetObject

Returns the value of attribute target.



63
64
65
# File 'lib/avo/fields/belongs_to_field.rb', line 63

def target
  @target
end

#typesObject (readonly)

for Polymorphic associations



66
67
68
# File 'lib/avo/fields/belongs_to_field.rb', line 66

def types
  @types
end

Instance Method Details

#can_create?(final_target_resource = target_resource) ⇒ Boolean

field :user, as: :belongs_to, can_create: true Only can create when:

- `can_create: true` option is present
- target resource's policy allow creation (UserPolicy in this example)

Returns:

  • (Boolean)


305
306
307
# File 'lib/avo/fields/belongs_to_field.rb', line 305

def can_create?(final_target_resource = target_resource)
  @can_create && final_target_resource.authorization.authorize_action(:create, raise_exception: false)
end

#database_idObject



240
241
242
243
244
245
246
247
# File 'lib/avo/fields/belongs_to_field.rb', line 240

def database_id
  # If the field is a polymorphic value, return the polymorphic_type as key and pre-fill the _id in fill_field.
  return :"#{polymorphic_as}_type" if polymorphic_as.present?

  foreign_key
rescue
  attribute_id
end

#database_valueObject



135
136
137
138
139
# File 'lib/avo/fields/belongs_to_field.rb', line 135

def database_value
  target_resource.id
rescue
  nil
end

#default_nameObject



279
280
281
282
283
# File 'lib/avo/fields/belongs_to_field.rb', line 279

def default_name
  return polymorphic_as.to_s.humanize if polymorphic_as.present?

  super
end

#default_placeholderObject



90
91
92
# File 'lib/avo/fields/belongs_to_field.rb', line 90

def default_placeholder
  I18n.t("avo.choose_an_option")
end

#field_labelObject

What the user sees in the text field



112
113
114
# File 'lib/avo/fields/belongs_to_field.rb', line 112

def field_label
  label
end

#field_valueObject

The value



105
106
107
108
109
# File 'lib/avo/fields/belongs_to_field.rb', line 105

def field_value
  value.send(database_value)
rescue
  nil
end

#fill_field(record, key, value, params) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/avo/fields/belongs_to_field.rb', line 201

def fill_field(record, key, value, params)
  return record unless record.methods.include? key.to_sym

  if polymorphic_as.present?
    valid_model_class = valid_polymorphic_class params[:"#{polymorphic_as}_type"]

    record.send(:"#{polymorphic_as}_type=", valid_model_class)

    # If the type is blank, reset the id too.
    id_from_param = params["#{polymorphic_as}_id"]

    if valid_model_class.blank? || id_from_param.blank?
      record.send(:"#{polymorphic_as}_id=", nil)
    else
      target = target_resource(record:, polymorphic_model_class: value.safe_constantize)
      record_id = target.find_record(id_from_param, query: scoped_target_query(target, record, for_find: true)).id

      record.send(:"#{polymorphic_as}_id=", record_id)
    end
  else
    target = target_resource(record:)
    record_id = if value.blank?
      value
    else
      target.find_record(value, query: scoped_target_query(target, record, for_find: true)).send(reflection.association_primary_key)
    end

    record.send(:"#{key}=", record_id)
  end

  record
end

#foreign_keyObject



161
162
163
164
165
166
167
168
169
# File 'lib/avo/fields/belongs_to_field.rb', line 161

def foreign_key
  @foreign_key ||= if polymorphic_as.present?
    polymorphic_as
  elsif @record.present?
    get_model_class(@record).reflections[@relation_method].foreign_key
  elsif @resource.present? && @resource.model_class.reflections[@relation_method].present?
    @resource.model_class.reflections[@relation_method].foreign_key
  end
end

#form_field_labelObject



309
310
311
# File 'lib/avo/fields/belongs_to_field.rb', line 309

def form_field_label
  "#{id}_id"
end

#get_recordObject



275
276
277
# File 'lib/avo/fields/belongs_to_field.rb', line 275

def get_record
  @record || @resource.record
end

#id_input_foreign_keyObject



147
148
149
150
151
152
153
# File 'lib/avo/fields/belongs_to_field.rb', line 147

def id_input_foreign_key
  if is_polymorphic?
    "#{foreign_key}_id"
  else
    foreign_key
  end
end


293
294
295
296
297
298
299
# File 'lib/avo/fields/belongs_to_field.rb', line 293

def index_link_to_record
  if @link_to_record.present?
    get_record
  else
    value
  end
end


285
286
287
288
289
290
291
# File 'lib/avo/fields/belongs_to_field.rb', line 285

def index_link_to_resource
  if @link_to_record.present?
    @resource
  else
    target_resource
  end
end

#is_polymorphic?Boolean

Returns:

  • (Boolean)


155
156
157
158
159
# File 'lib/avo/fields/belongs_to_field.rb', line 155

def is_polymorphic?
  polymorphic_as.present?
rescue
  false
end

#labelObject



188
189
190
191
# File 'lib/avo/fields/belongs_to_field.rb', line 188

def label
  return if target_resource.blank?
  target_resource.new(record: value)&.record_title
end

#optionsObject



116
117
118
# File 'lib/avo/fields/belongs_to_field.rb', line 116

def options
  values_for_type
end

#polymorphic_form_field_labelObject



313
314
315
# File 'lib/avo/fields/belongs_to_field.rb', line 313

def polymorphic_form_field_label
  "#{id}_type"
end

#reflectionObject

Get the model reflection instance



178
179
180
181
182
# File 'lib/avo/fields/belongs_to_field.rb', line 178

def reflection
  reflection_for_key(attribute_id)
rescue
  nil
end

#reflection_for_key(key) ⇒ Object



171
172
173
174
175
# File 'lib/avo/fields/belongs_to_field.rb', line 171

def reflection_for_key(key)
  get_model_class(get_record).reflections[key.to_s]
rescue
  nil
end

#relation_model_classObject



184
185
186
# File 'lib/avo/fields/belongs_to_field.rb', line 184

def relation_model_class
  @resource.model_class
end

#target_resource(record: @record, polymorphic_model_class: value&.class) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/avo/fields/belongs_to_field.rb', line 249

def target_resource(record: @record, polymorphic_model_class: value&.class)
  @target_resource ||= if use_resource.present?
    use_resource
  elsif is_polymorphic?
    if polymorphic_model_class.present?
      get_resource_by_model_class(polymorphic_model_class)
    else
      return nil
    end
  else
    reflection_key = polymorphic_as || attribute_id

    reflection_object = record.class.reflect_on_association(reflection_key)

    if (link_to_child_resource || @resource&.link_to_child_resource) && value.present?
      get_resource_by_model_class(value.class.to_s)
    elsif reflection_object.klass.present?
      get_resource_by_model_class(reflection_object.klass.to_s)
    elsif reflection_object.options[:class_name].present?
      get_resource_by_model_class(reflection_object.options[:class_name])
    else
      App.get_resource_by_name reflection_key
    end
  end
end

#to_permitted_paramObject



193
194
195
196
197
198
199
# File 'lib/avo/fields/belongs_to_field.rb', line 193

def to_permitted_param
  if polymorphic_as.present?
    return [:"#{polymorphic_as}_type", :"#{polymorphic_as}_id"]
  end

  foreign_key.to_sym
end

#type_input_foreign_keyObject



141
142
143
144
145
# File 'lib/avo/fields/belongs_to_field.rb', line 141

def type_input_foreign_key
  if is_polymorphic?
    "#{foreign_key}_type"
  end
end

#valid_polymorphic_class(possible_class) ⇒ Object



234
235
236
237
238
# File 'lib/avo/fields/belongs_to_field.rb', line 234

def valid_polymorphic_class(possible_class)
  types.find do |type|
    type.to_s == possible_class.to_s
  end
end

#valueObject



94
95
96
97
98
99
100
101
102
# File 'lib/avo/fields/belongs_to_field.rb', line 94

def value
  if is_polymorphic?
    # Get the value from the pre-filled association record
    super(polymorphic_as)
  else
    # Get the value from the pre-filled association record
    super(@relation_method)
  end
end

#values_for_type(model = nil) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/avo/fields/belongs_to_field.rb', line 120

def values_for_type(model = nil)
  resource = target_resource
  resource = Avo.resource_manager.get_resource_by_model_class model if model.present?

  query = scoped_target_query(resource, get_record)

  lookup_list_limit = Avo.configuration.associations[:lookup_list_limit]

  query.all.limit(lookup_list_limit).map do |record|
    [resource.new(record: record).record_title, record.to_param]
  end.tap do |options|
    options << t("avo.more_records_available") if options.size == lookup_list_limit
  end
end

#visible_in_reflection?(reflection = nil) ⇒ Boolean

When displayed inside a has_one/has_many reflection, hide this field if it points back to the parent record (i.e., it is the inverse of the reflection). This mirrors the filtering logic in Avo::Concerns::HasItems#get_fields.

Returns:

  • (Boolean)


320
321
322
323
324
325
326
327
328
329
330
# File 'lib/avo/fields/belongs_to_field.rb', line 320

def visible_in_reflection?(reflection = nil)
  return true if reflection.nil?
  return true unless respond_to?(:foreign_key)
  return true unless reflection.inverse_of.present?
  return true unless reflection.inverse_of.respond_to?(:foreign_key)

  inverse_fk = reflection.inverse_of.foreign_key
  self_fk = is_polymorphic? ? self.reflection&.foreign_key : foreign_key

  inverse_fk != self_fk
end