Nurguly Ashyrov

GraphQL for ProcessWire

90 posts in this topic

@Nurguly Ashyrov Hi, is there a way to add images/files to a page with the updatePage method ? If not, do you plan to implement it or do I have to do this another way ?

Thanks again :)

2 people like this

Share this post


Link to post
Share on other sites
16 hours ago, mvdesign said:

@Nurguly Ashyrov Hi, is there a way to add images/files to a page with the updatePage method ? If not, do you plan to implement it or do I have to do this another way ?

Unfortunately not yet. I am planning to implement them. But I am a bit busy now and it would definitely take some time to implement it. For now, you will have to do it some other way. Sorry :( 

2 people like this

Share this post


Link to post
Share on other sites

When I go to GraphiQl interface, selector argument for template fields does not appear to be optional as it's in the screencast. However, if I provide a selector, it works as expected. Do I need to set up something on backend?

How it looks on my GraphiQL:

mine.png.cc4f36341789f0adda896e40f4986af4.png

How it is in the screencast

screencast.png.9569ef1df452db72361faac3b40dbbe9.png

 

1 person likes this

Share this post


Link to post
Share on other sites
13 hours ago, abdus said:

When I go to GraphiQl interface, selector argument for template fields does not appear to be optional as it's in the screencast. However, if I provide a selector, it works as expected. Do I need to set up something on backend?

Hi @abdus. You are good to go actually. The argument is required only when there is an exclamation mark after it. It does not have to be = false. Sorry for the confusion, the way I explain it in screencast is a bit misleading. To sum it up, if there is an exclamation mark "!" at the end, it means it is required, of not then it is optional. The = false part means, the default value is false. I know, it sounds stupid. It was a bug in the library I used for this module. I updated it to the latest version since the screencast and now it shows correctly. The = false part is actually is a bug in older version of the module. You probably installed the latest version which shows correctly.

So, your version is actually is the way it supposed to be. The version in the screencast is a bit misleading, but still correct.

1 person likes this

Share this post


Link to post
Share on other sites
3 hours ago, Nurguly Ashyrov said:

Hi @abdus. You are good to go actually. The argument is required only when there is an exclamation mark after it. It does not have to be = false. Sorry for the confusion, the way I explain it in screencast is a bit misleading. To sum it up, if there is an exclamation mark "!" at the end, it means it is required, of not then it is optional. The = false part means, the default value is false. I know, it sounds stupid. It was a bug in the library I used for this module. I updated it to the latest version since the screencast and now it shows correctly. The = false part is actually is a bug in older version of the module. You probably installed the latest version which shows correctly.

So, your version is actually is the way it supposed to be. The version in the screencast is a bit misleading, but still correct.

Thanks for the clarification. The problem is that if I dont specify any selector, I get empty array, also an error

chrome_2017-04-06_15-50-19.thumb.png.00e9aa75f76ebead5470e0a37f80b24b.png

But if I specify a selector, it all works fine

chrome_2017-04-06_15-53-01.thumb.png.cbbe96957338cba8f6b242264d8e3940.png

I'm not sure if this is the expected behavior.

Share this post


Link to post
Share on other sites
20 hours ago, abdus said:

Thanks for the clarification. The problem is that if I dont specify any selector, I get empty array, also an error

I'm not sure if this is the expected behavior.

Well now I am embarrassed. :( So sorry. It turns out upgrading to the latest graphql library broke some stuff. For some reason I did not check the queries without the selectors. I made some fixes. This should solve your problem. Please get the latest version and try again.

4 people like this

Share this post


Link to post
Share on other sites
12 hours ago, Nurguly Ashyrov said:

This should solve your problem

Thanks a lot for the changes, it works without a problem now :)

Also, is it possible for you to add some hookable functions to the module so that we can add custom queries and mutations? Inside Schema::build(), query and mutation fields are defined, maybe split build() method into ___getMutations and ___getQueries, then we can modify $event->returns inside hooks to add our own fields? Something like this maybe:

<?php
// maybe build parameters in 
// /ProcessGraphQL/ProcessGraphQL.module

public function ___getQueries() {
  // build an assc. array of FieldInterface objects
  return [...];
}

public function ___getMutations() {
  // build an assc. array of FieldInterface objects
  return [...];
}

public function executeGraphQL()
{
  // instantiating Processor and setting the schema

  $schema = new Schema($this->___getQueries(), $this->___getMutations());
  $processor = new Processor($schema);

  // ...
}

then inside Schema.php you would just use the provided fields.

<?php
class Schema extends AbstractSchema {
    private $queries = [];
    private $mutations = [];

    public function __construct($queries, $mutations)
    {
        // populate fields, perform checks etc
    }

    public function build(SchemaConfig $config)
    {
        $moduleConfig = Utils::moduleconfig();
        $query = $config->getQuery();
        $mutation = $config->getMutation();

        // add other query fields
        $query->addFields($this->queries);
        $mutation->addFields($this->mutations);
    }
}

It's a proof of concept, needs better separation of concerns etc, but it should work, why wouldn't it.

1 person likes this

Share this post


Link to post
Share on other sites

Or maybe use $moduleConfig instead of causing fragmentation.

<?php // Schema.php
public function build(SchemaConfig $config)
{
	// $moduleConfig is an instance of ProcessGraphQL module
	$moduleConfig = Utils::moduleconfig();
	$customQueries = $moduleConfig->___getQueries();
	$customMutations = $moduleConfig->___getMutations();

	$query = $config->getQuery();
	$mutation = $config->getMutation();

	$query->addFields($customQueries);
	$mutation->addFields($customQueries);
	// then populate your own fields etc.
}
  

 

Share this post


Link to post
Share on other sites
On 4/8/2017 at 4:58 AM, abdus said:

Also, is it possible for you to add some hookable functions to the module so that we can add custom queries and mutations?...

Done! :)

