How to use "where" clause nested n relationships? - laravel

There are lot available reference that I went through before asking the question. And it seems all of them were just one to two layer relationship only. While my case is different because it can have n layer of relationship, so it's not really helpful in my end.
I have here nested relationship of Person.
class Person extends Model
{
public function children()
{
return $this->hasMany(Person::class, 'parent_id', 'id')->with('children');
}
}
This would result to this sample Model structure data. (show snippet below)
[
{
name: 'Don Pepe',
gender: 'male',
children: [
{
name: 'Juan',
gender: 'male',
children: [
{
name: 'Vito',
gender: 'male',
children: ... and so on, so forth
},
...
],
},
...
],
},
...
]
I would like to ask how to filter these data. For example like, if I would like to filter the male gender? We could just simply where clause the parent but for children of children, how we gonna where clause it?
I doubt it could not be done in Laravel Eloquent, only in collection. Please let me know your thoughts.
Laravel nested relationships
https://laracasts.com/discuss/channels/laravel/eloquent-nested-relations-with-where-clause

Related

How to hide columns when using "with" in Laravel?

How would I hide unwanted data for a cleaner response in laravel when using with statements.
For example, let's pretend I have a single post. That post has comments, and then comments have multiple tags related to each comment.
So I have below to illustrate the idea.
I have a model called Post
I then have a relationship in Post model that has comments relationship, ie
public function PostComments()
{
return $this->hasMany('App\Models\Post\Comments', 'postId', 'id');
}
I also have a model called PostComments
I then have this relationship in there.
public function PostCommentsTags()
{
return $this->hasMany('App\Models\Posts\PostCommentTags', 'postCommentId', 'id');
}
Then in my controller I have
$post = Post::with(
'PostComments',
'PostComments.PostCommentsTags',
)->first();
This works wonderfully, I get a response like this.
{
title: 'This is a title',
description: 'Description',
comments:[
{
id: 1,
comment: 'this is a comment',
commentTags:[{
id: 1,
tagName: 'emotion',
tagValue: 'angry',
},
{
id: 10,
tagName: 'timeOfDay',
tagValue: 'morning',
}],
},
{
id: 2,
comment: 'this is a comment too',
commentTags:[{
id: 7,
tagName: 'emotion',
tagValue: 'happy',
},
{
id: 9,
tagName: 'timeOfDay',
tagValue: 'evening',
}],
},
{
id: 3,
comment: 'Too many comments now',
commentTags:[{
id: 12,
tagName: 'emotion',
tagValue: 'angry',
},
{
id: 14,
tagName: 'timeOfDay',
tagValue: 'evning',
}],
}
]
}
But I want to get rid of a lot of clutter, ie id. (the real thing is a lot more complex than this example, so has a lot of clutter and unneeded info)
So I have this below, which hides all the ids for comments, this works great!
$post->Comments->makeHidden(["id"]);
But how do I hide it for Comment tags too? (I want no ids in my json)
I have tried below, but it does not work? How do you access nested models when using the with statement. I can't seem to find anything to help.
$post->Comments->CommentsTags->makeHidden(["id"]);
Spent a bit to much time on this now so time to ask for help. Please help :)
Best wishes.
Argghh finally figured it out. If you want to remove unwanted data, you loop through it like this.
foreach($post->Comments as $comment){
$comment->CommentsTags->makeHidden(['id','created_at','updated_at']);
}
I guess its a nice way to have hidden attributes without having it the model.

Graphql: How can I solve the N + N problem?

After having implemented dataloader in the respective resolvers to solve the N+1 problem, I also need to be able to solve the N+N problem.
I need a decently efficient data loading mechanism to get a relation like this:
{
persons (active: true) {
id,
given_name,
projects (active: true) {
id,
title,
}
}
}
I've created a naive implementation for this, returning
{
persons: [
{
id: 1,
given_name: 'Mike'
projects: [
{
id: 1,
title: 'API'
},
{
id: 2,
title: 'Frontend'
}
]
}
{
id: 2,
given_name: 'Eddie'
projects: [
{
id: 2,
title: 'Frontend'
},
{
id: 3,
title: 'Testing'
}
]
}
]
}
In SQL the underlying structure would be represented by a many many to many relationship.
Is there a similiar tool like dataloader for solving this or can this maybe even be solved with dataloader itself?
The expectation with GraphQL is that the trip to the database is generally the fastest thing you can do, so you just add a resolver to Person.projects that makes a call to the database. You can still use dataLoaders for that.
const resolvers = {
Query: {
persons(parent, args, context) {
// 1st call to database
return someUsersService.list()
},
},
Person: {
projects(parent, args, context) {
// this should be a dataLoader behind the scenes.
// Makes second call to database
return projectsService.loadByUserId(parent.id)
}
}
}
Just remember that now your dataLoader is expecting to return an Array of objects in each slot instead of a single object.

