使用 Lighthouse 构建 GraphQL 服务器的入门,从零开始为一个简单博客创建 GraphQL API
1、使用 Lighthouse 构建 GraphQL 服务器的入门。参考网址:https://lighthouse-php.cn/tutorial/
2、从零开始为一个简单博客创建 GraphQL API。通过 Composer 创建一个新的 Laravel 项目:lighthouse-tutorial
PS E:\wwwroot> composer create-project laravel/laravel lighthouse-tutorial Creating a "laravel/laravel" project at "./lighthouse-tutorial" Installing laravel/laravel (v8.6.10) - Installing laravel/laravel (v8.6.10): Extracting archive Created project in E:\wwwroot\lighthouse-tutorial > @php -r "file_exists('.env') || copy('.env.example', '.env');" Loading composer repositories with package information Updating dependencies Lock file operations: 110 installs, 0 updates, 0 removals - Locking asm89/stack-cors (v2.0.5) - Locking brick/math (0.9.3) - Locking dflydev/dot-access-data (v3.0.1) - Locking doctrine/inflector (2.0.4) - Locking doctrine/instantiator (1.4.0) - Locking doctrine/lexer (1.2.1) - Locking dragonmantank/cron-expression (v3.2.3) - Locking egulias/email-validator (2.1.25) - Locking facade/flare-client-php (1.9.1) - Locking facade/ignition (2.17.4) - Locking facade/ignition-contracts (1.0.2) - Locking fakerphp/faker (v1.17.0) - Locking filp/whoops (2.14.5) - Locking fruitcake/laravel-cors (v2.0.5) - Locking graham-campbell/result-type (v1.0.4) - Locking guzzlehttp/guzzle (7.4.1) - Locking guzzlehttp/promises (1.5.1) - Locking guzzlehttp/psr7 (2.1.0) - Locking hamcrest/hamcrest-php (v2.0.1) - Locking laravel/framework (v8.78.1) - Locking laravel/sail (v1.12.12) - Locking laravel/sanctum (v2.13.0) - Locking laravel/serializable-closure (v1.0.5) - Locking laravel/tinker (v2.6.3) - Locking league/commonmark (2.1.1) - Locking league/config (v1.1.1) - Locking league/flysystem (1.1.9) - Locking league/mime-type-detection (1.9.0) - Locking mockery/mockery (1.4.4) - Locking monolog/monolog (2.3.5) - Locking myclabs/deep-copy (1.10.2) - Locking nesbot/carbon (2.55.2) - Locking nette/schema (v1.2.2) - Locking nette/utils (v3.2.6) - Locking nikic/php-parser (v4.13.2) - Locking nunomaduro/collision (v5.11.0) - Locking opis/closure (3.6.2) - Locking phar-io/manifest (2.0.3) - Locking phar-io/version (3.1.0) - Locking phpdocumentor/reflection-common (2.2.0) - Locking phpdocumentor/reflection-docblock (5.3.0) - Locking phpdocumentor/type-resolver (1.6.0) - Locking phpoption/phpoption (1.8.1) - Locking phpspec/prophecy (v1.15.0) - Locking phpunit/php-code-coverage (9.2.10) - Locking phpunit/php-file-iterator (3.0.6) - Locking phpunit/php-invoker (3.1.1) - Locking phpunit/php-text-template (2.0.4) - Locking phpunit/php-timer (5.0.3) - Locking phpunit/phpunit (9.5.11) - Locking psr/container (1.1.2) - Locking psr/event-dispatcher (1.0.0) - Locking psr/http-client (1.0.1) - Locking psr/http-factory (1.0.1) - Locking psr/http-message (1.0.1) - Locking psr/log (1.1.4) - Locking psr/simple-cache (1.0.1) - Locking psy/psysh (v0.10.12) - Locking ralouphie/getallheaders (3.0.3) - Locking ramsey/collection (1.2.2) - Locking ramsey/uuid (4.2.3) - Locking sebastian/cli-parser (1.0.1) - Locking sebastian/code-unit (1.0.8) - Locking sebastian/code-unit-reverse-lookup (2.0.3) - Locking sebastian/comparator (4.0.6) - Locking sebastian/complexity (2.0.2) - Locking sebastian/diff (4.0.4) - Locking sebastian/environment (5.1.3) - Locking sebastian/exporter (4.0.4) - Locking sebastian/global-state (5.0.3) - Locking sebastian/lines-of-code (1.0.3) - Locking sebastian/object-enumerator (4.0.4) - Locking sebastian/object-reflector (2.0.4) - Locking sebastian/recursion-context (4.0.4) - Locking sebastian/resource-operations (3.0.3) - Locking sebastian/type (2.3.4) - Locking sebastian/version (3.0.2) - Locking swiftmailer/swiftmailer (v6.3.0) - Locking symfony/console (v5.4.2) - Locking symfony/css-selector (v5.4.2) - Locking symfony/deprecation-contracts (v2.5.0) - Locking symfony/error-handler (v5.4.2) - Locking symfony/event-dispatcher (v5.4.0) - Locking symfony/event-dispatcher-contracts (v2.5.0) - Locking symfony/finder (v5.4.2) - Locking symfony/http-foundation (v5.4.2) - Locking symfony/http-kernel (v5.4.2) - Locking symfony/mime (v5.4.2) - Locking symfony/polyfill-ctype (v1.24.0) - Locking symfony/polyfill-iconv (v1.24.0) - Locking symfony/polyfill-intl-grapheme (v1.24.0) - Locking symfony/polyfill-intl-idn (v1.24.0) - Locking symfony/polyfill-intl-normalizer (v1.24.0) - Locking symfony/polyfill-mbstring (v1.24.0) - Locking symfony/polyfill-php72 (v1.24.0) - Locking symfony/polyfill-php73 (v1.24.0) - Locking symfony/polyfill-php80 (v1.24.0) - Locking symfony/polyfill-php81 (v1.24.0) - Locking symfony/process (v5.4.2) - Locking symfony/routing (v5.4.0) - Locking symfony/service-contracts (v2.5.0) - Locking symfony/string (v5.4.2) - Locking symfony/translation (v5.4.2) - Locking symfony/translation-contracts (v2.5.0) - Locking symfony/var-dumper (v5.4.2) - Locking theseer/tokenizer (1.2.1) - Locking tijsverkoyen/css-to-inline-styles (2.2.4) - Locking vlucas/phpdotenv (v5.4.1) - Locking voku/portable-ascii (1.5.6) - Locking webmozart/assert (1.10.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 110 installs, 0 updates, 0 removals - Downloading nunomaduro/collision (v5.11.0) - Installing doctrine/inflector (2.0.4): Extracting archive - Installing doctrine/lexer (1.2.1): Extracting archive - Installing symfony/polyfill-ctype (v1.24.0): Extracting archive - Installing webmozart/assert (1.10.0): Extracting archive - Installing dragonmantank/cron-expression (v3.2.3): Extracting archive - Installing symfony/polyfill-php80 (v1.24.0): Extracting archive - Installing symfony/polyfill-mbstring (v1.24.0): Extracting archive - Installing symfony/var-dumper (v5.4.2): Extracting archive - Installing symfony/polyfill-intl-normalizer (v1.24.0): Extracting archive - Installing symfony/polyfill-intl-grapheme (v1.24.0): Extracting archive - Installing symfony/string (v5.4.2): Extracting archive - Installing symfony/deprecation-contracts (v2.5.0): Extracting archive - Installing psr/container (1.1.2): Extracting archive - Installing symfony/service-contracts (v2.5.0): Extracting archive - Installing symfony/polyfill-php73 (v1.24.0): Extracting archive - Installing symfony/console (v5.4.2): Extracting archive - Installing psr/log (1.1.4): Extracting archive - Installing monolog/monolog (2.3.5): Extracting archive - Installing voku/portable-ascii (1.5.6): Extracting archive - Installing phpoption/phpoption (1.8.1): Extracting archive - Installing graham-campbell/result-type (v1.0.4): Extracting archive - Installing vlucas/phpdotenv (v5.4.1): Extracting archive - Installing symfony/css-selector (v5.4.2): Extracting archive - Installing tijsverkoyen/css-to-inline-styles (2.2.4): Extracting archive - Installing symfony/routing (v5.4.0): Extracting archive - Installing symfony/process (v5.4.2): Extracting archive - Installing symfony/polyfill-php72 (v1.24.0): Extracting archive - Installing symfony/polyfill-intl-idn (v1.24.0): Extracting archive - Installing symfony/mime (v5.4.2): Extracting archive - Installing symfony/http-foundation (v5.4.2): Extracting archive - Installing psr/event-dispatcher (1.0.0): Extracting archive - Installing symfony/event-dispatcher-contracts (v2.5.0): Extracting archive - Installing symfony/event-dispatcher (v5.4.0): Extracting archive - Installing symfony/error-handler (v5.4.2): Extracting archive - Installing symfony/http-kernel (v5.4.2): Extracting archive - Installing symfony/finder (v5.4.2): Extracting archive - Installing symfony/polyfill-iconv (v1.24.0): Extracting archive - Installing egulias/email-validator (2.1.25): Extracting archive - Installing swiftmailer/swiftmailer (v6.3.0): Extracting archive - Installing symfony/polyfill-php81 (v1.24.0): Extracting archive - Installing ramsey/collection (1.2.2): Extracting archive - Installing brick/math (0.9.3): Extracting archive - Installing ramsey/uuid (4.2.3): Extracting archive - Installing psr/simple-cache (1.0.1): Extracting archive - Installing opis/closure (3.6.2): Extracting archive - Installing symfony/translation-contracts (v2.5.0): Extracting archive - Installing symfony/translation (v5.4.2): Extracting archive - Installing nesbot/carbon (2.55.2): Extracting archive - Installing league/mime-type-detection (1.9.0): Extracting archive - Installing league/flysystem (1.1.9): Extracting archive - Installing nette/utils (v3.2.6): Extracting archive - Installing nette/schema (v1.2.2): Extracting archive - Installing dflydev/dot-access-data (v3.0.1): Extracting archive - Installing league/config (v1.1.1): Extracting archive - Installing league/commonmark (2.1.1): Extracting archive - Installing laravel/serializable-closure (v1.0.5): Extracting archive - Installing laravel/framework (v8.78.1): Extracting archive - Installing facade/ignition-contracts (1.0.2): Extracting archive - Installing facade/flare-client-php (1.9.1): Extracting archive - Installing facade/ignition (2.17.4): Extracting archive - Installing fakerphp/faker (v1.17.0): Extracting archive - Installing asm89/stack-cors (v2.0.5): Extracting archive - Installing fruitcake/laravel-cors (v2.0.5): Extracting archive - Installing psr/http-message (1.0.1): Extracting archive - Installing psr/http-client (1.0.1): Extracting archive - Installing ralouphie/getallheaders (3.0.3): Extracting archive - Installing psr/http-factory (1.0.1): Extracting archive - Installing guzzlehttp/psr7 (2.1.0): Extracting archive - Installing guzzlehttp/promises (1.5.1): Extracting archive - Installing guzzlehttp/guzzle (7.4.1): Extracting archive - Installing laravel/sail (v1.12.12): Extracting archive - Installing laravel/sanctum (v2.13.0): Extracting archive - Installing nikic/php-parser (v4.13.2): Extracting archive - Installing psy/psysh (v0.10.12): Extracting archive - Installing laravel/tinker (v2.6.3): Extracting archive - Installing hamcrest/hamcrest-php (v2.0.1): Extracting archive - Installing mockery/mockery (1.4.4): Extracting archive - Installing filp/whoops (2.14.5): Extracting archive - Installing nunomaduro/collision (v5.11.0): Extracting archive - Installing phpdocumentor/reflection-common (2.2.0): Extracting archive - Installing phpdocumentor/type-resolver (1.6.0): Extracting archive - Installing phpdocumentor/reflection-docblock (5.3.0): Extracting archive - Installing sebastian/version (3.0.2): Extracting archive - Installing sebastian/type (2.3.4): Extracting archive - Installing sebastian/resource-operations (3.0.3): Extracting archive - Installing sebastian/recursion-context (4.0.4): Extracting archive - Installing sebastian/object-reflector (2.0.4): Extracting archive - Installing sebastian/object-enumerator (4.0.4): Extracting archive - Installing sebastian/global-state (5.0.3): Extracting archive - Installing sebastian/exporter (4.0.4): Extracting archive - Installing sebastian/environment (5.1.3): Extracting archive - Installing sebastian/diff (4.0.4): Extracting archive - Installing sebastian/comparator (4.0.6): Extracting archive - Installing sebastian/code-unit (1.0.8): Extracting archive - Installing sebastian/cli-parser (1.0.1): Extracting archive - Installing phpunit/php-timer (5.0.3): Extracting archive - Installing phpunit/php-text-template (2.0.4): Extracting archive - Installing phpunit/php-invoker (3.1.1): Extracting archive - Installing phpunit/php-file-iterator (3.0.6): Extracting archive - Installing theseer/tokenizer (1.2.1): Extracting archive - Installing sebastian/lines-of-code (1.0.3): Extracting archive - Installing sebastian/code-unit-reverse-lookup (2.0.3): Extracting archive - Installing phpunit/php-code-coverage (9.2.10): Extracting archive - Installing doctrine/instantiator (1.4.0): Extracting archive - Installing phpspec/prophecy (v1.15.0): Extracting archive - Installing phar-io/version (3.1.0): Extracting archive - Installing phar-io/manifest (2.0.3): Extracting archive - Installing myclabs/deep-copy (1.10.2): Extracting archive - Installing phpunit/phpunit (9.5.11): Extracting archive 70 package suggestions were added by new dependencies, use `composer suggest` to see details. Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead. Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fruitcake/laravel-cors Discovered Package: laravel/sail Discovered Package: laravel/sanctum Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. 77 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan vendor:publish --tag=laravel-assets --ansi --force No publishable resources for tag [laravel-assets]. Publishing complete. > @php artisan key:generate --ansi Application key set successfully.
3、创建数据库:lighthouse_tutorial,配置 .env 中的数据库连接。运行数据库迁移以创建 users 表
PS E:\wwwroot\lighthouse-tutorial> php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (42.37ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (36.39ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (40.11ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (53.85ms)
4、在数据库中植入一些假的用户数据。factory(‘App\User’, 10)->create(); 在 Laravel 8 已经不可用。需要使用 User::factory()->count(10)->create();。如图1
PS E:\wwwroot\lighthouse-tutorial> php artisan tinker Psy Shell v0.10.12 (PHP 7.4.27 — cli) by Justin Hileman >>> factory('App\User', 10)->create(); PHP Fatal error: Call to undefined function factory() in Psy Shell code on line 1 >>> App\Factory('App\User', 10)->create(); PHP Fatal error: Call to undefined function App\Factory() in Psy Shell code on line 1 >>> User::factory()->count(50)->create(); [!] Aliasing 'User' to 'App\Models\User' for this Tinker session. => Illuminate\Database\Eloquent\Collection {#3581 all: [ App\Models\User {#3585 name: "Ashton Tremblay", email: "letha42@example.net", email_verified_at: "2022-01-12 05:51:41", #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", #remember_token: "k1o1mrmP7o", updated_at: "2022-01-12 05:51:41", created_at: "2022-01-12 05:51:41", id: 1, }, App\Models\User {#3592 name: "Rene Powlowski", email: "hkovacek@example.org", email_verified_at: "2022-01-12 05:51:41", #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", #remember_token: "64Cl19QXs3", updated_at: "2022-01-12 05:51:41", created_at: "2022-01-12 05:51:41", id: 2, }, ... ], } >>>
5、安装 Lighthouse,将使用 Lighthouse 作为 GraphQL 服务器。
PS E:\wwwroot\lighthouse-tutorial> composer require nuwave/lighthouse Using version ^5.35 for nuwave/lighthouse ./composer.json has been updated Running composer update nuwave/lighthouse Loading composer repositories with package information Updating dependencies Lock file operations: 5 installs, 0 updates, 0 removals - Locking haydenpierce/class-finder (0.4.3) - Locking laragraph/utils (v1.1.1) - Locking nuwave/lighthouse (v5.35.0) - Locking thecodingmachine/safe (v1.3.3) - Locking webonyx/graphql-php (v14.11.3) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 5 installs, 0 updates, 0 removals - Downloading nuwave/lighthouse (v5.35.0) - Installing webonyx/graphql-php (v14.11.3): Extracting archive - Installing thecodingmachine/safe (v1.3.3): Extracting archive - Installing laragraph/utils (v1.1.1): Extracting archive - Installing haydenpierce/class-finder (0.4.3): Extracting archive - Installing nuwave/lighthouse (v5.35.0): Extracting archive 6 package suggestions were added by new dependencies, use `composer suggest` to see details. Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead. Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fruitcake/laravel-cors Discovered Package: laravel/sail Discovered Package: laravel/sanctum Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Discovered Package: nuwave/lighthouse Package manifest generated successfully. 79 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan vendor:publish --tag=laravel-assets --ansi --force No publishable resources for tag [laravel-assets]. Publishing complete. PS E:\wwwroot\lighthouse-tutorial>
6、使用 Altair GraphQL Client 作为 GraphQL 查询的 IDE (在线调试工具)。它就好像 GraphQL 专用的 Postman,拥有很多神奇的功能。在 Chrome 浏览器的网上应用商店中搜索:GraphQL,排名第一的就是。如图2
7、将默认模式发布到 graphql/schema.graphql。报错:No publishable resources for tag [schema].
PS E:\wwwroot\lighthouse-tutorial> php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema No publishable resources for tag [schema]. Publishing complete. PS E:\wwwroot\lighthouse-tutorial>
8、建议参考英文的最新文档:https://lighthouse-php.com/tutorial 。将默认模式发布到 graphql/schema.graphql。发布完成。
PS E:\wwwroot\lighthouse-tutorial> php artisan vendor:publish --tag=lighthouse-schema Copied File [\vendor\nuwave\lighthouse\src\default-schema.graphql] To [\graphql\schema.graphql] Publishing complete. PS E:\wwwroot\lighthouse-tutorial>
9、在 Nginx 中配置虚拟主机:lighthouse-tutorial.local 。在 Altair GraphQL Client 中打开网址:http://lighthouse-tutorial.local/graplql 。如图3
server { listen 80; server_name lighthouse-tutorial.local; root E:/wwwroot/lighthouse-tutorial/public; access_log logs/lighthouse-tutorial.local.access.log; error_log logs/lighthouse-tutorial.local.error.log; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { # fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } }
请求:
query{ user(id: 1){ id name email created_at updated_at } }
响应:
{ "data": { "user": { "id": "1", "name": "Ashton Tremblay", "email": "letha42@example.net", "created_at": "2022-01-12 05:51:41", "updated_at": "2022-01-12 05:51:41" } } }
10、为我们的 posts 和 comments 定义模型和迁移。运行迁移。将 posts 模型添加到 app/User.php。
11、The Schema,修改 graphql/schema.graphql 并根据我们创建的有说服力的模型来定义我们的 posts 的模式。我们添加了两个用于查询文章到 Query 类型的最后面。 @all 返回所有内容的列表 Post 模型 。 @find 和 @eq 是通过其 ID 组合检索单个 Post。我们还可以通过添加额外的类型定义,明确定义我们的数据模型。如图4
"A date string with format `Y-m-d`, e.g. `2011-05-23`." scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") "A datetime string with format `Y-m-d H:i:s`, e.g. `2018-05-23 13:43:32`." scalar DateTime @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime") type Query { users: [User!]! @paginate(defaultCount: 10) user(id: ID @eq): User @find posts: [Post!]! @all post(id: Int! @eq): Post @find } type User { id: ID! name: String! email: String! created_at: DateTime! updated_at: DateTime! posts: [Post!]! @hasMany } type Post { id: ID! title: String! content: String! author: User! @belongsTo comments: [Comment!]! @hasMany } type Comment { id: ID! reply: String! post: Post! @belongsTo }
12、最终测试,在你的数据库中插入一些假数据,你可以使用 Laravel seeders 来完成。编辑模型工厂文件:database/factories/PostFactory.php、database/factories/CommentFactory.php。
database/factories/PostFactory.php
<?php namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; use App\Models\Post; class PostFactory extends Factory { protected $model = Post::class; /** * Define the model's default state. * * @return array */ public function definition() { $sentence = $this->faker->sentence(); return [ 'author_id' => rand(1, 100), 'title' => $sentence, 'content' => $this->faker->text(), ]; } }
database/factories/CommentFactory.php
<?php namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; use App\Models\Comment; class CommentFactory extends Factory { protected $model = Comment::class; /** * Define the model's default state. * * @return array */ public function definition() { return [ 'post_id' => rand(1, 100), 'reply' => $this->faker->sentence(), ]; } }
13、完成数据工厂的定制后,接下来我们开始书写填充部分。编辑数据填充文件:database/seeders/UsersTableSeeder.php、database/seeders/PostsTableSeeder.php、database/seeders/CommentsTableSeeder.php。
database/seeders/UsersTableSeeder.php
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use App\Models\User; class UsersTableSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { User::factory()->count(100)->create(); } }
database/seeders/PostsTableSeeder.php
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use App\Models\Post; class PostsTableSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { Post::factory()->count(100)->create(); } }
database/seeders/CommentsTableSeeder.php
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use App\Models\Comment; class CommentsTableSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { Comment::factory()->count(100)->create(); } }
14、注册数据填充。编辑 database/seeders/DatabaseSeeder.php
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(UsersTableSeeder::class); $this->call(PostsTableSeeder::class); $this->call(CommentsTableSeeder::class); } }
15、执行数据填充命令:php artisan migrate:refresh –seed,报错:Call to undefined method App\Models\Post::factory()。如图5
PS E:\wwwroot\lighthouse-tutorial> php artisan migrate:refresh --seed Rolling back: 2022_01_12_062416_create_comments_table Rolled back: 2022_01_12_062416_create_comments_table (16.76ms) Rolling back: 2022_01_12_062104_create_posts_table Rolled back: 2022_01_12_062104_create_posts_table (8.20ms) Rolling back: 2019_12_14_000001_create_personal_access_tokens_table Rolled back: 2019_12_14_000001_create_personal_access_tokens_table (9.17ms) Rolling back: 2019_08_19_000000_create_failed_jobs_table Rolled back: 2019_08_19_000000_create_failed_jobs_table (8.87ms) Rolling back: 2014_10_12_100000_create_password_resets_table Rolled back: 2014_10_12_100000_create_password_resets_table (9.72ms) Rolling back: 2014_10_12_000000_create_users_table Rolled back: 2014_10_12_000000_create_users_table (7.01ms) Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (28.61ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (31.30ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (31.69ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (39.67ms) Migrating: 2022_01_12_062104_create_posts_table Migrated: 2022_01_12_062104_create_posts_table (17.16ms) Migrating: 2022_01_12_062416_create_comments_table Migrated: 2022_01_12_062416_create_comments_table (20.25ms) Seeding: Database\Seeders\UsersTableSeeder Seeded: Database\Seeders\UsersTableSeeder (1,839.80ms) Seeding: Database\Seeders\PostsTableSeeder BadMethodCallException Call to undefined method App\Models\Post::factory() at E:\wwwroot\lighthouse-tutorial\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php:71 67▕ * @throws \BadMethodCallException 68▕ */ 69▕ protected static function throwBadMethodCallException($method) 70▕ { ➜ 71▕ throw new BadMethodCallException(sprintf( 72▕ 'Call to undefined method %s::%s()', static::class, $method 73▕ )); 74▕ } 75▕ } • Bad Method Call: Did you mean App\Models\Post::author() ? 1 E:\wwwroot\lighthouse-tutorial\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php:36 Illuminate\Database\Eloquent\Model::throwBadMethodCallException("factory") 2 E:\wwwroot\lighthouse-tutorial\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:2132 Illuminate\Database\Eloquent\Model::forwardCallTo(Object(Illuminate\Database\Eloquent\Builder), "factory", []) PS E:\wwwroot\lighthouse-tutorial>
16、编辑数据模型文件,app/Models/Post.php、app/Models/Comment.php,引入 HasFactory。
app/Models/Post.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Post extends Model { use HasFactory; public function author(): BelongsTo { return $this->belongsTo(User::class, 'author_id'); } public function comments(): HasMany { return $this->hasMany(Comment::class); } }
app/Models/Comment.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Comment extends Model { use HasFactory; public function post(): BelongsTo { return $this->belongsTo(Post::class); } }
17、执行数据填充成功后,访问 /graphql 并尝试以下的查询。获取到数据库中所有 posts 的列表,以及全部 comments 和 user 的 name。符合预期。如图6
请求:
query { posts { id title author { name } comments { id reply } } }
响应:
{ "data": { "posts": [ { "id": "1", "title": "Optio earum deserunt fuga qui quia hic.", "author": { "name": "Mrs. Lottie Kuhlman IV" }, "comments": [] }, { "id": "2", "title": "Soluta eius quisquam consequatur accusantium distinctio minima.", "author": { "name": "Otilia Marvin" }, "comments": [] }, { "id": "3", "title": "Unde harum commodi rerum nam dolores vel eligendi.", "author": { "name": "Alfreda Crooks" }, "comments": [ { "id": "14", "reply": "Laborum exercitationem odit voluptas cumque adipisci." } ] }, { "id": "4", "title": "Praesentium iste nisi aut reprehenderit aliquam.", "author": { "name": "Hassan Goyette MD" }, "comments": [] }, { "id": "5", "title": "Atque illo consequatur eveniet.", "author": { "name": "Estevan Mann" }, "comments": [] }, { "id": "6", "title": "Praesentium et eaque soluta.", "author": { "name": "Prof. Rosalia Cartwright Sr." }, "comments": [ { "id": "5", "reply": "Omnis doloremque tempore ut laboriosam nihil." } ] }, { "id": "7", "title": "Assumenda consequatur quo perspiciatis reiciendis.", "author": { "name": "Nestor Bauch" }, "comments": [ { "id": "70", "reply": "Error quia natus inventore aliquid praesentium et." } ] }, { "id": "8", "title": "Nesciunt eos enim minus ipsa non sed minus.", "author": { "name": "Reggie Batz" }, "comments": [ { "id": "22", "reply": "Magnam et cum reiciendis et." } ] }, { "id": "9", "title": "In et quasi dolorem aliquam nemo sequi.", "author": { "name": "Carroll Cartwright" }, "comments": [ { "id": "73", "reply": "Cumque harum id officiis eum cumque." } ] }, { "id": "10", "title": "Consequatur modi enim fugit ut eos.", "author": { "name": "Kiley Connelly" }, "comments": [ { "id": "71", "reply": "Aut harum fuga quos veniam quia perspiciatis." }, { "id": "90", "reply": "Harum unde debitis sunt sint tenetur temporibus voluptates." } ] }, ... ] } }
18、随机选择 posts 的 id 等于 10 的数据验证,响应与数据库中一致。如图7
近期评论