The latest version now allows you to hook into getQuery & getMutation methods of the ProcessGraphQL class. Those hooks are solely there so you could modify the query and mutation operations. Here how it might look like in your template file.

<?php namespace ProcessWire;
use Youshido\GraphQL\Type\Scalar\StringType;

$processGraphQL = $modules->get('ProcessGraphQL');

wire()->addHookAfter('ProcessGraphQL::getQuery', function ($event) {
    $query = $event->return;
  
    $query->addField('hello', [
        'type' => new StringType(),
        'resolve' => function () {
            return 'world!';
        }
    ]);
});

echo $processGraphQL->executeGraphQL();

The above code will add a hello field into your GraphQL query that responds with the string "world!". You'll have to use the Youshido\GraphQL library that ProcessGraphQL module uses internally. The same thing could be done with the mutation operation via getMutation  hook method.

4 people like this

Share this post


Link to post
Share on other sites

@Nurguly Ashyrov I am trying to build a mutation field for mailing list subscription that takes in email address and returns status code and error messages, if any. 

<?php namespace ProcessWire;
// /site/templates/graphql.php

use ProcessWire\Api\SubscriptionField;
require_once '../modules/ProcessGraphQL/vendor/autoload.php';

$processGraphQL = modules()->get('ProcessGraphQL');

wire()->addHookAfter('ProcessGraphQL::getMutation', function ($event) {
    $query = $event->return;
    $query->addField(new SubscriptionField());
});

echo $processGraphQL->executeGraphQL();

Here's my SubscriptionField definition

Spoiler

<?php namespace Processwire\Api;
// /site/templates/api/SubscriptionField.php

use Youshido\GraphQL\Config\Field\FieldConfig;
// others

class SubscriptionField extends AbstractField
{
    public function getType()
    {
        return new SubscriptionType();
    }

    public function resolve($value, array $args, ResolveInfo $info)
    {
        return [
            'statusCode' => 200,
            'errors' => [
                'no error'
            ]
        ];
    }

