plato-solutions/yii2-graphql

为 yii2 PHP 框架提供的 GraphQL 服务器端实现

安装次数: 2,784

依赖者: 0

推荐者: 0

安全: 0

星标: 6

关注者: 0

分支: 26

类型:yii2-extension

0.12.0-alpha 2021-09-09 11:09 UTC

This package is not auto-updated.

Last update: 2024-09-21 02:50:59 UTC


README

使用 GraphQL PHP 服务器实现。这是从 yii2-graphql 分支出来的,并扩展了 graphql-php 以适应 Yii2

指南(适用于 Yii 基础模板)

与 Yii 高级模板相同,但

  • 不是在 backend 命名空间中,而是在 app
    • 例如,命名空间应该是 namespace app\modules\graphql\...
  • 不是 main.php,而是 web.php 例如
    • 例如,配置在 config/web.php

指南(适用于 Yii 高级模板)

安装

使用 composer

composer require Plato-solutions/yii2-graphql

启用 Yii JsonParser

backend/config/main.php 中启用对 JSON 请求的解析

'components' => [
    'request' => [
        // ... other config
        'parsers' => [
            'application/json' => 'yii\web\JsonParser',
        ]
    ]
]

创建一个 GraphQLModule

  1. 在您的基路径(即 backend)中创建一个 modules 文件夹

  2. 在模块文件夹中创建一个 graphql 文件夹。因此 backend/modules/graphql

  3. 在该文件夹中创建一个 GraphqlModule.php 文件,内容如下:backend/modules/graphql/GraphqlModule.php

<?php
 namespace backend\modules\graphql;
 
 use yii\base\Module;
 use yii\graphql\GraphQLModuleTrait;
 
 class GraphqlModule extends Module{
     use GraphQLModuleTrait;
 }
  1. backend/config/main.php 中找到 modules 配置并将其添加到其中,如下所示
'modules' => [
    'graphql => [
        'class' => 'backend\modules\graphql\GraphqlModule',
    ]
]

创建一个 控制器

  1. 在您的 modules/graphql 文件夹中创建一个 controllers 文件夹。
  2. 在该文件夹中创建一个 DefaultController.php 文件,内容如下
<?php

namespace backend\modules\graphql\controllers;

use Yii;
use yii\rest\Controller;

class DefaultController extends Controller
{
    public function actions()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        return [
            'index' => [
                'class' => 'yii\graphql\GraphQLAction'
            ],
        ];
    }
}

创建 GraphQL 类型

例如,在 backend/models 文件夹中的模型如下所示,

<?php
/**
 * This is the model class for table "country".
 *
 * @property string $code
 * @property string $name
 * @property int $population
 * @property Person $leader
 */
class Country extends \yii\db\ActiveRecord
{

其中 Person 是另一个模型,它有自己的属性,就像 Country 一样

  1. 在您的模块 modules/graphql 中创建一个文件夹,命名为 types
  2. 创建一个 CountryType.php(根据您的模型类命名,后缀为 Type),内容如下
<?php

namespace backend\modules\graphql\types;

use GraphQL\Type\Definition\Type;
use yii\graphql\base\GraphQLType;
use yii\graphql\GraphQL;

class CountryType extends GraphQLType
{
    protected $attributes = [
        'name' => 'country',
        'description' => 'description here'
    ];

    public function fields()
    {
        return [
            'id' => Type::id(),
            'name' => Type::string(),
            'population' => Type::int(),
            'leader' => GraphQLType::type(PersonType::class)
        ];
    }
}

对所有 backend\models 中的模型执行上述操作。

有关类型下的所有类型完整列表,请参阅下方的标量类型。

模型 创建 GraphQL 查询

  1. 在您的模块 modules/graphql 中创建一个文件夹,命名为 queries
  2. 创建一个 CountryQuery.php(根据您的模型类命名,后缀为 Query),内容如下
<?php

namespace backend\modules\graphql\queries;

use GraphQL\Type\Definition\Type;
use yii\graphql\queries\ModelQuery;
use backend\modules\graphql\types\CountryType;
use backend\models\Country;

class CountryQuery extends ModelQuery
{
    public $type = CountryType::class;

