Comments (15)
Do you have a ContainerCollectionQuery
class? If so, what's in that?
from laravel.
Thanks @lindyhopchris for the fast reply,
I dont use any **CollectionQuery at all, the only thing related may be the relationship method on the Container model
class Container extends Model
{
// ...
/**
* A container may have many rows
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function rows()
{
return $this->hasMany(Row::class, 'container_id', 'id')
->select('rows.*')
->join('products', 'rows.product_id', '=', 'products.id')
->orderBy('products.is_visible', 'desc')
->orderBy('products.is_snoozed')
->orderBy('weight');
}
}
but If I remove the custom sorting the results is the same without any "meta".
from laravel.
Does it work if you remove the select()
from that?
Basically that isn't a typical Eloquent relationship there - so I'd suspect something to do with that is a problem.
Does it work if you just change it to:
public function rows()
{
return $this->hasMany(Row::class, 'container_id', 'id');
}
Which is a more typical Eloquent relationship? FYI I wouldn't recommend doing all those things on the relationship in the rows()
method - it should be up to calling code that is querying the relationship as to which columns it wants to select, whether it needs to do a join, what order it wants things in, etc.
from laravel.
If I use this typical relation:
/**
* A container may have many rows
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function rows()
{
return $this->hasMany(Row::class, 'container_id', 'id');
}
I still dont get any "meta" field on the response:
https://domain.com/v1/containers/115?withCount=rows
this is the full response btw
{
"jsonapi": {
"version": "1.0"
},
"links": {
"self": "https://domain.com/v1/containers/115"
},
"data": {
"type": "containers",
"id": "115",
"attributes": {
"label": "prova",
"weight": 3,
"livemode": false,
"container_id": null,
"image_path": "assets/containers/115/y5f3IBxosTZbzCPuUQo7CP9r8UDR1NKlswOQKqNb.jpg",
"image_url": "https://domain.s3.eu-central-1.amazonaws.com/assets/containers/115/y5f3IBxosTZbzCPuUQo7CP9r8UDR1NKlswOQKqNb.jpg?...blablabla",
"description": null,
"attributes": null,
"created_at": "2024-01-22T15:57:01.000000Z",
"updated_at": "2024-02-11T17:45:51.000000Z"
},
"relationships": {
"rows": {
"links": {
"related": "https://domain.com/v1/containers/115/rows"
}
},
"products": {
"links": {
"related": "https://domain.com/v1/containers/115/products"
}
},
"containers": {
"links": {
"related": "https://domain.com/v1/containers/115/containers"
}
}
},
"links": {
"self": "https://domain.com/v1/containers/115"
}
}
}
from laravel.
So this is working in the automated tests, so I'm going to need you to debug as it's impossible for me to say based on this information. Here's an automated test for it which is why I know it works:
laravel/tests/dummy/tests/Api/V1/Posts/ReadTest.php
Lines 212 to 231 in 567263e
Some things I'd need to know:
Do you have a custom controller action?
Do you have a ContainerQuery
class? (got that wrong earlier when I asked about the ContainerCollectionQuery
class.)
If you don't have a custom controller action, can you debug the model here:
To see whether it has the count information in it? I believe Eloquent would call it rows_count
or something like that.
from laravel.
I was looking at the custom controller, I have a custom controller for Containers:
<?php
namespace App\Http\Controllers\Api\V1;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Document\Error;
use LaravelJsonApi\Laravel\Http\Controllers\Actions;
use App\Http\Controllers\Controller;
use App\Models\Business\Container;
class ContainersController extends Controller
{
use Actions\FetchMany;
use Actions\FetchOne;
use Actions\Store;
use Actions\Update;
use Actions\FetchRelated;
use Actions\FetchRelationship;
use Actions\UpdateRelationship;
use Actions\AttachRelationship;
use Actions\DetachRelationship;
/**
* Delete a container and everything contained in it, including sub-containers.
*
* @return \Illuminate\Http\Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function deleteAll(Request $request, Container $container)
{
$container->deleteAll();
return response()->json([], 204);
}
/**
* Delete only the container.
*
* @return \Illuminate\Http\Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function deleteContainerOnly(Request $request, Container $container)
{
// Can't delete container if sub-containers are present
if ($container->containers()->exists()) {
return Error::make()->setStatus(400)->setDetail('Resource could not be deleted.');
}
$container->delete();
return response()->json([], 204);
}
/**
* Delete the container and all the products contained.
*
* @return \Illuminate\Http\Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function deleteContainer(Request $request, Container $container)
{
// Can't delete container if sub-containers are present
if ($container->containers()->exists()) {
return Error::make()->setStatus(400)->setDetail('Resource could not be deleted.');
}
$container->deleteAll();
return response()->json([], 204);
}
}
I checked with another resource that hasnot a custom controller but I still dont get the "meta"
from laravel.
So the FetchOne
action will be handling the https://domain.com/v1/containers/115?withCount=rows
request, as you're fetching a single resource with id 115
.
It would help if you can debug the model returned here:
In its attributes, does it have a rows_count
value?
from laravel.
Just to clarify something - I'm not saying there isn't a bug in the package. But when I have a passing automated test for exactly this scenario, and I don't have access to your application, then I need you to provide more debug insight, because otherwise I'm just totally guessing as to what the problem might be!
from laravel.
Ok, I got it, yes there is a rows_count
attribute on the model
App\Models\Business\Container {#2928 ▼ // vendor/laravel-json-api/laravel/src/Http/Controllers/Actions/FetchOne.php:59
#connection: "mysql"
#table: "containers"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:12 [▼
"id" => 115
"price_list_id" => 4
"label" => "prova"
"weight" => 3
"livemode" => 0
"container_id" => null
"image_path" => "assets/containers/115/y5f3IBxosTZbzCPuUQo7CP9r8UDR1NKlswOQKqNb.jpg"
"description" => null
"attributes" => null
"created_at" => "2024-01-22 15:57:01"
"updated_at" => "2024-02-11 17:45:51"
"rows_count" => 3
]
#original: array:12 [▶]
#changes: []
#casts: array:4 [▶]
#classCastCache: []
#attributeCastCache: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
+usesUniqueIds: false
#hidden: []
#visible: []
#fillable: array:6 [▶]
#guarded: array:1 [▶]
}
Maybe the problem is on the resource? I use **Resource.php on every model
from laravel.
Thanks so much for debugging this - it's great to confirm that the value is in the model as that means this package is loading the data properly.
My next questions was going to be: are you using a resource class? Which you've already answered!
If you're using your own resource class, then you'd need to assign the meta to the relationship yourself:
https://laraveljsonapi.io/docs/3.0/resources/relationships.html#meta
Out of interest, why are you using resource classes? Generally it's not required.
from laravel.
Well done! Im glad we found out what's the problem.
We use resources cause we need to control what will be exposed to an external client by the api.
Now I have a question,
by using resources it seems to me that some of the "countable relationship" modifiers/methods are useless because I have to manage the meta field by myself.
$this->relation('rows')->withoutSelfLink()->withMeta(['rows_count' => $this->rows_count]),
Ex. This will always include the meta for the relation, how do I include the meta only when requested by the client?
Something like this but seems a bit "procedural"
/**
* Get the resource's relationships.
*
* @param Request|null $request
* @return iterable
*/
public function relationships($request): iterable
{
return [
$this->relation('rows')->withoutSelfLink()->withMeta(function () use ($request) {
if ($request->has('withCount')) {
$countables = explode(',', $request->query('withCount'));
if (in_array('rows', $countables)) {
return ['rows_count' => $this->rows_count];
}
}
}),
$this->relation('products')->withoutSelfLink(),
$this->relation('containers')->withoutSelfLink(),
];
}
from laravel.
Yeah you have to manage the meta field by yourself as you have chosen to manage the resource by yourself. If you want the package to manage the meta field you should not use a resource class.
As you've implemented a specific resource class, you shouldn't do anything "generic" like you're proposing, because it would be inefficient. In your resource class, you know that the row count might be there, so all you need to do is:
$this->relation('rows')->withMeta(fn () => ['count' => $this->rows_count]);
You can obviously put logic in there if you only want the count to show if rows_count
on the model is an integer. You really really do not need to be checking the withCount
on the request, because the model won't have rows_count
as an attribute if it wasn't requested.
As a side note, I really wouldn't recommend using withoutSelfLink()
. The spec specifies the self link should be there.
I'm probably going to have to start deprecating and removing some of the usages of the resource class as it's enabling people to deviate from the spec. Which is bad, because the whole point of the package is to implement the spec.
from laravel.
You might find it's better not to bother with the resource class, and instead use this feature:
https://laraveljsonapi.io/docs/3.0/schemas/relationships.html#relationship-serialization
I think if you use that (i.e. remove the resource class and instead rely on serialisation customisation in the schema class) then the count meta should automatically be added. You'd need to check that though.
from laravel.
$this->relation('rows')->withoutSelfLink()->withMeta(fn () => is_numeric($this->rows_count) ? ['rows_count' => $this->rows_count] : null),
one line code
Thank you very much for the advice you are giving me. I will ask the developers to do an in-depth analysis in order to simplify the code.
For the sake of "withoutSelfLink", from a frontend point of view, it is useless. With the frontend we must have the resource available and avoid as many calls to the API as possible. I decided not to show it: 1. to simplify the response json, 2. not to give fe/nders a chance to cause confusion.
For the point about resources, I find it is cleaner to have the code well structured and divided rather than having to manage everything on the schema but this is a stylistic preference more than anything else. The important thing is to be consistent in our decisions.
Thanks for what you do and keep it up!
from laravel.
Sure no problem and thanks for the explanation.
I'll leave this ticket open as I need to add something to the docs on countable relationships, making it clear you have to add the meta manually if you have a resource class.
from laravel.
Related Issues (20)
- Customize top-level meta when fetching related resource HOT 1
- Incorrect data serialization within ArrayHash (Hashable trait) HOT 1
- [question] Validation on filter values HOT 4
- route model binding (Variable name cannot be longer than 32 characters in route pattern) HOT 9
- How to create resource belongsTo resource in one request? HOT 3
- Controllers cannot be placed in a custom namespace HOT 5
- Rules in a a ResourceRequest are not checked against the Schema's defined fields; no exception thrown for an incorrect Request HOT 2
- [Feature Request] Nesting Filters HOT 1
- Included morph to many data is `null` HOT 5
- Updating a model's relationships where the policy denies updating the relationship gives 200 OK, but doesn't update the relationship HOT 5
- Impossible to change route model binding? HOT 5
- Proper docs for v4.0 and Laravel 11.x HOT 1
- Exception Handling with Laravel 11.x HOT 8
- Filtering inlcuded resources HOT 1
- Error when handling validation failures HOT 5
- QueryToMany, QueryToOne, HasMany, HasOne, BelongsTo and BelongsToMany may fail to find a relation if declared with resolveRelationUsing HOT 3
- Add Where Any / All Filters HOT 1
- [Question] Event Sourcing with Eloquent Resources? HOT 2
- Failing Installation in LA 10.x HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from laravel.