5
votes

Currently I have such Types.php:

namespace Application\GraphQL;

use Application\GraphQL\Type\NodeType;

use Application\GraphQL\Type\QueryType;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\Type;
use Application\GraphQL\Type\PersonType;

/**
 * Class Types
 *
 * Acts as a registry and factory for your types.
 *
 * As simplistic as possible for the sake of clarity of this example.
 * Your own may be more dynamic (or even code-generated).
 *
 * @package GraphQL\Examples\Blog
 */
class Types
{

    private static $query;
    private static $person;
    private static $node;


    public static function person()
    {
        return self::$person ?: (self::$person = new PersonType());
    }

    /**
     * @return QueryType
     */
    public static function query()
    {
        return self::$query ?: (self::$query = new QueryType());
    }

    /**
     * @return NodeType
     */
    public static function node()
    {
        return self::$node ?: (self::$node = new NodeType());
    }

    /**
     * @return \GraphQL\Type\Definition\IDType
     */
    public static function id()
    {
        return Type::id();
    }

    /**
     * @return \GraphQL\Type\Definition\StringType
     */
    public static function string()
    {
        return Type::string();
    }

    /**
     * @param Type $type
     * @return NonNull
     */
    public static function nonNull($type)
    {
        return new NonNull($type);
    }
}

In query() function it creates QueryType instance. I added to QueryType constructor PersonTable model class so that it could query persons from database.

QueryType.php

public function __construct(PersonTable $table)
    {
        $config = [
            'name' => 'Query',
            'fields' => [
                'person' => [
                    'type' => Types::person(),
                    'description' => 'Returns person by id',
                    'args' => [
                        'id' => Types::nonNull(Types::id())
                    ]
                ],
                'hello' => Type::string()
            ],
            'resolveField' => function($val, $args, $context, ResolveInfo $info) {
                return $this->{$info->fieldName}($val, $args, $context, $info);
            }
        ];

        $this->table = $table;
        parent::__construct($config);
    }

I have set up factories in module\Application\src\Module.php:

/**
 * @link      http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Application;

use Application\Model\PersonTable;

use Application\Model\Person;

use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;

class Module
{
    const VERSION = '3.0.2dev';

    public function getConfig()
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    // Add this method:
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Application\Model\PersonTable' =>  function($sm) {
                    $tableGateway = $sm->get('PersonTableGateway');
                    $table = new PersonTable($tableGateway);
                    return $table;
                },
                'PersonTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Person());
                    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
                },
            ),
        );
    }
}

I am doing by this example which does not have any framework:

https://github.com/webonyx/graphql-php/tree/master/examples/01-blog

So the question is - how do I create queryType instance with injected PersonTable instance? I should somehow get from the factory the PersonTable instance but I do not understand how.

Update:

I decided to try to inject QueryType into the controller. Created such function:

public function __construct(QueryType $queryType)
    {
        $this->queryType = $queryType;
    }

Now module\Application\src\Module.php getServiceConfig looks like this:

 public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Application\Model\PersonTable' =>  function($sm) {
                    $tableGateway = $sm->get('PersonTableGateway');
                    $table = new PersonTable($tableGateway);
                    return $table;
                },
                'PersonTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Person());
                    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
                },
                QueryType::class => function ($sm) {
                    return new QueryType($sm->get(PersonTable::class));
                }
                // when putting in namespace does not find??????????
                //QueryType::class => Application\GraphQL\Type\Factories\QueryTypeFactory::class
                //QueryType::class => \QueryTypeFactory::class

            ),
        );
    }

But I get error:

Catchable fatal error: Argument 1 passed to Application\Controller\IndexController::__construct() must be an instance of Application\GraphQL\Type\QueryType, none given, called in E:\projektai\php projektai\htdocs\graphQL_zend_3\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php on line 32 and defined in E:\projektai\php projektai\htdocs\graphQL_zend_3\module\Application\src\Controller\IndexController.p

How can none be given if I configured in that function?

If I could inject into the controller, then I plan to do like this:

 $schema = new Schema([
                //'query' => Types::query()
                'query' => $this->queryType
            ]);

So I would not need to call query() function which return the QueryType instance anyway.

And then PersonTable would be automatically injected into QueryType class.

Update:

I had created the factory, similar as in the asnswer:

class QueryTypeFactory implements FactoryInterface
{

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        return new QueryType($container->get(PersonTable::class));
    }

}

In the IndexController I have constructor:

public function __construct(QueryType $queryType)
    {
        $this->queryType = $queryType;
    }

In the Module.php I use this factory:

public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Application\Model\PersonTable' =>  function($sm) {
                    $tableGateway = $sm->get('PersonTableGateway');
                    $table = new PersonTable($tableGateway);
                    return $table;
                },
                'PersonTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Person());
                    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
                },
//                QueryType::class => function ($sm) {
//                    //return new QueryType($sm->get(PersonTable::class));
//
//                }

                //QueryType::class => Application\GraphQL\Type\Factories\QueryTypeFactory::class
                //QueryType::class => \QueryTypeFactory::class
                QueryType::class => QueryTypeFactory::class

            ),
        );
    }

It simply does not work, I get error:

Catchable fatal error: Argument 1 passed to Application\Controller\IndexController::__construct() must be an instance of Application\GraphQL\Type\QueryType, none given, called in E:\projektai\php projektai\htdocs\graphQL_zend_3\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php on line 32 and defined in E:\projektai\php projektai\htdocs\graphQL_zend_3\module\Application\src\Controller\IndexController.php on line

I also tried this way:

$queryTypeFactory = new QueryTypeFactory();

            // GraphQL schema to be passed to query executor:
            $schema = new Schema([
                //'query' => Types::query()
                //'query' => $this->queryType
              //  'query' => $queryType
                'query' => $queryTypeFactory()
            ]);

But the $queryTypeFactory() needs parameter $container. Which is not what I want, I guess. I should be able to create an instance without passing parameters.

I hope it is ok to use QueryType::class in the factories array as key. It will create with full name space which is set:

use Application\GraphQL\Type\QueryType;

And in index controller I also call that use statement.

1

1 Answers

2
votes
<?php
namespace Application\Service\Factory;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\CurrencyConverter;
use Application\Service\PurchaseManager;

/**
 * This is the factory for PurchaseManager service. Its purpose is to instantiate the 
 * service and inject its dependencies.
 */
class PurchaseManagerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, 
                $requestedName, array $options = null)
    {
        // Get CurrencyConverter service from the service manager.
        $currencyConverter = $container->get(CurrencyConverter::class);

        // Instantiate the service and inject dependencies.
        return new PurchaseManager($currencyConverter);
    }
}

In the code above we have the PurchaseManagerFactory class which implements the Zend\ServiceManager\Factory\FactoryInterface interface. The factory class has the __invoke() method whose goal is to instantiate the object. This method has the $container argument which is the service manager. You can use $container to retrieve services from service manager and pass them to the constructor method of the service being instantiated.