zoukankan      html  css  js  c++  java
  • 基于 GraphQL 构建 Laravel API —— 基本使用篇


    官方:https://www.howtographql.com/basics/0-introduction/

    官方:https://graphql.org/learn/

    参考:

    Laravel API 系列教程(四):基于 GraphQL 构建 Laravel API —— 基本使用篇

    Laravel API 系列教程(五):基于 GraphQL 构建 Laravel API —— 高级使用篇

    https://auth0.com/blog/developing-and-securing-graphql-apis-with-laravel/


    D:laragonwwwgraphql>composer require rebing/graphql-laravel
    Using version ^5.1 for rebing/graphql-laravel
    ./composer.json has been updated
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Package operations: 2 installs, 0 updates, 0 removals
      - Installing webonyx/graphql-php (v0.13.8): Downloading (100%)
      - Installing rebing/graphql-laravel (5.1.1): Downloading (100%)
    webonyx/graphql-php suggests installing react/promise (To leverage async resolving on React PHP platform)
    Writing lock file
    Generating optimized autoload files
    > IlluminateFoundationComposerScripts::postAutoloadDump
    > @php artisan package:discover --ansi
    Discovered Package: facade/ignition
    Discovered Package: fideloper/proxy
    Discovered Package: fruitcake/laravel-cors
    Discovered Package: laravel/tinker
    Discovered Package: laravel/ui
    Discovered Package: nesbot/carbon
    Discovered Package: nunomaduro/collision
    Discovered Package: rebing/graphql-laravel
    Discovered Package: tymon/jwt-auth
    Package manifest generated successfully.
    3 packages you are using are looking for funding.
    Use the `composer fund` command to find out more!
    
    D:laragonwwwgraphql>
    D:laragonwwwgraphql>php artisan vendor:publish --provider="RebingGraphQLGraphQLServiceProvider"
    Copied File [vendor
    ebinggraphql-laravelconfigconfig.php] To [configgraphql.php]
    Publishing complete.
    
    D:laragonwwwgraphql>php artisan make:graphql:type UserType
    Type created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:query UserQuery
    Query created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:mutation UpdateUserPasswordMutation
    Mutation created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:mutation UpdateUserEmailMutation
    Mutation created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:type ArticleType
    Type created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:enum ArticleStatusEnum
    Enum created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:interface CharacterInterface
    Interface created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:type HumanType
    Type created successfully.
    
    D:laragonwwwgraphql>php artisan make:graphql:field PictureField
    Field created successfully.

    graphql.php

    <?php
    
    declare(strict_types=1);
    
    use exampleMutationExampleMutation;
    use exampleQueryExampleQuery;
    use exampleTypeExampleRelationType;
    use exampleTypeExampleType;
    
    return [
    
        // The prefix for routes
        'prefix' => 'graphql',
    
        // The routes to make GraphQL request. Either a string that will apply
        // to both query and mutation or an array containing the key 'query' and/or
        // 'mutation' with the according Route
        //
        // Example:
        //
        // Same route for both query and mutation
        //
        // 'routes' => 'path/to/query/{graphql_schema?}',
        //
        // or define each route
        //
        // 'routes' => [
        //     'query' => 'query/{graphql_schema?}',
        //     'mutation' => 'mutation/{graphql_schema?}',
        // ]
        //
        'routes' => '{graphql_schema?}',
    
        // The controller to use in GraphQL request. Either a string that will apply
        // to both query and mutation or an array containing the key 'query' and/or
        // 'mutation' with the according Controller and method
        //
        // Example:
        //
        // 'controllers' => [
        //     'query' => 'RebingGraphQLGraphQLController@query',
        //     'mutation' => 'RebingGraphQLGraphQLController@mutation'
        // ]
        //
        'controllers' => RebingGraphQLGraphQLController::class . '@query',
    
        // Any middleware for the graphql route group
        'middleware' => [],
    
        // Additional route group attributes
        //
        // Example:
        //
        // 'route_group_attributes' => ['guard' => 'api']
        //
        'route_group_attributes' => [],
    
        // The name of the default schema used when no argument is provided
        // to GraphQL::schema() or when the route is used without the graphql_schema
        // parameter.
        'default_schema' => 'default',
    
        // The schemas for query and/or mutation. It expects an array of schemas to provide
        // both the 'query' fields and the 'mutation' fields.
        //
        // You can also provide a middleware that will only apply to the given schema
        //
        // Example:
        //
        //  'schema' => 'default',
        //
        //  'schemas' => [
        //      'default' => [
        //          'query' => [
        //              'users' => 'AppGraphQLQueryUsersQuery'
        //          ],
        //          'mutation' => [
        //
        //          ]
        //      ],
        //      'user' => [
        //          'query' => [
        //              'profile' => 'AppGraphQLQueryProfileQuery'
        //          ],
        //          'mutation' => [
        //
        //          ],
        //          'middleware' => ['auth'],
        //      ],
        //      'user/me' => [
        //          'query' => [
        //              'profile' => 'AppGraphQLQueryMyProfileQuery'
        //          ],
        //          'mutation' => [
        //
        //          ],
        //          'middleware' => ['auth'],
        //      ],
        //  ]
        //
        'schemas' => [
            'default' => [
                'query' => [
                    // 'example_query' => ExampleQuery::class,
                    'users' => AppGraphQLQueriesUserQuery::class,
                ],
                'mutation' => [
                    // 'example_mutation'  => ExampleMutation::class,
                    'updateUserPassword' => AppGraphQLMutationsUpdateUserPasswordMutation::class,
                    'updateUserEmail' => AppGraphQLMutationsUpdateUserEmailMutation::class,
                ],
                'middleware' => ['auth:api'],
                'method' => ['get', 'post'],
            ],
        ],
    
        // The types available in the application. You can then access it from the
        // facade like this: GraphQL::type('user')
        //
        // Example:
        //
        // 'types' => [
        //     'user' => 'AppGraphQLTypeUserType'
        // ]
        //
        'types' => [
            // 'example'           => ExampleType::class,
            // 'relation_example'  => ExampleRelationType::class,
            // RebingGraphQLSupportUploadType::class,
            'User' => AppGraphQLTypesUserType::class,
            'Article' => AppGraphQLTypesArticleType::class,
            'ArticleStatusEnum' => AppGraphQLEnumsArticleStatusEnum::class,
        ],
    
        // The types will be loaded on demand. Default is to load all types on each request
        // Can increase performance on schemes with many types
        // Presupposes the config type key to match the type class name property
        'lazyload_types' => false,
    
        // This callable will be passed the Error object for each errors GraphQL catch.
        // The method should return an array representing the error.
        // Typically:
        // [
        //     'message' => '',
        //     'locations' => []
        // ]
        'error_formatter' => ['RebingGraphQLGraphQL', 'formatError'],
    
        /*
         * Custom Error Handling
         *
         * Expected handler signature is: function (array $errors, callable $formatter): array
         *
         * The default handler will pass exceptions to laravel Error Handling mechanism
         */
        'errors_handler' => ['RebingGraphQLGraphQL', 'handleErrors'],
    
        // You can set the key, which will be used to retrieve the dynamic variables
        'params_key' => 'variables',
    
        /*
         * Options to limit the query complexity and depth. See the doc
         * @ https://webonyx.github.io/graphql-php/security
         * for details. Disabled by default.
         */
        'security' => [
            'query_max_complexity' => null,
            'query_max_depth' => null,
            'disable_introspection' => false,
        ],
    
        /*
         * You can define your own pagination type.
         * Reference RebingGraphQLSupportPaginationType::class
         */
        'pagination_type' => RebingGraphQLSupportPaginationType::class,
    
        /*
         * Config for GraphiQL (see (https://github.com/graphql/graphiql).
         */
        'graphiql' => [
            'prefix' => '/graphiql',
            'controller' => RebingGraphQLGraphQLController::class . '@graphiql',
            'middleware' => [],
            'view' => 'graphql::graphiql',
            'display' => env('ENABLE_GRAPHIQL', true),
        ],
    
        /*
         * Overrides the default field resolver
         * See http://webonyx.github.io/graphql-php/data-fetching/#default-field-resolver
         *
         * Example:
         *
         * ```php
         * 'defaultFieldResolver' => function ($root, $args, $context, $info) {
         * },
         * ```
         * or
         * ```php
         * 'defaultFieldResolver' => [SomeKlass::class, 'someMethod'],
         * ```
         */
        'defaultFieldResolver' => null,
    
        /*
         * Any headers that will be added to the response returned by the default controller
         */
        'headers' => [],
    
        /*
         * Any JSON encoding options when returning a response from the default controller
         * See http://php.net/manual/function.json-encode.php for the full list of options
         */
        'json_encoding_options' => 0,
    ];
    
    


    User.php:

    <?php
    
    namespace App;
    
    use IlluminateContractsAuthMustVerifyEmail;
    use IlluminateDatabaseEloquentSoftDeletes;
    use IlluminateFoundationAuthUser as Authenticatable;
    use IlluminateNotificationsNotifiable;
    use TymonJWTAuthContractsJWTSubject;
    
    class User extends Authenticatable implements JWTSubject
    {
        use Notifiable, SoftDeletes;
    
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        protected $fillable = [
            'name', 'email', 'password',
        ];
    
        /**
         * The attributes that should be hidden for arrays.
         *
         * @var array
         */
        protected $hidden = [
            'password', 'remember_token',
        ];
    
        /**
         * The attributes that should be cast to native types.
         *
         * @var array
         */
        protected $casts = [
            'email_verified_at' => 'datetime',
        ];
    
        public function articles()
        {
            return $this->hasMany(Article::class);
        }
    
        public function getJWTIdentifier()
        {
            return $this->getKey();
        }
    
        public function getJWTCustomClaims()
        {
            return [];
        }
    }
    
    


    Article.php:

    <?php
    
    namespace App;
    
    use IlluminateDatabaseEloquentModel;
    use IlluminateDatabaseEloquentSoftDeletes;
    
    class Article extends Model
    {
        use SoftDeletes;
    
        protected $fillable = ['user_id', 'title', 'body'];
    
    
        public function user()
        {
            return $this->belongsTo(User::class);
        }
    }
    
    

    ArticleController.php:

    <?php
    
    namespace AppHttpControllers;
    
    use AppArticle;
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesValidator;
    
    class ArticleController extends Controller
    {
        public function __construct()
        {
    //        $this->middleware('auth:api')->except(['show', 'index']);
            $this->middleware('auth:api')->except(['show']);
        }
        /**
         * Display a listing of the resource.
         *
         *
         *///@return IlluminateHttpResponse
        public function index(Request $request)
        {
            $user = $request->user();
    
            $articles = $user->articles()->get();
            return response()->json($articles);
        }
    
    
        /**
         * Store a newly created resource in storage.
         *
         * @param IlluminateHttpRequest $request
         *
         *///@return IlluminateHttpResponse
        public function store(Request $request)
        {
    
            $rules = [
                'title' => 'required|string|unique:articles|max:255',
                'body' => 'required|string|max:255',
            ];
    
            $message = [
                'title.required' => '必须输入title',
                'title.string' => 'title格式为字符串',
                'title.max' => 'title不要超过255',
                'title.unique' => 'title不可重复',
                'body.required' => '必须输入title',
                'body.string' => 'title格式为字符串',
                'body.max' => 'title不要超过255',
            ];
    
            $validator = Validator::make($request->all(), $rules, $message);
    
            if ($validator->fails()) {
                return response()->json($validator->errors()->getMessages(), 302);
            }
    
            $user = $request->user();
    
            if (!$user) {
                return response()->json([
                    'error' => '请先登录',
                ], 401);
            }
    
            $data = $validator->validated();
    
            $data['user_id'] = $user->id;
    
            $article = Article::create($data);
    
            return response()->json($article, 201);
    
        }
    
        /**
         * Display the specified resource.
         *
         * @param AppArticle $article
         *
         */ //@return IlluminateHttpResponse
        public function show(Article $article)
        {
            return response()->json($article);
        }
    
        /**
         * Update the specified resource in storage.
         *
         * @param IlluminateHttpRequest $request
         * @param AppArticle $article
         *
         */ // @return IlluminateHttpResponse
        public function update(Request $request, Article $article)
        {
            if ($article->user->id !== auth()->id()) {
                return response()->json([
                    'error' => '没有权限操作不属于自己的文章',
                ], 402);
            }
    
            if ($request->get('title') === $article->title) {
                $rules = [
                    'title' => 'required|string|max:255',
                    'body' => 'required|string|max:255',
                ];
            } else {
                $rules = [
                    'title' => 'required|string|unique:articles|max:255',
                    'body' => 'required|string|max:255',
                ];
            }
    
            $message = [
                'title.required' => '必须输入title',
                'title.string' => 'title格式为字符串',
                'title.max' => 'title不要超过255',
                'title.unique' => 'title不可重复',
                'body.required' => '必须输入title',
                'body.string' => 'title格式为字符串',
                'body.max' => 'title不要超过255',
            ];
    
            $validator = Validator::make($request->all(), $rules, $message);
    
            if ($validator->fails()) {
                return response()->json($validator->errors()->getMessages(), 302);
            }
    
            $data = $validator->validated();
            if (!$article->update($data)) {
                return response()->json([
                    'error' => 'Article update failed',
                ]);
            }
    
            return response()->json([
                'message' => 'Article updated',
            ], 200);
    
        }
    
        /**
         * Remove the specified resource from storage.
         *
         * @param AppArticle $article
         *
         */ //@return IlluminateHttpResponse
        public function destroy(Article $article)
        {
            $article->delete();
            return response()->json([
                'message' => 'Successfully Deleted',
            ], 200);
        }
    }
    
    

    ApiAuthAuthController.php

    <?php
    
    namespace AppHttpControllersApiAuth;
    
    use AppHttpControllersController;
    use AppUser;
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesHash;
    use IlluminateSupportFacadesValidator;
    
    class AuthController extends Controller
    {
        public function __construct()
        {
            $this->middleware('auth:api')->except(['login', 'register']);
        }
    
        public function login(Request $request)
        {
            $credentials = $request->validate([
                'email' => 'required|string|email',
                'password' => 'required|string',
            ]);
    
            if (!$token = auth('api')->attempt($credentials)) {
                return response()->json([
                    'error' => 'Wrong credentials',
                ], 401);
            }
    
            return $this->responseWithToken($token);
        }
    
        public function logout()
        {
            auth()->logout();
            return response()->json([
                'message' => 'Successfully Logged out',
            ], 200);
        }
    
        public function refresh()
        {
            return $this->responseWithToken(auth()->refresh());
        }
    
        public function me()
        {
            $user = auth('api')->user();
    
            return response()->json([
                'user' => $user,
                'token' => [
                    'access_token' => auth('api')->login($user),
                    'token_type' => 'bearer',
                    'expires_in' => auth('api')->factory()->getTTL() * 60,
                ]
            ]);
        }
    
        public function register(Request $request)
        {
            $rules = [
                'name' => 'required|string|max:255',
                'email' => 'required|string|email|unique:users|max:255',
                'password' => 'required|string|confirmed|min:6|max:255',
            ];
    
    
            $validator = Validator::make($request->all(), $rules);
    
            if ($validator->fails()) {
                return response()->json($validator->errors()->getMessages(), 302);
            }
    
            $data = $validator->validated();
    
            $user = User::create(
                [
                    'name' => $data['name'],
                    'email' => $data['email'],
                    'password' => Hash::make($data['password']),
                ]
            );
    
            $token = auth('api')->login($user);
    
            return $this->responseWithToken($token);
        }
    
        public function responseWithToken($token)
        {
            return response()->json([
                'access_token' => $token,
                'token_type' => 'bearer',
                'expires_in' => auth('api')->factory()->getTTL() * 60,
            ]);
        }
    }
    
    

    GraphQLTypesUserType.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLTypes;
    
    use AppGraphQLFieldsPictureField;
    use GraphQLGraphQL;
    use GraphQLTypeDefinitionType;
    use RebingGraphQLSupportType as GraphQLType;
    
    class UserType extends GraphQLType
    {
        protected $attributes = [
            'name' => 'User',
            'description' => 'A type'
        ];
    
        public function fields(): array
        {
            return [
                'id' => [
                    'type' => Type::nonNull(Type::string()),
                    'description' => 'The id of the user',
                ],
                'email' => [
                    'type' => Type::string(),
                    'description' => 'The email of the user',
                ],
                'articles' => [
                    'type' => Type::listOf(RebingGraphQLSupportFacadesGraphQL::type('Article')),
                    'description' => 'The articles of the user',
                ],
                'picture' => PictureField::class
            ];
        }
    
        protected function resolveEmailField($root, $args)
        {
            return strtolower($root->email);
        }
    
        protected function resolveArticlesField($root, $args)
        {
            if (isset($args['id'])) {
                return $root->articles->where('id', $args['id']);
            }
            return $root->articles;
        }
    }
    
    

    GraphQLTypesArticleType.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLTypes;
    
    use GraphQLTypeDefinitionType;
    use RebingGraphQLSupportFacadesGraphQL;
    use RebingGraphQLSupportType as GraphQLType;
    
    class ArticleType extends GraphQLType
    {
        protected $attributes = [
            'name' => 'Article',
            'description' => 'A type'
        ];
    
        public function fields(): array
        {
            return [
                'id' => [
                    'name' => 'id',
                    'type' => Type::nonNull(Type::string()),
                    'description' => 'The id of the article',
                ],
                'title' => [
                    'name' => 'title',
                    'type' => Type::nonNull(Type::string()),
                    'description' => 'The title of the article',
                ],
                'body' => [
                    'name' => 'body',
                    'type' => Type::nonNull(Type::string()),
                    'description' => 'The body of the article',
                ],
                'status' => [
                    'name' => 'status',
                    'type' => GraphQL::type('ArticleStatusEnum'),
                    'description' => 'The status of the article',
                ]
    
            ];
        }
    }
    
    

    GraphQLTypesHumanType.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLTypes;
    
    use GraphQLTypeDefinitionType;
    use RebingGraphQLSupportFacadesGraphQL;
    use RebingGraphQLSupportType as GraphQLType;
    
    class HumanType extends GraphQLType
    {
        protected $attributes = [
            'name' => 'Human',
            'description' => 'A Human'
        ];
    
        public function fields(): array
        {
            return [
                'id' => [
                    'type' => Type::nonNull(Type::int()),
                    'description' => 'The id of the human.',
                ],
                'appearsIn' => [
                    'type' => Type::nonNull(Type::listOf(GraphQL::type('Episode'))),
                    'description' => 'A list of episodes in which the human has an appearance.'
                ],
                'totalCredits' => [
                    'type' => Type::nonNull(Type::int()),
                    'description' => 'The total amount of credits this human owns.'
                ]
            ];
        }
    
        public function interfaces(): array
        {
            return [
                GraphQL::type('Character')
            ];
        }
    }
    
    

    GraphQLQueriesUserQuery.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLQueries;
    
    use AppUser;
    use Closure;
    use GraphQLTypeDefinitionResolveInfo;
    use GraphQLTypeDefinitionType;
    use IlluminateSupportFacadesAuth;
    use RebingGraphQLSupportFacadesGraphQL;
    use RebingGraphQLSupportQuery;
    use RebingGraphQLSupportSelectFields;
    
    class UserQuery extends Query
    {
        protected $attributes = [
            'name' => 'user',
            'description' => 'A query'
        ];
    
        public function type(): Type
        {
            return Type::listOf(GraphQL::type('User'));
        }
    
        public function args(): array
        {
            return [
                'id' => [
                    'name' => 'id',
                    'type' => Type::string(),
                ],
                'email' => [
                    'name' => 'email',
                    'type' => Type::string(),
                ],
            ];
        }
    
    //    public function resolve($root, $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
    //    {
    ////        /** @var SelectFields $fields */
    ////        $fields = $getSelectFields();
    ////        $select = $fields->getSelect();
    ////        $with = $fields->getRelations();
    ////
    ////        return [
    ////            'The user works',
    ////        ];
    //        if (isset($args['id'])) {
    //            return User::where('id', $args['id'])->get();
    //        } elseif (isset($args['email'])) {
    //            return User::where('email', $args['email'])->get();
    //        } else {
    //            return User::all();
    //        }
    //    }
    
        public function resolve($root, $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
        {
            $fields = $resolveInfo->getFieldSelection($depth = 3);
    
            if (isset($args['id'])) {
                $user = User::where('id', $args['id']);
            } elseif (isset($args['email'])) {
                $user = User::where('email', $args['email']);
            } else {
                $user = User::query();
            }
    
            foreach ($fields as $field => $keys) {
                if ($field == 'articles') {
                    $user->with('articles');
                }
            }
    
            return $user->get();
        }
    
    }
    
    

    GraphQLMutationsUpdateUserPasswordMutation.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLMutations;
    
    use AppUser;
    use Closure;
    use GraphQLTypeDefinitionResolveInfo;
    use GraphQLTypeDefinitionType;
    use IlluminateSupportFacadesAuth;
    use IlluminateSupportFacadesHash;
    use RebingGraphQLSupportFacadesGraphQL;
    use RebingGraphQLSupportMutation;
    use RebingGraphQLSupportSelectFields;
    use TymonJWTAuthJWTAuth;
    
    class UpdateUserPasswordMutation extends Mutation
    {
        protected $attributes = [
            'name' => 'updateUserPassword',
            'description' => 'A mutation'
        ];
    
        public function type(): Type
        {
            return GraphQL::type('User');
        }
    
        public function args(): array
        {
            return [
                'id' => [
                    'name' => 'id',
                    'type' => Type::nonNull(Type::string()),
                ],
                'password' => [
                    'name' => 'password',
                    'type' => Type::nonNull(Type::string()),
                ],
            ];
        }
    
        public function resolve($root, $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
        {
    //        $fields = $getSelectFields();
    //        $select = $fields->getSelect();
    //        $with = $fields->getRelations();
    //
    //        return [];
    
            $user = User::find($args['id']);
    
            if (!$user) {
                return null;
            }
            $user->password = Hash::make($args['password']);
    
            $user->save();
    
            return $user;
        }
    
    }
    
    

    GraphQLMutationsUpdateUserEmailMutation.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLMutations;
    
    use Closure;
    use GraphQLTypeDefinitionResolveInfo;
    use GraphQLTypeDefinitionType;
    use AppUser;
    use RebingGraphQLSupportFacadesGraphQL;
    use RebingGraphQLSupportMutation;
    use RebingGraphQLSupportSelectFields;
    
    class UpdateUserEmailMutation extends Mutation
    {
        protected $attributes = [
            'name' => 'updateUserEmail',
            'description' => 'A mutation'
        ];
    
        public function type(): Type
        {
            return GraphQL::type('User');
        }
    
        public function args(): array
        {
            return [
                'id' => [
                    'name' => 'id',
                    'type' => Type::nonNull(Type::string()),
                ],
                'email' => [
                    'name' => 'email',
                    'type' => Type::nonNull(Type::string()),
                ],
            ];
        }
    
        public function rules(array $args = []): array
        {
            return [
                'id' => 'required',
                'email' => ['required', 'email'],
            ];
        }
    
        public function resolve($root, $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
        {
    //        $fields = $getSelectFields();
    //        $select = $fields->getSelect();
    //        $with = $fields->getRelations();
    //
    //        return [];
    
            $user = User::find($args['id']);
    
            if (!$user) {
                return null;
            }
    
            $user->email = $args['email'];
    
            $user->save();
    
            return $user;
        }
    }
    
    

    GraphQLInterfacesCharacterInterface.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLInterfaces;
    
    use GraphQLTypeDefinitionStringType;
    use GraphQLTypeDefinitionType;
    use RebingGraphQLSupportFacadesGraphQL;
    use RebingGraphQLSupportInterfaceType;
    
    class CharacterInterface extends InterfaceType
    {
        protected $attributes = [
            'name' => 'CharacterInterface',
            'description' => 'Character interface',
        ];
    
        public function resolveType($root): StringType
        {
            // Use the resolveType to resolve the Type which is implemented trough this interface
            $type = $root['type'];
            if ($type === 'human') {
                return GraphQL::type('Human');
            } else if ($type === 'droid') {
                return GraphQL::type('Droid');
            }
        }
    
        public function fields(): array
        {
            return [
                'id' => [
                    'type' => Type::nonNull(Type::int()),
                    'description' => 'The id of the character.'
                ],
                'appearsIn' => [
                    'type' => Type::nonNull(Type::listOf(GraphQL::type('Episode'))),
                    'description' => 'A list of episodes in which the character has an appearance.'
                ],
            ];
        }
    }
    
    

    GraphQLFieldsPictureField.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLFields;
    
    use GraphQLTypeDefinitionType;
    use RebingGraphQLSupportField;
    
    class PictureField extends Field
    {
        protected $attributes = [
            'description' => 'A picture'
        ];
    
        public function type(): Type
        {
            return Type::string();
        }
    
        public function args(): array
        {
            return [
                'width' => [
                    'type' => Type::int(),
                    'description' => 'The width of the picture'
                ],
                'height' => [
                    'type' => Type::int(),
                    'description' => 'The height of the picture'
                ]
            ];
        }
    
        public function resolve($root, $args): string
        {
            $width = isset($args['width']) ? $args['width'] : 100;
            $height = isset($args['height']) ? $args['height'] : 100;
            return 'http://placehold.it/' . $width . 'x' . $height;
        }
    }
    
    

    GraphQLEnumsArticleStatusEnum.php:

    <?php
    
    declare(strict_types=1);
    
    namespace AppGraphQLEnums;
    
    use RebingGraphQLSupportEnumType;
    
    class ArticleStatusEnum extends EnumType
    {
        protected $enumObject = true;
    
        protected $attributes = [
            'name' => 'ArticleStatusEnum',
            'description' => 'Article Status Enum',
    //        'values' => [
    ////            'TEST' => [
    ////                'value' => 1,
    ////                'description' => 'test',
    ////            ],
    //            'APPROVED' => [
    //                'value' => 1,
    //                'description' => 'approved',
    //            ],
    //            'REJECT' => [
    //                'value' => 0,
    //                'description' => 'reject',
    //            ]
    //        ],
        ];
    
        public function values()
        {
            return [
                'APPROVED' => '1',
                'REJECT' => '0'
            ];
        }
    }
    
    

    appExceptionsHandler.php:

    <?php
    
    namespace AppExceptions;
    
    use IlluminateAuthAuthenticationException;
    use IlluminateFoundationExceptionsHandler as ExceptionHandler;
    use Throwable;
    
    class Handler extends ExceptionHandler
    {
        /**
         * A list of the exception types that are not reported.
         *
         * @var array
         */
        protected $dontReport = [
            //
        ];
    
        /**
         * A list of the inputs that are never flashed for validation exceptions.
         *
         * @var array
         */
        protected $dontFlash = [
            'password',
            'password_confirmation',
        ];
    
        /**
         * Report or log an exception.
         *
         * @param Throwable $exception
         * @return void
         *
         * @throws Exception
         */
        public function report(Throwable $exception)
        {
            parent::report($exception);
        }
    
        /**
         * Render an exception into an HTTP response.
         *
         * @param IlluminateHttpRequest $request
         * @param Throwable $exception
         * @return SymfonyComponentHttpFoundationResponse
         *
         * @throws Throwable
         */
        public function render($request, Throwable $exception)
        {
            if ($exception instanceof AuthenticationException) {
                return response()->json(
                    ['error' => '请登录'],
                    401
                );
            }
            return parent::render($request, $exception);
        }
    }
    
    

    configauth.php:

    <?php
    
    return [
    
        /*
        |--------------------------------------------------------------------------
        | Authentication Defaults
        |--------------------------------------------------------------------------
        |
        | This option controls the default authentication "guard" and password
        | reset options for your application. You may change these defaults
        | as required, but they're a perfect start for most applications.
        |
        */
    
        'defaults' => [
            'guard' => 'web',
            'passwords' => 'users',
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Authentication Guards
        |--------------------------------------------------------------------------
        |
        | Next, you may define every authentication guard for your application.
        | Of course, a great default configuration has been defined for you
        | here which uses session storage and the Eloquent user provider.
        |
        | All authentication drivers have a user provider. This defines how the
        | users are actually retrieved out of your database or other storage
        | mechanisms used by this application to persist your user's data.
        |
        | Supported: "session", "token"
        |
        */
    
        'guards' => [
            'web' => [
                'driver' => 'session',
                'provider' => 'users',
            ],
    
            'api' => [
                'driver' => 'jwt',
                'provider' => 'users',
                'hash' => false,
            ],
        ],
    
        /*
        |--------------------------------------------------------------------------
        | User Providers
        |--------------------------------------------------------------------------
        |
        | All authentication drivers have a user provider. This defines how the
        | users are actually retrieved out of your database or other storage
        | mechanisms used by this application to persist your user's data.
        |
        | If you have multiple user tables or models you may configure multiple
        | sources which represent each model / table. These sources may then
        | be assigned to any extra authentication guards you have defined.
        |
        | Supported: "database", "eloquent"
        |
        */
    
        'providers' => [
            'users' => [
                'driver' => 'eloquent',
                'model' => AppUser::class,
            ],
    
            // 'users' => [
            //     'driver' => 'database',
            //     'table' => 'users',
            // ],
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Resetting Passwords
        |--------------------------------------------------------------------------
        |
        | You may specify multiple password reset configurations if you have more
        | than one user table or model in the application and you want to have
        | separate password reset settings based on the specific user types.
        |
        | The expire time is the number of minutes that the reset token should be
        | considered valid. This security feature keeps tokens short-lived so
        | they have less time to be guessed. You may change this as needed.
        |
        */
    
        'passwords' => [
            'users' => [
                'provider' => 'users',
                'table' => 'password_resets',
                'expire' => 60,
                'throttle' => 60,
            ],
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Password Confirmation Timeout
        |--------------------------------------------------------------------------
        |
        | Here you may define the amount of seconds before a password confirmation
        | times out and the user is prompted to re-enter their password via the
        | confirmation screen. By default, the timeout lasts for three hours.
        |
        */
    
        'password_timeout' => 10800,
    
    ];
    
    

    configjwt.php:

    <?php
    
    /*
     * This file is part of jwt-auth.
     *
     * (c) Sean Tymon <tymon148@gmail.com>
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */
    
    return [
    
        /*
        |--------------------------------------------------------------------------
        | JWT Authentication Secret
        |--------------------------------------------------------------------------
        |
        | Don't forget to set this in your .env file, as it will be used to sign
        | your tokens. A helper command is provided for this:
        | `php artisan jwt:secret`
        |
        | Note: This will be used for Symmetric algorithms only (HMAC),
        | since RSA and ECDSA use a private/public key combo (See below).
        |
        */
    
        'secret' => env('JWT_SECRET'),
    
        /*
        |--------------------------------------------------------------------------
        | JWT Authentication Keys
        |--------------------------------------------------------------------------
        |
        | The algorithm you are using, will determine whether your tokens are
        | signed with a random string (defined in `JWT_SECRET`) or using the
        | following public & private keys.
        |
        | Symmetric Algorithms:
        | HS256, HS384 & HS512 will use `JWT_SECRET`.
        |
        | Asymmetric Algorithms:
        | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
        |
        */
    
        'keys' => [
    
            /*
            |--------------------------------------------------------------------------
            | Public Key
            |--------------------------------------------------------------------------
            |
            | A path or resource to your public key.
            |
            | E.g. 'file://path/to/public/key'
            |
            */
    
            'public' => env('JWT_PUBLIC_KEY'),
    
            /*
            |--------------------------------------------------------------------------
            | Private Key
            |--------------------------------------------------------------------------
            |
            | A path or resource to your private key.
            |
            | E.g. 'file://path/to/private/key'
            |
            */
    
            'private' => env('JWT_PRIVATE_KEY'),
    
            /*
            |--------------------------------------------------------------------------
            | Passphrase
            |--------------------------------------------------------------------------
            |
            | The passphrase for your private key. Can be null if none set.
            |
            */
    
            'passphrase' => env('JWT_PASSPHRASE'),
    
        ],
    
        /*
        |--------------------------------------------------------------------------
        | JWT time to live
        |--------------------------------------------------------------------------
        |
        | Specify the length of time (in minutes) that the token will be valid for.
        | Defaults to 1 hour.
        |
        | You can also set this to null, to yield a never expiring token.
        | Some people may want this behaviour for e.g. a mobile app.
        | This is not particularly recommended, so make sure you have appropriate
        | systems in place to revoke the token if necessary.
        | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
        |
        */
    
        'ttl' => env('JWT_TTL', 60),
    
        /*
        |--------------------------------------------------------------------------
        | Refresh time to live
        |--------------------------------------------------------------------------
        |
        | Specify the length of time (in minutes) that the token can be refreshed
        | within. I.E. The user can refresh their token within a 2 week window of
        | the original token being created until they must re-authenticate.
        | Defaults to 2 weeks.
        |
        | You can also set this to null, to yield an infinite refresh time.
        | Some may want this instead of never expiring tokens for e.g. a mobile app.
        | This is not particularly recommended, so make sure you have appropriate
        | systems in place to revoke the token if necessary.
        |
        */
    
        'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
    
        /*
        |--------------------------------------------------------------------------
        | JWT hashing algorithm
        |--------------------------------------------------------------------------
        |
        | Specify the hashing algorithm that will be used to sign the token.
        |
        | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
        | for possible values.
        |
        */
    
        'algo' => env('JWT_ALGO', 'HS256'),
    
        /*
        |--------------------------------------------------------------------------
        | Required Claims
        |--------------------------------------------------------------------------
        |
        | Specify the required claims that must exist in any token.
        | A TokenInvalidException will be thrown if any of these claims are not
        | present in the payload.
        |
        */
    
        'required_claims' => [
            'iss',
            'iat',
            'exp',
            'nbf',
            'sub',
            'jti',
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Persistent Claims
        |--------------------------------------------------------------------------
        |
        | Specify the claim keys to be persisted when refreshing a token.
        | `sub` and `iat` will automatically be persisted, in
        | addition to the these claims.
        |
        | Note: If a claim does not exist then it will be ignored.
        |
        */
    
        'persistent_claims' => [
            // 'foo',
            // 'bar',
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Lock Subject
        |--------------------------------------------------------------------------
        |
        | This will determine whether a `prv` claim is automatically added to
        | the token. The purpose of this is to ensure that if you have multiple
        | authentication models e.g. `AppUser` & `AppOtherPerson`, then we
        | should prevent one authentication request from impersonating another,
        | if 2 tokens happen to have the same id across the 2 different models.
        |
        | Under specific circumstances, you may want to disable this behaviour
        | e.g. if you only have one authentication model, then you would save
        | a little on token size.
        |
        */
    
        'lock_subject' => true,
    
        /*
        |--------------------------------------------------------------------------
        | Leeway
        |--------------------------------------------------------------------------
        |
        | This property gives the jwt timestamp claims some "leeway".
        | Meaning that if you have any unavoidable slight clock skew on
        | any of your servers then this will afford you some level of cushioning.
        |
        | This applies to the claims `iat`, `nbf` and `exp`.
        |
        | Specify in seconds - only if you know you need it.
        |
        */
    
        'leeway' => env('JWT_LEEWAY', 0),
    
        /*
        |--------------------------------------------------------------------------
        | Blacklist Enabled
        |--------------------------------------------------------------------------
        |
        | In order to invalidate tokens, you must have the blacklist enabled.
        | If you do not want or need this functionality, then set this to false.
        |
        */
    
        'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
    
        /*
        | -------------------------------------------------------------------------
        | Blacklist Grace Period
        | -------------------------------------------------------------------------
        |
        | When multiple concurrent requests are made with the same JWT,
        | it is possible that some of them fail, due to token regeneration
        | on every request.
        |
        | Set grace period in seconds to prevent parallel request failure.
        |
        */
    
        'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
    
        /*
        |--------------------------------------------------------------------------
        | Cookies encryption
        |--------------------------------------------------------------------------
        |
        | By default Laravel encrypt cookies for security reason.
        | If you decide to not decrypt cookies, you will have to configure Laravel
        | to not encrypt your cookie token by adding its name into the $except
        | array available in the middleware "EncryptCookies" provided by Laravel.
        | see https://laravel.com/docs/master/responses#cookies-and-encryption
        | for details.
        |
        | Set it to true if you want to decrypt cookies.
        |
        */
    
        'decrypt_cookies' => false,
    
        /*
        |--------------------------------------------------------------------------
        | Providers
        |--------------------------------------------------------------------------
        |
        | Specify the various providers used throughout the package.
        |
        */
    
        'providers' => [
    
            /*
            |--------------------------------------------------------------------------
            | JWT Provider
            |--------------------------------------------------------------------------
            |
            | Specify the provider that is used to create and decode the tokens.
            |
            */
    
            'jwt' => TymonJWTAuthProvidersJWTLcobucci::class,
    
            /*
            |--------------------------------------------------------------------------
            | Authentication Provider
            |--------------------------------------------------------------------------
            |
            | Specify the provider that is used to authenticate users.
            |
            */
    
            'auth' => TymonJWTAuthProvidersAuthIlluminate::class,
    
            /*
            |--------------------------------------------------------------------------
            | Storage Provider
            |--------------------------------------------------------------------------
            |
            | Specify the provider that is used to store tokens in the blacklist.
            |
            */
    
            'storage' => TymonJWTAuthProvidersStorageIlluminate::class,
    
        ],
    
    ];
    
    

    api.php:

    <?php
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesRoute;
    
    /*
    |--------------------------------------------------------------------------
    | API Routes
    |--------------------------------------------------------------------------
    |php
    | Here is where you can register API routes for your application. These
    | routes are loaded by the RouteServiceProvider within a group which
    | is assigned the "api" middleware group. Enjoy building your API!
    |
    */
    
    Route::middleware('auth:api')->get('/user', function (Request $request) {
        return $request->user();
    });
    
    Route::apiResource('articles', 'ArticleController');
    
    Route::post('login', 'ApiAuthAuthController@login')->middleware('guest')->name('login.api');
    Route::post('logout', 'ApiAuthAuthController@logout')->middleware('auth:api')->name('logout.api');
    Route::post('refresh', 'ApiAuthAuthController@refresh')->middleware('auth:api')->name('refresh.api');
    Route::post('register', 'ApiAuthAuthController@register')->middleware('guest')->name('register.api');
    Route::get('me', 'ApiAuthAuthController@me')->middleware('auth:api');
    
    
    

    .env:

    APP_NAME=Laravel
    APP_ENV=local
    APP_KEY=base64:UkfFliQ/y1iTF4uW8QVQar7t855lRmD5Ap3i8rySShw=
    APP_DEBUG=true
    APP_URL=http://localhost
    
    LOG_CHANNEL=stack
    
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=graphql
    DB_USERNAME=root
    DB_PASSWORD=
    
    BROADCAST_DRIVER=log
    CACHE_DRIVER=file
    QUEUE_CONNECTION=sync
    SESSION_DRIVER=file
    SESSION_LIFETIME=120
    
    REDIS_HOST=127.0.0.1
    REDIS_PASSWORD=null
    REDIS_PORT=6379
    
    MAIL_MAILER=smtp
    MAIL_HOST=smtp.mailtrap.io
    MAIL_PORT=2525
    MAIL_USERNAME=null
    MAIL_PASSWORD=null
    MAIL_ENCRYPTION=null
    MAIL_FROM_ADDRESS=null
    MAIL_FROM_NAME="${APP_NAME}"
    
    AWS_ACCESS_KEY_ID=
    AWS_SECRET_ACCESS_KEY=
    AWS_DEFAULT_REGION=us-east-1
    AWS_BUCKET=
    
    PUSHER_APP_ID=
    PUSHER_APP_KEY=
    PUSHER_APP_SECRET=
    PUSHER_APP_CLUSTER=mt1
    
    MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
    MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
    
    JWT_SECRET=1Je1e5gh0ZxO5XJ7ScOJJZq6WAbX0Uez7yP5Vkvf1tyyUkioTziZ0aFpZ1tDx9KP
    
    

    CreateUsersTable:

    <?php
    
    use IlluminateDatabaseMigrationsMigration;
    use IlluminateDatabaseSchemaBlueprint;
    use IlluminateSupportFacadesSchema;
    
    class CreateUsersTable extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('users', function (Blueprint $table) {
                $table->id();
                $table->string('name');
                $table->string('email')->unique();
                $table->timestamp('email_verified_at')->nullable();
                $table->string('password');
                $table->softDeletes();
                $table->rememberToken();
                $table->timestamps();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::dropIfExists('users');
        }
    }
    
    

    CreateArticlesTable

    <?php
    
    use IlluminateDatabaseMigrationsMigration;
    use IlluminateDatabaseSchemaBlueprint;
    use IlluminateSupportFacadesSchema;
    
    class CreateArticlesTable extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('articles', function (Blueprint $table) {
                $table->id();
                $table->string('title');
                $table->text('body');
                $table->unsignedBigInteger('user_id');
                $table->softDeletes();
                $table->timestamps();
            });
            Schema::table('articles', function (Blueprint $table) {
                $table->foreign('user_id')
                    ->references('id')
                    ->on('users')
                    ->cascadeOnDelete();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::dropIfExists('articles');
        }
    }
    
    

    ArticleFactory.php:

    <?php
    
    /** @var IlluminateDatabaseEloquentFactory $factory */
    
    use AppArticle;
    use FakerGenerator as Faker;
    
    $factory->define(Article::class, function (Faker $faker) {
        return [
            //
            'title' => $faker->sentence,
            'body' => $faker->paragraph,
        ];
    });
    
    

    ArticleTableSeeder.php:

    <?php
    
    use IlluminateDatabaseSeeder;
    
    class ArticleTableSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
         */
        public function run()
        {
            //
            factory(AppArticle::class, 50)->create(
                ['user_id' => 1]
            );
        }
    }
    
    

    DatabaseSeeder.php:

    <?php
    
    use IlluminateDatabaseSeeder;
    
    class DatabaseSeeder extends Seeder
    {
        /**
         * Seed the application's database.
         *
         * @return void
         */
        public function run()
        {
            // $this->call(UserSeeder::class);
            $this->call(UserTableSeeder::class);
            $this->call(ArticleTableSeeder::class);
        }
    }
    
    

    UserTableSeeder.php:

    <?php
    
    use AppUser;
    use IlluminateDatabaseSeeder;
    
    class UserTableSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
         */
        public function run()
        {
            //
            factory(User::class, 10)->create();
        }
    }
    
    

    Github:

    https://github.com/dzkjz/graphql_learn1

    https://github.com/dzkjz/graphql_learn1.git

  • 相关阅读:
    topcoder srm 320 div1
    topcoder srm 325 div1
    topcoder srm 330 div1
    topcoder srm 335 div1
    topcoder srm 340 div1
    topcoder srm 300 div1
    topcoder srm 305 div1
    topcoder srm 310 div1
    topcoder srm 315 div1
    如何统计iOS产品不同渠道的下载量?
  • 原文地址:https://www.cnblogs.com/dzkjz/p/12796857.html
Copyright © 2011-2022 走看看