    public function build(FieldConfig $config)
    {
        $config->addArgument('email', new NonNullType(new StringType()));
    }
}

and my SubscriptionType class

Spoiler

<?php namespace ProcessWire\Api;
// /site/templates/api/SubscriptionType.php
use Youshido\GraphQL\Type\ListType\ListType;
// others

class SubscriptionType extends AbstractObjectType
{

    public function getName()
    {
        return 'Subscription';
    }

    public function build($config)
    {
        $config->addField('statusCode', [
            'type' => new NonNullType(new IntType()),
            'description' => 'The subscription status code. E.g. 200 if successful.',
            'resolve' => function ($value) {
                return (integer)$value->statusCode;
            }
        ]);

        $config->addField('errors', [
            'type' => new ListType(new StringType()),
            'description' => 'Human readable subscription errors. E.g. "Added to list!"',
            'resolve' => function ($value) {
                return (string)$value->errors;
            }
        ]);
    }

}

When I open the GraphiQL console, I get this error

chrome_2017-04-09_17-44-58.png.e7445fbe2fd0a032a0fcf2d9444d5ce0.png

I am not sure why PHP can't find the class, any ideas?

When I manually require field class (require_once 'api/SubscriptionField.php';), it resolves the class, but then cannot resolve others (error: Class 'Youshido\GraphQL\Field\AbstractField' not found)

Share this post


Link to post
Share on other sites

@abdus First of all you don't need to import ProcesGraphQL's vendor.php file. It is done automatically when you do 

$processGraphQL = modules()->get('ProcessGraphQL');

Second is, I don't see where you include your /site/templates/api/SubscriptionField.php file into graphql.php. So replace the 

require_once '../modules/ProcessGraphQL/vendor/autoload.php';

with

require_once "./api/SubscriptionType.php";

in your graphql.php and it should work.

Also, your SubscriptionField class requires the public function getName().

2 people like this

Share this post


Link to post
Share on other sites
1 minute ago, Nurguly Ashyrov said:

@abdus First of all you don't need to import ProcesGraphQL's vendor.php file. It is done automatically when you do 


$processGraphQL = modules()->get('ProcessGraphQL');

Second is, I don't see where you include your /site/templates/api/SubscriptionField.php file into graphql.php. So replace the 


require_once '../modules/ProcessGraphQL/vendor/autoload.php';

with


require_once "./api/SubscriptionType.php";

in your graphql.php and it should work.

Also, your SubscriptionField class requires the public function getName().

Thanks for the guidance @Nurguly Ashyrov. The problem was that I wasn't including calling the module (and therefore not autoloading GraphQL classes) before I included mine. That was why it wasn't resolving.

<?php
// CORRECT
$processGraphQL = modules()->get('ProcessGraphQL');
require_once 'api/SubscriptionField.php';
require_once 'api/SubscriptionType.php';

// INCORRECT
require_once 'api/SubscriptionField.php';
require_once 'api/SubscriptionType.php';
$processGraphQL = modules()->get('ProcessGraphQL');

 

Share this post


Link to post
Share on other sites

For reference here's a simple mutation field implementation

<?php
wire()->addHookAfter('ProcessGraphQL::getMutation', function ($event) {
    $query = $event->return;
    $query->addField('log', [
      	// define return object under type
        'type' => new ObjectType([
            'name' => 'Log',
            'fields' => [
                'result' => [
                    'type' => new StringType()
                ]
            ]
        ]),
      	// available arguments for the query
        'args' => [
            'text' => new StringType()
        ],
      	// what to do with the request
        'resolve' => function ($value, $args) {
            // process given input
          	// return object outline should be same as 'type'
            return [
                'result' => 'done. ' . $args['text']
            ];
        }
    ]);
});

When you refresh GraphiQL console, you get your new field!

graphql.thumb.png.b670200b517c7563e5330a441fa3c9de.png

4 people like this

Share this post


Link to post
Share on other sites

Hi,

I just tried the module and get a JSON.parse Error because the query responded  this error:.

