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, #help, #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?, #hidden_in_reflection?, #meta, #name, #name_override, #options_for_filter, #parent, #placeholder, #plural_name, #record_errors, #resolve_attribute, #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
89
90
# File 'lib/avo/fields/belongs_to_field.rb', line 73

def initialize(id, **args, &block)
  args[:placeholder] ||= I18n.t("avo.choose_an_option")

  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)


303
304
305
# File 'lib/avo/fields/belongs_to_field.rb', line 303

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

#database_idObject



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

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



133
134
135
136
137
# File 'lib/avo/fields/belongs_to_field.rb', line 133

def database_value
  target_resource.id
rescue
  nil
end

#default_nameObject



277
278
279
280
281
# File 'lib/avo/fields/belongs_to_field.rb', line 277

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

  super
end

#field_labelObject

What the user sees in the text field



110
111
112
# File 'lib/avo/fields/belongs_to_field.rb', line 110

def field_label
  label
end

#field_valueObject

The value



103
104
105
106
107
# File 'lib/avo/fields/belongs_to_field.rb', line 103

def field_value
  value.send(database_value)
rescue
  nil
end

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



199
200
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
# File 'lib/avo/fields/belongs_to_field.rb', line 199

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



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

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



307
308
309
# File 'lib/avo/fields/belongs_to_field.rb', line 307

def form_field_label
  "#{id}_id"
end

#get_recordObject



273
274
275
# File 'lib/avo/fields/belongs_to_field.rb', line 273

def get_record
  @record || @resource.record
end

#id_input_foreign_keyObject



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

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


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

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


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

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

#is_polymorphic?Boolean

Returns:

  • (Boolean)


153
154
155
156
157
# File 'lib/avo/fields/belongs_to_field.rb', line 153

def is_polymorphic?
  polymorphic_as.present?
rescue
  false
end

#labelObject



186
187
188
189
# File 'lib/avo/fields/belongs_to_field.rb', line 186

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

#optionsObject



114
115
116
# File 'lib/avo/fields/belongs_to_field.rb', line 114

def options
  values_for_type
end

#polymorphic_form_field_labelObject



311
312
313
# File 'lib/avo/fields/belongs_to_field.rb', line 311

def polymorphic_form_field_label
  "#{id}_type"
end

#reflectionObject

Get the model reflection instance



176
177
178
179
180
# File 'lib/avo/fields/belongs_to_field.rb', line 176

def reflection
  reflection_for_key(attribute_id)
rescue
  nil
end

#reflection_for_key(key) ⇒ Object



169
170
171
172
173
# File 'lib/avo/fields/belongs_to_field.rb', line 169

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

#relation_model_classObject



182
183
184
# File 'lib/avo/fields/belongs_to_field.rb', line 182

def relation_model_class
  @resource.model_class
end

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



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

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



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

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



139
140
141
142
143
# File 'lib/avo/fields/belongs_to_field.rb', line 139

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

#valid_polymorphic_class(possible_class) ⇒ Object



232
233
234
235
236
# File 'lib/avo/fields/belongs_to_field.rb', line 232

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

#valueObject



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

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



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

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)


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

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