I’m working on a feature that lets users add custom fields to records. For this, I have three resources:
FieldType
with columns name
and description
.
Field
with columns name
, field_type_id
, description
, and options
.
FieldResourceRecord
with columns field_id
, resource_name
, resource_record_id
, and value
.
Any resource needing custom fields will have a one-to-many relationship with FieldResourceRecord
where resource_name
matches ResourceWithManyFields
.
Currently, I’m defining the has_many :fields, MyApp.Fields.FieldResourceRecord
relationship on each resource. Is there a way to centralize this relationship, similar to how we handle preparations
, changes
, and manual_actions
?
You can centralize it by writing an extension, although for just one relationship it may be sort of a nuclear option
defmodule YourApp.CustomFields do
use Spark.Dsl.Extension,
transformers: [YourApp.CustomFields.Transformers.AddCustomFields]
end
defmodule YourApp.CustomFields.Transformers.AddCustomFields do
use Spark.Dsl.Transformer
def transform(dsl) do
dsl
|> Ash.Resource.Builder.add_new_relationship(:has_many, :fields, MyApp>Fields.FieldResourceRecord)
end
end
Then you can do
use Ash.Resource, extensions: [YourApp.CustomFields]
1 Like
Thank you @zachdaniel, how can I add the following filters to that relationship in this extension. I could not determine how to do it in the documentation: Ash.Resource.Builder — ash v3.4.5
has_many :fields, Hr.Fields.FieldResourceRecord do
destination_attribute :resource_record_id
# where `Hr.People.Person` is determined automatically as the module running this extension
filter expr(resource_name == "Hr.People.Person")
end
Here is how far I could go. Still unable to apply filters in the transformers add_new_relationship
function.
defmodule MyApp.Extensions.CustomerFields.Transformers.AddCustomerFields do
use Spark.Dsl.Transformer
def transform(dsl) do
Ash.Resource.Builder.add_new_relationship(
dsl,
:has_many,
:fields,
Hr.Fields.FieldResourceRecord,
destination_attribute: :resource_record_id
)
|> dbg()
end
end
Make sure to import Ash.Expr
import Ash.Expr
and then you can use it like so:
Ash.Resource.Builder.add_new_relationship(
dsl,
:has_many,
:fields,
Hr.Fields.FieldResourceRecord,
destination_attribute: :resource_record_id,
filter: expr(resource_name == "Hr.People.Person")
)
EDIT: I’m pretty sure that the filter
option should “just work” like that, but let me know if not.
1 Like
It was not working. I fixed it like below:
defmodule Hr.Extensions.CustomFields.Transformers.AddCustomFields do
use Spark.Dsl.Transformer
import Ash.Expr
@doc """
Automatically add relationship to the FieldResourceRecord on any given resource
"""
def transform(dsl) do
Ash.Resource.Builder.add_new_relationship(
dsl,
:has_many,
:fields,
Hr.Fields.FieldResourceRecord,
destination_attribute: :resource_record_id,
filters: expr(resource_name == get_resource_name(dsl))
)
end
defp get_resource_name(dsl) do
dsl.persist.module
|> Atom.to_string()
|> String.replace("Elixir.", "")
end
end
Also the error message says it requires filter options even if it has been passed. So, I figured out that the issues is in the error messages.
The error message should report missing destination_attribute
instead of destination_field
, and it should report missing filters
instead of filter
.
Error showing missing filter instead of filters
Error showing destination field
missing nstead of destination_attribute
1 Like