EDIT: Running on PHP 7.1 on Heroku and ext-mbstring was missing. I installed it and the error is away :-)


Fatal error: Uncaught Error: Call to undefined function Youshido\GraphQL\Parser\mb_strlen() in /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Token.php:63
Stack trace:
#0 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(184): Youshido\GraphQL\Parser\Token->__construct('identifier', 2, 7, 'pages')
#1 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(134): Youshido\GraphQL\Parser\Tokenizer->scanWord()
#2 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(33): Youshido\GraphQL\Parser\Tokenizer->scan()
#3 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(320): Youshido\GraphQL\Parser\Tokenizer->next()
#4 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Parser.php(96): Youshido\GraphQL\Parser\Tokenizer->lex()
#5 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Parser.php(42): Youshido\GraphQL\Parser\Parser->parseBody()
#6 /app/site/modules/Pro in /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Token.php on line 63


Error:     Uncaught Error: Call to undefined function Youshido\GraphQL\Parser\mb_strlen() in /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Token.php:63
Stack trace:
#0 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(184): Youshido\GraphQL\Parser\Token->__construct('identifier', 2, 7, 'pages')
#1 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(134): Youshido\GraphQL\Parser\Tokenizer->scanWord()
#2 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(33): Youshido\GraphQL\Parser\Tokenizer->scan()
#3 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Tokenizer.php(320): Youshido\GraphQL\Parser\Tokenizer->next()
#4 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Parser.php(96): Youshido\GraphQL\Parser\Tokenizer->lex()
#5 /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Parser.php(42): Youshido\GraphQL\Parser\Parser->parseBody()
#6 /app/site/modules/Pro (line 63 of /app/site/modules/ProcessGraphQL/vendor/youshido/graphql/src/Parser/Token.php) 