GraphQL ancestor data context

I need to be able to create a catalog for a given entity and then somewhere in the grandchildren I want to use the catalog IDs and resolve them.
think of this (very simplified) data model
type Entity {
id: ID
componentCatalog: [Component]
child: Child
}
type Child {
grandChildren: [GrandChild]
}
type GrandChild {
components: [Component]
}
in the NoSQL db I would store this as:
{
id: 'abc',
componentCatalog: [ { id: 1, title: 'a' }, { id: 2, title: 'b' }],
child: {
grandChildren: [
{
componentIds: [1]
},
{
componentIds: [1,2]
}
]
}
}
and I would like to resolve the IDs to the components that are stored in the catalog of the Entity
however how do I get to the data from the grandchildren? Parent is just a child, do I have to save the catalog into the GQL context? If so then how? If there are multiple entities in the query how do I know which Grandchild belongs to which entity?
Thanks a lot in advance

How to upsert nested tables values in Hasura GraphQL?

wanted to ask if it is possible to upsert nested objects? for example, if i have a 'Users' table and a 'Students' table, and I'm inserting a new User(with a taken id), i want to update all fields (using on_conflict and update_columns) including the fields in the 'Students' table.
Basically replace all user's fields except the primary key.
mutation($UsersData: [core_users_insert_input!]!) {
insert_core_users(
objects: $UsersData
on_conflict: {
constraint: core_users_id_unique
update_columns: [first_name, last_name, gender]
}
) {
affected_rows
}
}
The update_column array should include fields from the 'Students' table but i can't figure it out.
It is possible, relevant documentation is here: https://hasura.io/docs/1.0/graphql/manual/mutations/upsert.html#upsert-in-nested-mutations
It is possible to use on_conflict key on any level (top, or nested) where you want to resolve updating an existing record.
mutation upsert_author_article {
insert_author(
objects: [
{
name: "John",
articles: {
data: [
{
title: "Article 3",
content: "Article 3 content"
}
],
on_conflict: {
constraint: article_title_key,
update_columns: [content]
}
}
}
]
) {
affected_rows
}
}

graphql query SQL parent child relationship

I have a postgres table that represents a hierarchy with a parent child table:
Table (Categories):
id name parentId
1 CatA null
2 CatB null
3 CatC 1
4 CatD 1
5 CatE 3
desired result:
categories:
[
{
name: "CatA",
children: [
{
name: "CatC",
children: [
{
name: "CatE",
children: []
}]
},
{
name: "CatD",
children: []
}
],
},
{
name: "CatB",
children: []
}
]
The problem is that I don't know how many levels there are, so I can't query something like:
category {
name
parent {
name
parent {
name
...
You can actually achieve the potential infinite recursion with GraphQL. So it doesn't mind if you don't know how deep you go with your schema.
I was able to reproduce your desired result with this schema. I hope it might helps you:
const categories = [
{
name: 'CatA',
children: [
{
name: 'CatC',
children: [
{
name: 'CatE',
children: []
}]
},
{
name: 'CatD',
children: []
}
]
},
{
name: 'CatB',
children: []
}
];
const categoryType = new GraphQLObjectType({
name: 'CategoryType',
fields: () => ({
name: { type: GraphQLString },
children: { type: new GraphQLList(categoryType) }
})
});
const queryType = new GraphQLObjectType({
name: 'RootQuery',
fields: () => ({
categories: {
type: new GraphQLList(categoryType),
resolve: () => categories
}
})
});
And I got this result:
Please notice that I define field property as a function rather than an plain object. The field property defined as object would failed and wouldn't allow you to use categoryType variable in the fields, because in the time of execution it doesn't exist.
fields: () => ({
...
})
One of the difficulties of using GraphQL on top of a SQL database is reconciling the two paradigms. GraphQL is hierarchical. SQL databases are relational. There isn't always a clear mapping between the two.
We open-sourced a framework, Join Monster, that has an opinionated way of setting up your schemas. If you do so, it automatically generates the SQL queries for you. It was built with the idea of relations in its core. In theory you can achieve arbitrary depth in your GraphQL queries.

Resources