Search code examples
phpgraphqlsilverstripe-4

silverstripe-graphql mutations using alternate ID


I'm attempting to write a GraphQL mutation to do updates. In this application, a DataObject called QuickPossession contains a second ID which corresponds to a 3rd-party database. I need to update QuickPossessions according to that second ID. Here is the model:

<?php
namespace Organization\HomeBuilderSite\DataObjects;

use SilverStripe\ORM\DataObject;
use SilverStripe\GraphQL\Scaffolding\Interfaces\ScaffoldingProvider;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\SchemaScaffolder;

class QuickPossession extends DataObject implements ScaffoldingProvider {
  private static $table_name = 'QuickPossession';

  private static $db = [
    'SecondID' => 'Int',
    'Title' => 'Varchar(255)',
    'Address' => 'Varchar(255)',
    'SquareFeet' => 'Int',
  ];

  public function provideGraphQLScaffolding(SchemaScaffolder $scaffolder) {
    $scaffolder
      ->type(QuickPossession::class)
        ->addFields([
          'SecondID', 
          'ID',
          'Title',
          'Address', 
          'SquareFeet'
        ])
        ->operation(SchemaScaffolder::CREATE)
          ->end()
        ->operation(SchemaScaffolder::READ)
          ->end()
        ->operation(SchemaScaffolder::UPDATE)
          ->end()
        ->end();
    return $scaffolder;
  }
}

And in my mysite.yml

SilverStripe\GraphQL\Controller:
  schema:
    scaffolding_providers:
      - Organization\HomeBuilderSite\DataObjects\QuickPossession

I am able to successfully run the following mutation:

mutation UpdateQuickPossession($ID: ID!, $Input: QuickPossessionUpdateInputType!) {
    updateQuickPossession(ID: $ID, Input: $Input) {
        Title
    }
}

With these variables:

{
    "ID": "3",
    "Input": {
        "Title": "Example Home Name",
        "Address": "123 Smith Street"
    }
}

Which correctly gives this output:

{
    "data": {
        "updateQuickPossession": {
            "Title": "Example Home Name"
        }
    }
}

But I can't figure out how to do the same using the SecondID as the unique identifier. I've tried the following:

mutation UpdateQuickPossession($ID: SecondID!, $Input: QuickPossessionUpdateInputType!) {
    updateQuickPossession(SecondID: $ID, Input: $Input) {
        Title
    }
}

With these variables:

{
    "SecondID": "1457",
    "Input": {
        "Title": "Example Home Name",
        "Address": "123 Smith Street"
    }
}

Which produces this error:

{
    "data": null,
    "errors": [
        {
            "message": "Unknown type \"SecondID\".",
            "locations": [
                {
                    "line": 1,
                    "column": 37
                }
            ]
        },
        {
            "message": "Unknown argument \"SecondID\" on field \"updateQuickPossession\" of type \"Mutation\".",
            "locations": [
                {
                    "line": 2,
                    "column": 24
                }
            ]
        },
        {
            "message": "Field \"updateQuickPossession\" argument \"ID\" of type \"ID!\" is required but not provided.",
            "locations": [
                {
                    "line": 2,
                    "column": 2
                }
            ]
        }
    ]
}

My question is this: do mutations only work with the primary ID? Is it possible to use alternate unique identifiers?

Or am I missing something else? Perhaps this is a limitation of using the scaffold approach to setting DataObjects up for GraphQL queries? Many thanks :)

I've been referring to the silverstripe-graphql and graphql.org documentation.


Solution

  • I was not able to do this using SchemaScaffolder::UPDATE, but I was able to write an arbitrary mutation as per the silverstripe-graphql documentation.

    <?php
    namespace Organization\HomeBuilderSite\Pages;
    
    use Organization\HomeBuilderSite\Page;
    use SilverStripe\GraphQL\Scaffolding\Interfaces\ScaffoldingProvider;
    use SilverStripe\GraphQL\Scaffolding\Scaffolders\SchemaScaffolder;
    
    class QuickPossession extends Page implements ScaffoldingProvider {
      private static $table_name = 'QuickPossession';
      private static $db = [
        'SecondID' => 'Varchar(30)',
        'Title' => 'Varchar(255)',
        'Address' => 'Varchar(255)',
        'SquareFeet' => 'Int',
      ];
    
      public function provideGraphQLScaffolding(SchemaScaffolder $scaffolder) {
        $scaffolder
          // Setting up QuickPossessions for queries
          ->type(QuickPossession::class)
            ->addFields([
              'SecondID', 
              'ID',
              'Title',
              'Address', 
              'SquareFeet',
              'Community'
            ])
            ->operation(SchemaScaffolder::READ)
              ->end()
    
          ->mutation('updateQuickPossession', QuickPossession::class)
            ->addArgs([
              'SecondID' => 'ID!',
              'Title' => 'String',
            ])
            ->setResolver(function ($obj, $args, $context) {
              $item = QuickPossession::get()->filter('SecondID',$args['SecondID'])->first();
              $item->SecondID = $args['SecondID'];
              $item->Title = $args['Title'];
              $item->write();
              return $item;
            })
            ->end();
        return $scaffolder;
      }
    

    And then I can run a mutation with:

    mutation UpdateQuickPossession($SecondID: ID!, $Title: String) {
        updateQuickPossession(SecondID: $SecondID, Title: $Title) {
                ID
                SecondID
                Title
                SquareFeet
                Address
        }
    }
    

    With query variables:

    {
        "SecondID": "1",
        "Title": "Clarkson"
    }
    

    Which successfully gives me:

    {
        "data": {
            "updateQuickPossession": {
                "ID": "9",
                "SecondID": "1",
                "Title": "Clarkson",
                "SquareFeet": 2123,
                "Address": "123 Smith Rd"
            }
        }
    }
    

    It looks like the custom mutation() is required in order to override the use of ID as the primary identifier. The only drawback thus far is that it's not clear to me how to define and use complex input types (ie. $Input: QuickPossessionUpdateInputType!) in this method.