laravelgraphlaravel-lighthouse

How to make input with unique rule in mutation scheme?


I have input with unique rule:

input VoteCategoryInput {
    name: String! @rules(apply: ["string", "min:2", "max:255", "unique:vote_categories,name"]),
    active: Boolean!,
    in_subscriptions: Boolean!,
    meta_description: String,
    meta_keywords: [String],
}

extend type Mutation {
   createVoteCategory(
       input: VoteCategoryInput! @spread
   ): VoteCategory! @create

   updateVoteCategory(
       id: ID!,
       input: VoteCategoryInput! @spread
   ): VoteCategory! @update

   deleteVoteCategory(id: ID! @whereKey): VoteCategory @delete
}

and running mutation for existing rows = 10:

  mutation {
    updateVoteCategory(
      id: 10
      input: {
        name: "UPDATED Vote Category 11"
        active: false
        in_subscriptions: false
        meta_keywords: ["meta_keywords UPDATED 1", "meta_keywords UPDATED2"]
      }
    ) {
        id
        name
        slug
        in_subscriptions
        meta_description
        meta_keywords
        created_at
       updated_at
    }
  }

I got error with unique rule break : in checking sql tracement I do not see condition id <> 10 - so this checking statement find itself. I tried to put unique rule out of input :

input VoteCategoryInput {
    active: Boolean!,
    in_subscriptions: Boolean!,
    meta_description: String,
    meta_keywords: [String],
}

extend type Mutation {
   createVoteCategory(
       name: String! @rules(apply: ["string", "min:2", "max:255", "unique:vote_categories,name"]),
       input: VoteCategoryInput! @spread
   ): VoteCategory! @create

   updateVoteCategory(
       id: ID!,
       name: String! @rules(apply: ["string", "min:2", "max:255"]),
       input: VoteCategoryInput! @spread
   ): VoteCategory! @update

   deleteVoteCategory(id: ID! @whereKey): VoteCategory @delete
}

and with mutation :

  mutation {
    updateVoteCategory(
      id: 10
      name: "Vote Category name #8"
      input: {
        active: false
        in_subscriptions: false
        meta_keywords: ["meta_keywords UPDATED 1", "meta_keywords UPDATED2"]
      }
    ) {
        id
        name
        slug
        in_subscriptions
        meta_description
        meta_keywords
        created_at
       updated_at
    }
  }

But Now I got

SQLSTATE[23000]: Integrity constraint violation 

if name "Vote Category name #8" was found with other id - this mutation did not use any unique check.

How cat it be fixed ? I would prefer 1st way with name rule inside of input... I did not find any similar option in docs at https://lighthouse-php.com/6/api-reference/directives.html#rules

UPDATED # 1:

I cretated a new validator with command :

php artisan lighthouse:validator VoteCategoryInputValidator

and got php file app/GraphQL/Validators/VoteCategoryInputValidator.php in which I can fill rules and messages methods which I can fill as I do in laravel app.

Next I need to assign VoteCategoryInputValidator to my mutation and I tried to add line in grqph file in input block:

input VoteCategoryInput {
    name: String! @rules(apply: ["string", "min:2", "max:255", "unique:vote_categories,name"]),
    active: Boolean!,
    in_subscriptions: Boolean!,
    meta_description: String,
    meta_keywords: [String],
    @rules(apply: ["App\\GraphQL\\Validators\\VoteCategoryInputValidator"])
}

I found reference to it at docs https://lighthouse-php.com/6/security/validation.html#single-arguments in “Custom Validation Rules” blocks

But I got error :

Object of class App\\GraphQL\\Validators\\VoteCategoryInputValidator could not be converted to string",

Is it invalid syntax ?

UPDATED # 2:

With syntax :

   updateVoteCategory(
       id: ID!,
       input: VoteCategoryInput! @spread
   ): VoteCategory!
    @validator(class: "app\\GraphQL\\Validators\\UpdateVoteCategoryValidator")
    @update

I return array of rules in UpdateVoteCategoryValidator, but now I get list of errors for all fields :

  "errors": [
    {
      "message": "Validation failed for the field [updateVoteCategory].",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "updateVoteCategory"
      ],
      "extensions": {
        "validation": {
          "name": [
            "The name field is required."
          ],
          "active": [
            "The active field is required."
          ],
          "in_subscriptions": [
            "The in subscriptions field is required."
          ],
          "input.name": [
            "The input.name has already been taken."
          ]
        },

    

I suppose that is as I use input, but how can I use input in UpdateVoteCategoryValidator ?


Solution

  • I suggest using the @validator like the lighthouse docs. In there is an example with Rule::unique('users', 'name')->ignore($this->arg('id'), 'id'); on the UpdateUserInputValidator. With the @rule directive they don't have any example with unique and the ID as you want.

    You access the validator key values with $this->arg('key').

    schema

    input VoteCategoryInput {
        name: String!,
        active: Boolean!,
        in_subscriptions: Boolean!,
        meta_description: String,
        meta_keywords: [String],
    }
    
    extend type Mutation {
       createVoteCategory(
           input: VoteCategoryInput! @spread
       ): VoteCategory! 
           @validator(class: "App\\GraphQL\\Validators\\CreateVoteCategoryValidator")
           @create
    
       updateVoteCategory(
           id: ID!,
           input: VoteCategoryInput! @spread #if you use the @spread, you don't need to use the key 'input.field' on the validator class because will "flat" the input.
       ): VoteCategory! 
           @validator(class: "App\\GraphQL\\Validators\\UpdateVoteCategoryValidator")
           @update
    
       deleteVoteCategory(
           id: ID! @whereKey
       ): VoteCategory 
           @delete
    }
    

    The UpdateVoteCategoryValidator rules file:

     public function rules(): array
        {
            return [
                'id' => [
                     'required',
                 ],
                'name' => [
                    'required',
                    Rule::unique('vote_categories', 'name')->ignore($this->arg('id')), # add to the ignore() 2nd parameter if the field has a different name.
                ],
                'active' => [
                    'required',
                ],
                'meta_description' => [
                    'required',
                ],       
                'meta_keywords' => [
                    'required',
                ],
            ];
        }
    

    The CreateVoteCategoryValidator rules file:

     public function rules(): array
        {
            return [
                'name' => [
                    'required',
                    Rule::unique('vote_categories', 'name'),
                ],
                'active' => [
                    'required',
                ],
                'meta_description' => [
                    'required',
                ],       
                'meta_keywords' => [
                    'required',
                ],
            ];
        }