Hopping Relationships
Our sample application
has the setup Employee > Position > Department
, where one of the
positions is the current_position
. What if we wanted to change this to
Employee > Department
, hiding everything about positions
under-the-hood?
Let’s start by saying an Employee
has many Department
s. Here’s the
spec:
Start by defining the association:
And you’ll get this error:
Which makes sense - if this is a has_many
association, we’d expect
DepartmentResource to filter by employee_id
. Though in our case we
don’t have that as a foreign key, we can still implement the
employee_id
filter:
In order to find Department
s by an employee_id
, we need to join the
positions
table which has the employee_id
column.
We now get this error:
Let’s say our URL is /employees?include=departments
. We’ve fetched all
the Employee
s and all the Department
s, now we need to associate each
Department
with its relevant Employee
. Normally we’d do that by
looking at the employee_id
foreign key on Department
, but this
scenario has non-standard logic. Let’s tell Graphiti how to select
relevant Department
s for a given Employee
:
There’s one final step - because we’re assigning a department to an employee, we have to make sure that accessor exists:
And that’s it! Our test now passes.
There’s a little bit of sleight-of-hand above though. Our filter joins
to the positions
table, and our assignment iterates over departments
and calls department.positions
. If we don’t eager load, we’ll cause
an N+!!
There are two solutions to this. The first is to simple change .joins
to .eager_load
:
This ensures that not only are we joining on the positions
table,
we’ll eagler load the positions
relationship and avoid the N+1.
If you’re a stickler, though, you may have a nitpick. For one, if
we’re hitting /departments?filter[employee_id]
directly there is no
need to eager load positions
because we’re never associating to an
Employee
. We’re paying a performance penalty when we don’t have to.
OK, let’s keep our filter .joins
. We just have to tell Graphiti to
switch it to .eager_load
when sideloading through EmployeeResource
:
The pre_load
hook fires after we’ve built up the scope, but before we
resolve it (before actually firing the query). It yields a proxy
object that we can modify - here we’re modifying the scope to eager load
positions.
It’s up to you if you care about this scenario - you may want to start
with .eager_load
and only embrace to the extra work of pre_load
when
you really need it.
The trick to these customizations is to think in Links. Resources
connect to each other with URLs - what would the query parameters of the
URL be? In this case, filter?[employee_id]=123
. After that, we just
have to define how to associate relevant objects. Even with complex
associations hopping several levels, the same logic applies.
See the final code here.