Class: CrudComponents::Fields::BelongsToField

Inherits:
Base
  • Object
show all
Defined in:
lib/crud_components/fields/belongs_to_field.rb

Overview

belongs_to / has_one: nil-safe link via the target's label. The filter (belongs_to only) accepts both the target's identify_by value (what the select submits) and free text matched against the target's label — the name shown in the cell — one param, two OR-combined parameterized subqueries.

Constant Summary

Constants inherited from Base

CrudComponents::Fields::Base::NON_EDITABLE_COLUMNS

Instance Attribute Summary

Attributes inherited from Base

#facets, #model, #name, #options

Instance Method Summary collapse

Methods inherited from Base

#apply_filter, #apply_filter_facet, #column, #custom_header?, #declared_preloads, #editable?, #editable_permitted?, #filter_control, #filter_facet, #filter_includes_null?, #filterable?, #form_partial, #group_label, #header, #header_actions, #human_name, #initialize, #nullable?, #permitted?, #picker_label, #range_filter?, #render_block, #renderer, #renderer_options, #sort_facet, #sortable?, #typed_filter, #value

Constructor Details

This class inherits a constructor from CrudComponents::Fields::Base

Instance Method Details

#apply_derived_filter(scope, value: nil) ⇒ Object



63
64
65
66
67
68
69
# File 'lib/crud_components/fields/belongs_to_field.rb', line 63

def apply_derived_filter(scope, value: nil, **)
  return scope unless value

  identified = scope.where(name => target.where(target_structure.identify_by => value))
  searched = like_subquery(scope, value)
  searched ? identified.or(searched) : identified
end

#apply_sort(scope, dir) ⇒ Object



42
43
44
45
46
# File 'lib/crud_components/fields/belongs_to_field.rb', line 42

def apply_sort(scope, dir)
  return super if sort_facet

  scope.left_joins(name).reorder(target.arel_table[sort_column].public_send(dir))
end

#default_editable?Boolean

── forms ──────────────────────────────────────────────────────────── Assigned via the foreign key; the select submits real ids (forms are POST bodies, not shareable URLs — unlike the filter, which uses identify_by).

Returns:

  • (Boolean)


88
# File 'lib/crud_components/fields/belongs_to_field.rb', line 88

def default_editable? = reflection.belongs_to? && !reflection.polymorphic?

#default_rendererObject



9
# File 'lib/crud_components/fields/belongs_to_field.rb', line 9

def default_renderer = :association

#derived_filter_controlObject

select (a dropdown of all targets) below select_limit rows, else free text. Counted per render, not memoized: the field instance lives on the process-cached Structure, so a memoized count would freeze at its boot-time value and render the wrong control once the table grows past the limit. One COUNT per filter-row render is negligible next to rendering the table.



53
54
55
# File 'lib/crud_components/fields/belongs_to_field.rb', line 53

def derived_filter_control
  target.count <= CrudComponents.config.select_limit ? :select : :text
end

#derived_filterable?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/crud_components/fields/belongs_to_field.rb', line 33

def derived_filterable?
  reflection.belongs_to? && !reflection.polymorphic?
end

#derived_sortable?Boolean

Sortable by the column behind the target's label — the name shown in the cell — reached with a join. A block label, or a label that isn't a real column, has no SQL ordering, so the column isn't sortable then.

Returns:

  • (Boolean)


40
# File 'lib/crud_components/fields/belongs_to_field.rb', line 40

def derived_sortable? = sort_column.present?

#eager_loadObject

Load the association, nesting the target's identity_preloads (its label's own association deps) plus any per-column preload: so the target's label never N+1s. e.g. { order: %i[customer training] }. A polymorphic belongs_to has no single target class, so we can't nest its label's preloads — just preload the association itself (Rails groups it by type); the cell still renders each record's label and links it at runtime.



77
78
79
80
81
82
# File 'lib/crud_components/fields/belongs_to_field.rb', line 77

def eager_load
  return [name] if reflection.polymorphic?

  nested = (target_structure.identity_preloads + declared_preloads).uniq
  [nested.empty? ? name : { name => nested }]
end

#filter_choices(_query = nil) ⇒ Object



57
58
59
60
61
# File 'lib/crud_components/fields/belongs_to_field.rb', line 57

def filter_choices(_query = nil)
  structure = target_structure
  target.all.map { |record| [structure.label_for(record).to_s, record.public_send(structure.identify_by)] }
        .sort_by(&:first)
end

#form_choicesObject



92
93
94
95
# File 'lib/crud_components/fields/belongs_to_field.rb', line 92

def form_choices
  structure = target_structure
  target.all.map { |record| [structure.label_for(record).to_s, record.id] }.sort_by(&:first)
end

#form_controlObject



89
# File 'lib/crud_components/fields/belongs_to_field.rb', line 89

def form_control = :belongs_to

#group_modelObject

Picker grouping: a belongs_to/has_one column anchors its target's group (polymorphic has no single target, so it groups under its own model).



21
# File 'lib/crud_components/fields/belongs_to_field.rb', line 21

def group_model = reflection.polymorphic? ? model : target

#permit_paramObject



90
# File 'lib/crud_components/fields/belongs_to_field.rb', line 90

def permit_param = reflection.foreign_key.to_sym

#reflectionObject



11
12
13
# File 'lib/crud_components/fields/belongs_to_field.rb', line 11

def reflection
  @reflection ||= model.reflect_on_association(name)
end

#search_spec_entryObject

Default ?q= reaches the target's label (the name shown in the cell). Skipped for polymorphic (no single target) or a block/columnless label.



29
30
31
# File 'lib/crud_components/fields/belongs_to_field.rb', line 29

def search_spec_entry
  name if !reflection.polymorphic? && target_structure.label_field_name
end

#targetObject



15
16
17
# File 'lib/crud_components/fields/belongs_to_field.rb', line 15

def target
  reflection.klass
end

#target_structureObject



23
24
25
# File 'lib/crud_components/fields/belongs_to_field.rb', line 23

def target_structure
  Structure.for(target)
end