laravel-query-builder icon indicating copy to clipboard operation
laravel-query-builder copied to clipboard

AllowedFields are not taking into consideration the relationship fields

Open PharaohAsh opened this issue 3 years ago • 9 comments

Using V4.0.1

When we do the following:

QueryBuilder::for(Post::class)
    ->allowedFields('author.id', 'author.name')
    ->allowedIncludes('author');

And query the endpoint like so:

GET /posts?include=author&fields[author]=id,name

It appears to list all the fields of the author and not just id and name.

I think the issue lies here ->get($table) where it's grabbing the $table instead of the $relation from the collection of fields.

PharaohAsh avatar Nov 01 '21 03:11 PharaohAsh

I too have just ran into the similar issue (v4.0.1 as well)

I tried applying a resource to each of the keys within the main resource. ie.

    public function toArray($request)
    {
        // return parent::toArray($request);
        return [
            'id' => $this->id,
            'name' => $this->name,
            'first_include' => (new FirstResource($this->first)),
            'second_include' => (new SecondCollection($this->second)),
        ];
    }

But it appears, when I call the $this->first it pulls in the entire record, thus does not obey the include'd requirement within the URL.

It would be awesome if any included/related models apply their resource or resource collections.

wattsie avatar Nov 25 '21 00:11 wattsie

FYI: I have found a work around.

By checking the relationsToArray I have found which additional includes have been defined.

Then I just check and convert into either Resource or ResourceCollection as required.

class PrimaryResource extends JsonResource
{
    public function toArray($request)
    {
        $ret_val = [
            'id' => $this->id,
            'name' => $this->name,
        ];
        // Include resource
        if (array_key_exists('first_include', $this->relationsToArray())) {
            $ret_val['first_include'] = (new FirstIncludeResource($this->first_include));
        }
        // Include a resource collection
        if (array_key_exists('second_include', $this->relationsToArray())) {
            $ret_val['second_include'] = (new SecondIncludeCollection($this->second_include));
        }
        return $ret_val;
    }
}

Wonder if you could do the same with the "field" limits.

wattsie avatar Nov 25 '21 01:11 wattsie

I just ran into this issue as well today. Same thing specifying fields on a relationship is not added to the select statement. If the field is for the root object, then it does work.

?include=author&fields[post]=id&fields[author]=id,name

outputs

select post.id from post ...

omitting the ?field[post]=id just results in a select * ...

jpgilchrist avatar Nov 30 '21 17:11 jpgilchrist

Same problem here. Using version 4.0.1 too.

claudiofabiao avatar Dec 18 '21 18:12 claudiofabiao

You may try to use illuminatech/data-provider instead. It does provide the ability to specify selected fields for the relations.

klimov-paul avatar Jan 28 '22 12:01 klimov-paul

Hello, I am facing the same issue - is there any solution?

Will upgrading to 5v help?

rezaffm avatar May 07 '22 09:05 rezaffm

As pointed out by @PharaohAsh initially the problem is with how the table name is inferred using the relation string:

QueryBuilder::for(Post::class)
  ->allowedIncludes('author')
  ->allowedFields('author.id', 'author.name')

// Relation author is transformed to table 'authors'
// AddsFieldsToQuery.php
$table = Str::plural(Str::snake($relation)); // TODO: make this configurable

A workaround could be to specify the included fields with this transformation in mind:

QueryBuilder::for(Post::class)
  ->allowedIncludes('author')
  ->allowedFields('authors.id', 'authors.name')

One thing to keep in mind: you always need to include the key for 1-to-1 relations and the foreign_key for 1-to-many relations for Eloquent to be able to correctly associate the retrieved results. Stackoverflow: Query specific columns for relations

@rezaffm Upgrading to v5 will currently not help with this issue.

I'll try to open a PR to fix this issue in a couple of days.

dominikb avatar May 07 '22 16:05 dominikb

Great stuff, much appreciated.

Would be great if it works for 4v as well then, not ready for Laravel 9 yet :-)

rezaffm avatar May 08 '22 05:05 rezaffm

This issue can't be fixed yet?

../Concerns/AddsFieldsToQuery.php

$table = Str::plural(Str::snake($relation)); // TODO: make this configurable

$fields = $this->request->fields()->mapWithKeys(function ($fields, $table) {
    return [$table => $fields];
})->get($table);

Why not rollback like the previous version? Is it related to the others?

$fields = $this->request->fields()->mapWithKeys(function ($fields, $relation) {
    return [Str::camel($relation) => $fields];
})->get($relation);

beonami avatar Jul 23 '22 18:07 beonami

Dear contributor,

because this issue seems to be inactive for quite some time now, I've automatically closed it. If you feel this issue deserves some attention from my human colleagues feel free to reopen it.

spatie-bot avatar Nov 24 '22 11:11 spatie-bot

I could solve this issue using, from the HTTP query, the relationship name in plural and adding the fields that keep the relationships.

For example:

image

https://myproject.test/query
?limit=5
&include=items,company,subcompany
&fields[orders]=id,created_at,updated_at,company_id,subcompany_id
&fields[items]=id,order_id,qty,price
&fields[companies]=id,name
&fields[subcompanies]=id,name

You can see more details about this scenario here: https://github.com/spatie/laravel-query-builder/issues/605#issuecomment-1370335460

anayarojo avatar Jan 06 '23 19:01 anayarojo

As pointed out by @PharaohAsh initially the problem is with how the table name is inferred using the relation string:

QueryBuilder::for(Post::class)
  ->allowedIncludes('author')
  ->allowedFields('author.id', 'author.name')

// Relation author is transformed to table 'authors'
// AddsFieldsToQuery.php
$table = Str::plural(Str::snake($relation)); // TODO: make this configurable

A workaround could be to specify the included fields with this transformation in mind:

QueryBuilder::for(Post::class)
  ->allowedIncludes('author')
  ->allowedFields('authors.id', 'authors.name')

One thing to keep in mind: you always need to include the key for 1-to-1 relations and the foreign_key for 1-to-many relations for Eloquent to be able to correctly associate the retrieved results. Stackoverflow: Query specific columns for relations

@rezaffm Upgrading to v5 will currently not help with this issue.

I'll try to open a PR to fix this issue in a couple of days.

Regarding the issue of passing the foreign_key for 1-to-many relations is there is any solution for not passing it?

zeinabmsalem avatar Nov 12 '23 15:11 zeinabmsalem

Spatie, please to update your docs for avoid to lost so much time with this, since this example here will not works with default config: https://spatie.be/docs/laravel-query-builder/v5/features/selecting-fields#content-selecting-fields-for-included-relations

Is missing to mention to use plural when selecting fields for a relation like post->author

thewebartisan7 avatar May 19 '24 06:05 thewebartisan7

Spatie, please to update your docs for avoid to lost so much time with this, since this example here will not works with default config: https://spatie.be/docs/laravel-query-builder/v5/features/selecting-fields#content-selecting-fields-for-included-relations

Is missing to mention to use plural when selecting fields for a relation like post->author

// fields[authors] and NOT fields[author]
// GET /posts?include=author&fields[authors]=id,name

QueryBuilder::for(Post::class)
   // authors and NOT author
    ->allowedFields('authors.id', 'authors.name')
    ->allowedIncludes('author');

thewebartisan7 avatar May 19 '24 06:05 thewebartisan7