Api: Transformer optional include must be a model attribute

Created on 25 Jul 2016  路  9Comments  路  Source: dingo/api

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

discussion

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', 鈥;

All 9 comments

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

Was this page helpful?
0 / 5 - 0 ratings