I have an optional include on a transformer like so:
public function includeContributingUsers(Conversation $conversation)
{
$contributors = $this->conversationService->getContributors($conversation);
return $this->collection($contributors, new UserTransformer);
}
This works fine if I am transforming a single conversation as an Item. But when I try to transform a Collection of conversations I get:
Call to undefined method Illuminate\Database\Query\Builder::contributingUsers()
It is looking for the relationship on the model, but it is supposed to be calling the includeContributingUsers() method on the transformer.
This Fractal issue describes the same problem: https://github.com/thephpleague/fractal/issues/240
Another: https://github.com/dingo/api/issues/1096#issuecomment-230889539
I believe this happens because of implicit eager loading by Dingo when using "includes" and when a collection is encountered:
https://github.com/dingo/api/blob/master/src/Transformer/Adapter/Fractal.php#L89
Not sure of a way around this though, other than defining the method on your model or probably disabling automatic eager loading and handling it yourself when needed.
I had this issue a while back as well, I had to add the method to the model and call a fake relationship
@jarednipper @georaldc and @tr33m4n are correct. You'd have to call the fractal adapter, and disable the eager loading feature.
@tr33m4n Same here. Added a duplicate / fake relationship. Striving for cleaner code, this is not ideal.
Would be nice if we were able to specify which includes should be eager-loaded right within each transformer to be able to have includes that don't resolve to a relationship directly.
Maybe as protected $eagerLoadedIncludes = ['user', 鈥;
Any updates on this?
If anyone is still interested in resolving this issue, I have made a quick fix using the idea of @JonasDoebertin .
I've added public $lazyLoadedIncludes = ['foo']; into my transformer, and changed the mergeEagerLoads method of Dingo\Api\Transformer\Abstract\Fractal into :
/**
* Get includes as their array keys for eager loading.
*
* @param \League\Fractal\TransformerAbstract $transformer
* @param string|array $requestedIncludes
*
* @return array
*/
protected function mergeEagerLoads($transformer, $requestedIncludes)
{
$includes = array_merge($requestedIncludes, $transformer->getDefaultIncludes());
$eagerLoads = [];
foreach ($includes as $key => $value) {
$eagerLoads[] = is_string($key) ? $key : $value;
}
if (property_exists($transformer, 'lazyLoadedIncludes'))
$eagerLoads = array_diff($eagerLoads, $transformer->lazyLoadedIncludes);
return $eagerLoads;
}
Note the public scope of the attribute : put it protected or private would force to add a getter in Fractal.
I chose to list the lazy loaded includes rather than the eager loaded includes mainly for compatibility reasons, as I already have a huge amount of transformers and doing that would have lead me to add this attribute everywhere. In my case, I just have to add it when I have 'includes' that I don't want to be eager loaded.
Resubmitted the fix in #1592
Merged
Most helpful comment
Would be nice if we were able to specify which includes should be eager-loaded right within each transformer to be able to have includes that don't resolve to a relationship directly.
Maybe as
protected $eagerLoadedIncludes = ['user', 鈥;