A relationship exists between a source resource and a destination resource. These are defined in the `relationships` block of the source resource. For example, if `MyApp.Tweet` is the source resource, and `MyApp.User` is the destination resource, we could define a relationship called `:owner` like this:
Each of these relationships has a `source` resource and a `destination` resource with a corresponding attribute on the source resource (`source_attribute`), and destination resource (`destination_attribute`). Relationships will validate that their configured attributes exist at compile time.
You don't need to have a corresponding "reverse" relationship for every relationship, i.e if you have a `MyApp.Tweet` resource with `belongs_to :user, MyApp.User` you aren't required to have a `has_many :tweets, MyApp.Tweet` on `MyApp.User`. All that is required is that the attributes used by the relationship exist.
A `belongs_to` relationship means that there is an attribute (`source_attribute`) on the source resource that uniquely identifies a record with a matching attribute (`destination_attribute`) in the destination. In the example above, the source attribute on `MyApp.Tweet` is `:owner_id` and the destination attribute on `MyApp.User` is `:id`.
By default, the `source_attribute` is defined as `:<relationship_name>_id` of the type `:uuid` on the source resource and the `destination_attribute` is assumed to be `:id`. You can override the attribute names by specifying the `source_attribute` and `destination_attribute` options like so:
-`d:Ash.Resource.Dsl.relationships.belongs_to|define_attribute?` to define it yourself
-`d:Ash.Resource.Dsl.relationships.belongs_to|attribute_type` to modify the default type
-`d:Ash.Resource.Dsl.relationships.belongs_to|attribute_writable?` to make the source attribute `private?: false, writable?: true` (both are not the default)
A `has_one` relationship means that there is a unique attribute (`destination_attribute`) on the destination resource that identifies a record with a matching unique attribute (`source_resource`) in the source. In the example above, the source attribute on `MyApp.User` is `:id` and the destination attribute on `MyApp.Profile` is `:user_id`.
A `has_one` is similar to a `belongs_to` except the reference attribute is on
A `has_many` relationship means that there is a non-unique attribute (`destination_attribute`) on the destination resource that identifies a record with a matching unique attribute (`source_resource`) in the source. In the example above, the source attribute on `MyApp.User` is `:id` and the destination attribute on `MyApp.Tweet` is `:user_id`.
A `has_many` relationship is similar to a `has_one` because the reference attribute exists on the destination resource. The only difference between this and `has_one` is that the destination attribute is not unique, and therefore will produce a list of related items. In the example above, `:tweets` corresponds to a list of `MyApp.Tweet` records.
A `many_to_many` relationship can be used to relate many source resources to many destination resources. To achieve this, the `source_attribute` and `destination_attribute` are defined on a join resource. A `many_to_many` relationship can be thought of as a combination of a `has_many` relationship on the source/destination resources and a `belongs_to` relationship on the join resource.
For example, consider two resources `MyApp.Tweet` and `MyApp.Hashtag` representing tweets and hashtags. We want to be able to associate a tweet with many hashtags, and a hashtag with many tweets. To do this, we could define the following `many_to_many` relationship:
It is convention to name this resource `<source_resource_name><destination_resource_name>` however this is not required. The attributes on the join resource must match the `source_attribute_on_join_resource` and `destination_attribute_on_join_resource` options on the `many_to_many` relationship. The relationships on the join resource are standard `belongs_to` relationships, and can be configured as such. In this case, we have specified that the `:tweet_id` and `:hashtag_id` attributes form the primary key for the join resource, and that they cannot be `nil`.
Given a single record or a set of records, it is possible to load their relationships by calling the `load` function on the record's parent API. For example:
```elixir
# user = %User{...}
YourApi.load(user, :tweets)
# users = [%User{...}, %User{...}, ....]
YourApi.load(users, :tweets)
```
This will fetch the tweets for each user, and set them in the corresponding `tweets` key.
```elixir
%User{
...
tweets: [
%Tweet{...},
%Tweet{...},
...
]
}
```
See `c:Ash.Api.load/3` for more information.
### In the query
The following will return a list of users with their tweets loaded identically to the previous example:
```elixir
User
|> Ash.Query.load(:tweets)
|> YourApi.read()
```
At present, loading relationships in the query is fundamentally the same as loading on records. Eventually, data layers will be able to optimize these loads (potentially including them as joins in the main query).