View Source ExTeal.Fields.ManyToMany (ExTeal v0.27.0)

The ManyToMany field corresponds to a many_to_many ecto relationship. For example, let's assume a User schema has a many_to_many relationship with a Role schema. We can add the relationship to our User resource like so:

alias ExTeal.Fields.ManyToMany

ManyToMany.make(:roles)

Customizing Index Tables

Let's assume we have a User schema with a many_to_many relationship with a Role schema. The pivot schema has a :primary boolean field. We want to display the Role's name and the User's email and the primary boolean field on the index table of 'Roles' for a 'User'. We can do this by defining:

ManyToMany.make(:roles, Role)
|> ManyToMany.with_pivot_fields([ExTeal.Fields.Boolean.make(:primary)])

The relationship table, attach and edit-attached interfaces now all a user to manage the 'primary' field which only exists on the pivot table.

Customizing Index Tables

By default, the ManyToMany field produces a ManyToManyBelongsTo field on the index table. This field appears as a BelongsTo field, providing the title for the linked resource and a link to the detail page of the linked asset, in the previous example 'Role'. There are times where you might need to customize the index table, overriding the ManyToManyBelongsTo and providing a unique list of fields to display.

Enter with_index_fields/2 and with_index_query/2. By default, Teal queries for both the related resource and the join to the current resource via some of the meta data associated with the underlying Ecto.Assoc struct. The results are then loaded in a custom resource lookup select query. When extending the default many-to-many functionality, the query must return a ExTeal.Resource.pivot_resource() type.

Example

Assume a User has many to many with Org through a Membership schema. Membership has a role field and we want to display some extra information in the Org many-to-many table for a user. We could define that as:

ManyToMany.make(:orgs, Org)
|> ManyToMany.with_index_fields([
  ExTeal.Fields.Text.make(:name)
  ExTeal.Fields.Boolean.make(:active),
  ExTeal.Fields.Select.make(:org_type),
  ExTeal.Fields.Number.make(:member_count) |> ExTeal.Field.virtual()
])
|> ManyToMany.with_index_query(fn query, _assoc, _resource_id ->
  query
  |> Ecto.Query.join(:left, [o], m in assoc(o, :memberships))
  |> Ecto.Query.select(query, [o, x, m], %{
    _row: %{
      name: o.name,
      active: o.active,
      org_type: o.org_type,
      member_count: selected_as(count(m.id), :member_count)
    },
    _pivot: x,
    pivot: true
  })
  |> Ecto.Query.group_by([o, x], [o.id, x.org_id])
end)
|> ManyToMany.with_pivot_fields([ExTeal.Fields.Select.make(:role)])

What a query, what a pipe. There are some conditions here. This functionality is only available when a many_to_many assocation is defined with a join_through schema. If the many to many is joined only through the table name, you may be able to customize the index fields by defining a with_index_fields/2 and ignoring the with_index_query/2 function. If you need to customize the query, you can do so by simply returning a full schema and avoiding the ExTeal.Resource.pivot_resource() type.

Summary

Functions

Link to this function

apply_options_for(field, model, conn, type)

View Source

Callback implementation for ExTeal.Field.apply_options_for/4.

Callback implementation for ExTeal.Field.default_sortable/0.

Callback implementation for ExTeal.Field.filterable_as/0.

Link to this function

index_fields(queried_resource, rel_name, related_resource)

View Source
Link to this function

make(name, label \\ nil)

View Source

Callback implementation for ExTeal.Field.make/2.

Link to this function

make(relationship_name, module, label \\ nil)

View Source

Callback implementation for ExTeal.Field.sanitize_as/0.

Link to this function

sortable_by(field, pivot_field_name)

View Source
Link to this function

value_for(field, model, method)

View Source

Callback implementation for ExTeal.Field.value_for/3.

Link to this function

with_index_fields(many_to_many_field, index_fields)

View Source
@spec with_index_fields(ExTeal.Field.t(), [ExTeal.Field.t()]) :: ExTeal.Field.t()
Link to this function

with_index_query(many_to_many_field, index_query_fn)

View Source
@spec with_index_query(
  ExTeal.Field.t(),
  (Ecto.Query.t(), struct(), any() -> Ecto.Query.t())
) ::
  ExTeal.Field.t()
Link to this function

with_pivot_fields(field, pivot_fields)

View Source