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
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
.
Callback implementation for ExTeal.Field.make/2
.
Callback implementation for ExTeal.Field.sanitize_as/0
.
Callback implementation for ExTeal.Field.value_for/3
.
Functions
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
.
Callback implementation for ExTeal.Field.make/2
.
Callback implementation for ExTeal.Field.sanitize_as/0
.
Callback implementation for ExTeal.Field.value_for/3
.
@spec with_index_fields(ExTeal.Field.t(), [ExTeal.Field.t()]) :: ExTeal.Field.t()
@spec with_index_query( ExTeal.Field.t(), (Ecto.Query.t(), struct(), any() -> Ecto.Query.t()) ) :: ExTeal.Field.t()