• 基于 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

  • 相关阅读:
    禁止select下拉框的其中某个选择项不能被选择
    jQuery 增加 删除 修改select option
    jquery ajax后台向前台传list 前台用jquery $.each遍历list
    转载 java枚举类型enum的使用 (原文地址:http://blog.csdn.net/wgw335363240/article/details/6359614)
    C#性能优化实践 资料整理
    MySql 优化 网上资料
    第06组 Alpha冲刺(4/6)
    第06组 Alpha冲刺(3/6)
    第06组 Alpha冲刺(2/6)
    第06组 Alpha冲刺(1/6)
  • 原文地址:https://www.cnblogs.com/dzkjz/p/12796857.html
Copyright © 2020-2023  润新知