This error message was shown because: site is in debug mode. ($config->debug = true; => /site/config.php). Error has been logged. 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By bernhard
      Hi,
      just stumbled over a little module that i built for my last project. it helped me to test performance of my rockdatatables module to generate 3000 random json datasets and i want to share it with you. maybe it saves some time for someone.
      https://gitlab.com/baumrock/RockDummyData/
      easy example:
      $rdd = $modules->get('RockDummyData'); for($i=0; $i<15; $i++) { // this has to be inside the for-loop to always get a new dummy $dummy = $rdd->getDummy(); echo date("d.m.Y H:i:s", $dummy->timestamp) . "<br>"; } more advanced:
      $json = new stdClass(); $json->data = array(); $rdd = $modules->get('RockDummyData'); for($i=0; $i<3000; $i++) { // this has to be inside the for-loop to always get a new dummy $dummy = $rdd->getDummy(); $obj = new stdClass(); $obj->name = $dummy->forename . ' ' . $dummy->surname; $obj->position = $dummy->job; $obj->office = $dummy->city; $obj->color = $dummy->color; $obj->start_date = new stdClass(); $obj->start_date->display = date('d.m.Y',$dummy->timestamp); $obj->start_date->sort = $dummy->timestamp; $obj->salary = rand(0,10000); $json->data[] = $obj; } echo json_encode($json); you have to store your random datasets on your own into the /data folder. there are several services for creating all kinds of random data on the web - if you know one service that allows sharing those datasets let me know and i can include common needed data into the module
    • By AndySh
      Hello!
      I need your assistance please. I purchased the module FormBuilder. Unfortunately, the module discontinued delivering customer submissions to e-mail box specified in the module settings. Direct mailing to the e-mail box works OK. The module settings stays the same and are correct, like "Send e-mail to administrator(s) is checked. The last version of FormBuilder 3.0 has been installed. Please advise how to resolve the issue becase I cannot get orders from customers anymore (((
    • By kixe
      As described in this post (https://processwire.com/talk/topic/8551-custom-urls-for-pages/?p=82742) the option 'Name Format Children' under the tab 'Family' in template settings doesn't work properly and also not as expected. I had a look inside the code and made some changes which are working properly, which offers much more options, more consistency and less code too.

      The result is the following. You have 3 Options for generating name and title, which could be combined in endless variations.
      Name is always derived from title, same like creating pages manually.
      type date: if function detects # character anywhere in the string, conversion will be: deletion of # and string will be used as format parameter for PHP date() function type field: if string is a fieldname of the parent page the value of this field will be used type string: if string doesn't fit to the 2 preceeding it will be taken as it is All parts (separated by comma) will be composed in the order of setting. You can use unlimited numbers of parts

      I made a pull request on github: https://github.com/ryancramerdesign/ProcessWire/pull/831

      Example screenshots

      Setting ...


      will result in


       
    • By kongondo
      FieldtypeRuntimeMarkup and InputfieldRuntimeMarkup
       
      Modules Directory: http://modules.processwire.com/modules/fieldtype-runtime-markup/
      GitHub: https://github.com/kongondo/FieldtypeRuntimeMarkup
       
      This module allows for custom markup to be dynamically (PHP) generated and output within a page's edit screen (in Admin).
       
      The value for the fieldtype is generated at runtime. No data is saved in the database. The accompanying InputfieldRuntimeMarkup is only used to render/display the markup in the page edit screen.
       
      The field's value is accessible from the ProcessWire API in the frontend like any other field, i.e. it has access to $page and $pages.
       
      The module was commissioned/sponsored by @Valan. Although there's certainly other ways to achieve what this module does, it offers a dynamic and flexible alternative to generating your own markup in a page's edit screen whilst also allowing access to that markup in the frontend. Thanks Valan!
       
      Warning/Consideration
      Although access to ProcessWire's Fields' admin pages is only available to Superusers, this Fieldtype will evaluate and run the custom PHP Code entered and saved in the field's settings (Details tab). Utmost care should therefore be taken in making sure your code does not perform any CRUD operations!! (unless of course that's intentional) The value for this fieldtype is generated at runtime and thus no data is stored in the database. This means that you cannot directly query a RuntimeMarkup field from $pages->find(). Usage and API
       
      Backend
      Enter your custom PHP snippet in the Details tab of your field (it is RECOMMENDED though that you use wireRenderFile() instead. See example below). Your code can be as simple or as complicated as you want as long as in the end you return a value that is not an array or an object or anything other than a string/integer.
       
      FieldtypeRuntimeMarkup has access to $page (the current page being edited/viewed) and $pages. 
       
      A very simple example.
      return 'Hello'; Simple example.
      return $page->title; Simple example with markup.
      return '<h2>' . $page->title . '</h2>'; Another simple example with markup.
      $out = '<h1>hello '; $out .= $page->title; $out .= '</h1>'; return $out; A more advanced example.
      $p = $pages->get('/about-us/')->child('sort=random'); return '<p>' . $p->title . '</p>'; An even more complex example.
      $str =''; if($page->name == 'about-us') { $p = $page->children->last(); $str = "<h2><a href='{$p->url}'>{$p->title}</a></h2>"; } else { $str = "<h2><a href='{$page->url}'>{$page->title}</a></h2>"; } return $str; Rather than type your code directly in the Details tab of the field, it is highly recommended that you placed all your code in an external file and call that file using the core wireRenderFile() method. Taking this approach means you will be able to edit your code in your favourite text editor. It also means you will be able to type more text without having to scroll. Editing the file is also easier than editing the field. To use this approach, simply do:
      return wireRenderFile('name-of-file');// file will be in /site/templates/ If using ProcessWire 3.x, you will need to use namespace as follows:
      return ProcessWire\wireRenderFile('name-of-file'); How to access the value of RuntimeMarkup in the frontend (our field is called 'runtime_markup')
       
      Access the field on the current page (just like any other field)
      echo $page->runtime_markup; Access the field on another page
      echo $pages->get('/about-us/')->runtime_markup; Screenshots
       
      Backend
       

       

       
      Frontend