    public $model = Country::class;

    public function args()
    {
        return [
            'id' => Type::id(),
            'name' => Type::string(),
            'population' => Type::int(),
            'leaderId' => Type::id() 
        ];
        // replace the non-scalar type with it's id in the args
    }
}

为所有希望在查询中使用的 backend\models 中的模型执行上述操作。

设置 模式

  1. backend/modules/graphql 中创建一个 PHP 文件 schema.php,内容如下
<?php

return [
    'query' => [
        'country' => 'backend\modules\graphql\queries\CountryQuery',
        //... add all your queries here
    ],
    'mutation' => [
        //... add all your mutations here
    ],
    'types' => [
        'country' => 'backend\modules\graphql\types\CountryType',
        //... add all your types here
    ]
];
  1. backend/config/main.php 中有关 modules 的部分中添加到 schema.php 的路径,如下所示(确保 schema 目录路径正确)。
    'modules' => [
        'graphql' => [
            'class' => 'backend\modules\graphql\GraphqlModule',
            'schema' => require __DIR__ . '/../../backend/modules/graphql/schema.php',
        ]
    ],

文档

类型

类型系统是GraphQL的核心,体现在GraphQLType中。通过解构GraphQL协议并使用graph-php库来精细控制所有元素,可以方便地根据自身需求扩展类。

标量类型

GraphQL规范描述了几个内置的标量类型。在graphql-php中,它们作为类GraphQL\Type\Definition\Type的静态方法暴露。

GraphQLType的主要元素

以下元素可以声明在类的$attributes属性中,或作为方法,除非另有说明。这也适用于此之后的所有元素。

查询

GraphQLQueryGraphQLMutation继承自GraphQLField。元素结构一致,如果您想有一个可重用的Field,可以继承它。每个Graphql查询都需要对应一个GraphQLQuery对象。ModelQuery继承自GraphQLQuery

GraphQLField的主要元素

突变

定义与GraphQLQuery类似,请参见上文。

简化字段定义

简化了Field的声明,不需要以类型键为数组的定义。

标准定义

//...
'id' => [
    'type' => Type::id(),
],
//...

简化定义

//...
'id' => Type::id(),
//...

输入验证

支持验证规则。除了基于graphql的验证外,还可以使用Yii Model验证,这目前用于输入参数的验证。将规则方法直接添加到突变定义中。

public function rules() {
    return [
        ['password','boolean']
    ];
}

授权验证

由于graphql查询可以组合,例如当查询合并两个查询时,这两个查询有不同的授权约束,需要自定义授权。我称此查询为“graphql操作”;当所有graphql操作条件配置完毕时,通过授权检查。

认证

在控制器的行为方法中,将授权方法设置为以下内容

function behaviors() {
    return [
        'authenticator'=>[
            'class' => 'yii\graphql\filter\auth\CompositeAuth',
            'authMethods' => [
                \yii\filters\auth\QueryParamAuth::class,
            ],
            'except' => ['hello']
        ],
    ];
}

如果想要支持IntrospectionQuery授权,相应的graphql操作是__schema

授权

如果用户已通过认证,您可能想检查对资源的访问权限。您可以在控制器中使用GraphqlActioncheckAccess方法。它将检查所有graphql操作。

class GraphqlController extends Controller
{
    public function actions() {
        return [
            'index' => [
                'class' => 'yii\graphql\GraphQLAction',
                'checkAccess'=> [$this,'checkAccess'],
            ]
        ];
    }

    /**
     * authorization
     * @param $actionName
     * @throws yii\web\ForbiddenHttpException
     */
    public function checkAccess($actionName) {
        $permissionName = $this->module->id . '/' . $actionName;
        $pass = Yii::$app->getAuthManager()->checkAccess(Yii::$app->user->id,$permissionName);
        if (!$pass){
            throw new yii\web\ForbiddenHttpException('Access Denied');
        }
    }
}