As far as I can see this isn't possible but I thought I'd ask in case I've missed something.
I have 2 entities, Rule and Condition
One Rule can have many Conditions
What I'm trying to achieve is adding a new Rule with one or more Conditions in a single request
I've tried the following via POST
{
"property": "id",
"conditions": [
{
"position": 1
}
],
"title": "getId",
"position": 1
}
Conditions have a rule_id column, but I obviously can't set it at this point as it hasn't been created yet
That request gives me the error
Nested documents for attribute "conditions" are not allowed. Use IRIs instead.
Of course, I can't use an IRI because I haven't created the condition yet, and I can't create the condition first as it would fail foreign key checks
So am I right in thinking this isn't possible or am I just doing it wrong?
Thanks in advance
You need to add the same serialization group to both the Rule and Condition classes, as explained here.
Also you must add the cascade={"persist"} attribute to the #OneToMany annotation of the Rule::conditions property.
Something like that:
// src/EntityRule.php
#[ORM\Entity(repositoryClass: RuleRepository::class)]
#[ApiResource(
collectionOperations: [
"post" => [
"denormalization_context" => [ "groups" => ["write:rule"]]
]
]
)]
class Rule
{
// ...
#[ORM\OneToMany(mappedBy: "rule", targetEntity: Condition::class, cascade: ["persist"])]
#[Groups(["write:rule"])]
/**
* #var Condition[]
*/
private Collection $conditions;
// ...
}
// src/Entity/Condition.php
#[ORM\Entity(repositoryClass: ConditionRepository::class)]
#[ApiResource(
collectionOperations: [
"post" => [
"denormalization_context" => ["groups" => ["write:condition"]
]
]
]
)]
class Condition
{
// ...
#[ORM\Column(nullable=false, unique=false)]
#[Groups(["write:condition", "write:rule"])]
private int $position;
// ...
}
Related
I am using nuwave/lighthouse:^5.0, and I am trying to create a mutation for an entity which have a belongsTo relationship. The thing is that in my input I am using a sanitizer directive to transform from string to id, but after that when Laravel gets the properties, it shows errors with the validation of the class. In addition, I debug the directive code and it works correctly.
Error
"errors": [
{
"message": "The given data was invalid.",
"extensions": {
"validation": {
"content_type_id": [
"The content type id field is required."
]
},
"category": "validation"
},
Input
input CreateContentInput {
content_type: CreateContentTypeBelongsTo!
.....
input CreateContentTypeBelongsTo {
connect: ID! #typeuuid(model: "App\\ContentType")
create: CreateContentTypeInput
update: UpdateContentTypeInput
}
Model
class Content extends Model
{
protected $rules = [
'content_type_id' => 'required|integer|is_main_content_type',
];
/**
* #return BelongsTo
*/
public function contentType(): BelongsTo
{
return $this->belongsTo(ContentType::class);
}
Any idea will be appreciated
Finally, after some days, I found the issue.
The error come from the main Input definition:
input CreateContentInput {
content_type: CreateContentTypeBelongsTo!
}
I was following a company standard that says that we need to use the properties always in ** snake case ** although they are relationships. So looks like Lighthouse uses always ** camel case ** for relationships.
The solution was add the ** rename ** property to the input. So the right input should be:
input CreateContentInput {
content_type: CreateContentTypeBelongsTo! #rename (attribute: "contentType")
}
I hope this could help someone else.
First, I will show the state stored in mongodb.
As you can see, it is a structure with a list called replies in a list called comments. And inside replies there is an array called likes.
comments : [
Object1 : {
replies : [
likes : [
0 : {},
1 : {}
]
]
},
Object2 : {
replies : [
likes : [
0 : {},
1 : {}
]
]
}
]
What I want to do here is to insert/subtract a value only from the likes array inside a specific replies structure. I'm currently using Spring boot and have tried the following:
Query query = new Query();
Criteria criteria = Criteria.where("_id").is(new ObjectId(postId))
.andOperator(Criteria.where("comments")
.elemMatch(Criteria.where("_id").is(new ObjectId(commentId))
.andOperator(Criteria.where("replies")
.elemMatch(Criteria.where("_id").is(new ObjectId(replyId)))
)
)
);
query.addCriteria(criteria);
Update update = new Update();
if (state) {
// remove user id
update.pull("comments.$[].replies.$.likes", new ObjectId(userId));
} else {
// add user id
update.push("comments.$[].replies.$.likes").value(new ObjectId(userId));
}
mongoTemplate.updateFirst(query, update, MyEntity.class);
It is an operation to add or remove userId according to boolean state. As a result of the attempt, up to a specific comment is found, but userId is unconditionally entered in the first likes list of the replies list inside the comment. What I want is to get into the likes list inside a specific reply. Am I using the wrong parameter in update.push()? I would appreciate it if you could tell me how to solve it.
Not a direct answer to your question as I'm not experienced with spring's criteria builder, but here's how you would do it in mongo directly, which might help you to figure it out:
You could define arrayfilters allowing you to keep track of the corresponding indices of each comments and replies. You can then use those indices to push a new object at the exact matching indices:
db.collection.update({
_id: "<postId>"
},
{
$push: {
"comments.$[comments].replies.$[replies].likes": {
_id: "newlyInsertedLikeId"
}
}
},
{
arrayFilters: [
{
"comments._id": "<commentId>"
},
{
"replies._id": "<replyId>"
}
]
})
Here's an example on mongoplayground: https://mongoplayground.net/p/eNdDXXlyi2X
here is my request
{
"formulations": [
{
"formulation_id": null,
"formulation_custom_name": "test",
"meal_time_id": null,
"remark": "demo1"
},
{
"formulation_id": 3,
"formulation_custom_name": "asd",
"meal_time_id": 2,
"remark": "demo"
}
]
}
validation rule
'formulations.*.formulation_id' => 'required_with:formulations.*.formulation_custom_name'
working properly for first object i.e formulation_id is required when formulation_custom_name is present
"errors": {
"formulations.0.formulation_id": [
"The formulations.0.formulation_id field is required when formulations.0.formulation custom name is present."
]
}
now my question is exactly opposite from above scenario i.e validate
formulation_custom_name required when formulation_id is null or not present
like
'formulations.*.formulation_id' => 'required_without:formulations.*.formulation_custom_name'
but this is not working for this request like this
{
"formulations": [
{
"formulation_id": 6,
"formulation_custom_name": "test",
"meal_time_id": null,
"remark": "demo1"
}
}
thanks in advance
required_without rule in Validator checks for field existence/presence and not for empty value or null value.
So you need required_if here, and use like below
'formulations.*.formulation_id' => 'required_if:formulations.*.formulation_custom_name,' // it will work for blank and null
Or if you need more complex condition then you can use requiredIf custom Rule
or if you need vice versa then write like below
'formulations.*.formulation_custom_name' => 'required_without:formulations.*.formulation_id'
Document Link
Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars
Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars