laravel-json-api icon indicating copy to clipboard operation
laravel-json-api copied to clipboard

Add Eloquent adapter query hooks

Open lucianholt97 opened this issue 6 years ago • 9 comments

I was wondering where is the best place to add some custom joins to the index query? Currently, I am overwriting the queryAll method in the Adapter but it doesn‘t feel right. Is there any function or hook which is better suited?

Thanks in advance!

lucianholt97 avatar Jan 31 '19 16:01 lucianholt97

Hey! queryAll would seem like the right place. The thing to be aware of is that method is also used when the resource appears in a to-many relationship (the $query will be an Eloquent relationship query).

Do you have any suggestions as to how it would be better to add joins? What is your join being used for?

lindyhopchris avatar Feb 01 '19 15:02 lindyhopchris

Thank you! I would like to query relationship counts and for example the customer_value (the sum of all related orders for a customer) in an efficient way. Additionally, sometimes it is more efficient to add the customer_name as an attribute directly to the order. That way I don't need a separate query for the related customer when I only need the name. I'd love to see a function, like the filter() function, where I can easily extend the query without overwriting anything.

lucianholt97 avatar Feb 01 '19 15:02 lucianholt97

Hey @lindyhopchris ! Do you have any update to this issue or do you plan to include a separate function? Because it seems like the queryAll() or with() method does not affect the read request for a single resource. I would like to avoid to duplicate my logic for the read method.

lucianholt97 avatar Mar 01 '19 17:03 lucianholt97

Hi! I'm not adverse to adding a new hook in, but I'm not sure what this hook would be, and under what circumstances it would get called? That would need to be clear for me to add it in - do you have any suggestions?

One thing to mention is that in these kind of scenarios, we've implemented it by putting all these values in a related resource that the client can choose to include or not. So for example, we'd have customers resource, and that would have all the attributes to do with the order's customer. Then that gets included only if the client asks for it to be included. Resources don't need to match your Eloquent models. The thing that makes me think that might apply in your scenario is this comment:

Additionally, sometimes it is more efficient to add the customer_name as an attribute directly to the order.

The purpose of the JSON API spec is to give the client control over what it is asking for. How are you determining when to put the customer name attribute on the order or not? Whether attributes are in the response or not is defined in the spec using the fields query param.

(Sorry for all the question - just trying to figure out what the best way to approach this use case is!)

lindyhopchris avatar Mar 05 '19 09:03 lindyhopchris

Hey @lindyhopchris, we would need a function (e.g. join()) which is called from the AbstractAdapter's query() and the find() function and should have the $query as an argument. You would then override the join() method in the Adapter and like:

public function join($query) {
$query->join('customers', 'orders.customer_id', '=', 'customers.id')
->addSelect(DB::raw("CONCAT(customers.given_name, ' ', customers.family_name) AS customer_name"));
}

To use the newly selected attribute, you can simply add the customer_name to the Schema and the fieldset selection from the client will work just fine. The client would even be able to sort by those new columns after adding them to the $sortColumns array and implementing #303 .

Another important use case for me is to add custom selection, like concatenating the customer name to the full_name on the fly or add counts to my resource. Therefore the method could also be named addSelect().

lucianholt97 avatar Mar 11 '19 21:03 lucianholt97

Ok. I think that join is too specific a use-case name for a hook that could be used for multiple use-cases.

It's more about how you are querying the resource, so the hooks I think should be:

  • querying: always called
  • queryingOne: called only when querying a single resource e.g. /comments/123
  • queryingMany: called when querying zero-to-many resources, e.g. /comments and /posts/1/comments

lindyhopchris avatar Mar 13 '19 11:03 lindyhopchris

I totally agree with you. The naming and separation of the hook looks perfect. Thank you in advance for the implementation! 🙏

lucianholt97 avatar Mar 13 '19 19:03 lucianholt97

Hi @lindyhopchris, are there any updates on this issue? Would really appreciate a querying hook.

lucianholt97 avatar Oct 01 '19 17:10 lucianholt97

No update I'm afraid - combination of too much other work going on, and me being in two minds about adding these querying hooks, or supporting them in some other way.

Let me have another think about these.

lindyhopchris avatar Oct 03 '19 08:10 lindyhopchris