Lighthouse 5 – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Mon, 25 May 2026 13:22:47 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 在 Laravel 6、Lighthouse 5 中,验证输入值的组合是否存在的实现 https://www.shuijingwanwq.com/2023/03/20/7503/ https://www.shuijingwanwq.com/2023/03/20/7503/#respond Mon, 20 Mar 2023 01:41:33 +0000 https://www.shuijingwanwq.com/?p=7503 Post Views: 74 1、在 GraphQL Schema 文件中定义如下


extend type Mutation {
    "取消发布主题(role 从 DEVELOPMENT 切换到 UNPUBLISHED)"
    onlineStoreThemeUnpublish(
        "待取消发布的主题ID"
        id: ID!
    ): ThemeUnpublishPayload @validator(class: "Modules\\OnlineStoreThemeGraphQl\\Validators\\UnpublishThemeValidator") @field(resolver: "Modules\\OnlineStoreThemeGraphQl\\Resolver\\Theme\\UnpublishThemeResolver") @guard
}


2、在自定义的验证器类 Validator Classes Modules\\OnlineStoreThemeGraphQl\\Validators\\UnpublishThemeValidator 中,代码实现如下
<pre class="wp-block-syntaxhighlighter-code">

<?php

namespace Modules\OnlineStoreThemeGraphQl\Validators;

use Nuwave\Lighthouse\Validation\Validator;
use Illuminate\Validation\Rule;
use Modules\ThemeStoreDb\Models\ThemeInstallation;

class UnpublishThemeValidator extends Validator
{
    /**
     * Return the validation rules.
     *
     * @return array<string, array<mixed>>
     */
    public function rules(): array
    {
        return [
            'id' => [
                Rule::exists('theme_installation', 'theme_id')->where(function ($query) {
                    $query->where('role', ThemeInstallation::ROLE_DEVELOPMENT);
                }),
            ],
        ];
    }
}


</pre>
3、请求验证失败,符合预期。如图1
请求验证失败,符合预期

图1



{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeUnpublish].",
      "extensions": {
        "validation": {
          "id": [
            "The selected id is invalid."
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 5
        }
      ],
      "path": [
        "onlineStoreThemeUnpublish"
      ],
      "trace": [
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\ArgTraversalDirective.php",
          "line": 27,
          "call": "Nuwave\\Lighthouse\\Validation\\ValidateDirective::Nuwave\\Lighthouse\\Validation\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\ConvertEmptyStringsToNullDirective.php",
          "line": 48,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\ArgTraversalDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\TrimDirective.php",
          "line": 54,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\ConvertEmptyStringsToNullDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Factories\\FieldFactory.php",
          "line": 97,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\TrimDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 623,
          "call": "Nuwave\\Lighthouse\\Schema\\Factories\\FieldFactory::Nuwave\\Lighthouse\\Schema\\Factories\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 550,
          "call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL\\Type\\Definition\\FieldDefinition, instance of GraphQL\\Language\\AST\\FieldNode, instance of Closure, null, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 474,
          "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 857,
          "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'onlineStoreThemeUnpublish')"
        },
        {
          "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'onlineStoreThemeUnpublish')"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 859,
          "function": "array_reduce(array(1), instance of Closure, array(0))"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 490,
          "call": "GraphQL\\Executor\\ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 263,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
          "line": 215,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\Executor.php",
          "line": 156,
          "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
          "line": 162,
          "call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), 'UnpublishTheme', null)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
          "line": 94,
          "call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), 'UnpublishTheme', null, array(30))"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
          "line": 268,
          "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), 'UnpublishTheme', null, array(30))"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
          "line": 203,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeParsedQuery(instance of GraphQL\\Language\\AST\\DocumentNode, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, 'UnpublishTheme')"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
          "line": 162,
          "call": "Nuwave\\Lighthouse\\GraphQL::parseAndExecuteQuery('            mutation UnpublishTheme($id: ID!) {\n    onlineStoreThemeUnpublish(id: $id) {\n        theme {\n            id\n            name\n            role\n            createdAt\n        }\n    }\n}', instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, 'UnpublishTheme')"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
          "line": 121,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeOperation(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Utils.php",
          "line": 99,
          "call": "Nuwave\\Lighthouse\\GraphQL::Nuwave\\Lighthouse\\{closure}(instance of GraphQL\\Server\\OperationParams)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
          "line": 120,
          "call": "Nuwave\\Lighthouse\\Support\\Utils::mapEach(instance of Closure, instance of GraphQL\\Server\\OperationParams)"
        },
        {
          "file": "E:\\wwwroot\\object\\Modules\\OnlineStoreThemeGraphQl\\Controllers\\GraphQLController.php",
          "line": 39,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeOperationOrOperations(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php",
          "line": 48,
          "call": "Modules\\OnlineStoreThemeGraphQl\\Controllers\\GraphQLController::__invoke(instance of Illuminate\\Http\\Request, instance of Nuwave\\Lighthouse\\GraphQL, instance of Illuminate\\Events\\Dispatcher, instance of Laragraph\\Utils\\RequestParser, instance of Nuwave\\Lighthouse\\Execution\\SingleResponse, instance of Nuwave\\Lighthouse\\Execution\\ContextFactory)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
          "line": 219,
          "call": "Illuminate\\Routing\\ControllerDispatcher::dispatch(instance of Illuminate\\Routing\\Route, instance of Modules\\OnlineStoreThemeGraphQl\\Controllers\\GraphQLController, '__invoke')"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
          "line": 176,
          "call": "Illuminate\\Routing\\Route::runController()"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
          "line": 681,
          "call": "Illuminate\\Routing\\Route::run()"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 130,
          "call": "Illuminate\\Routing\\Router::Illuminate\\Routing\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\Modules\\OnlineStoreThemeGraphQl\\Middleware\\InjectStoreOptionsToConfigMiddleware.php",
          "line": 24,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Modules\\OnlineStoreThemeGraphQl\\Middleware\\InjectStoreOptionsToConfigMiddleware::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AttemptAuthentication.php",
          "line": 34,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AttemptAuthentication::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\ThrottleRequests.php",
          "line": 59,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Routing\\Middleware\\ThrottleRequests::handle(instance of Illuminate\\Http\\Request, instance of Closure, 300, '1')"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\SubstituteBindings.php",
          "line": 41,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Routing\\Middleware\\SubstituteBindings::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\ThrottleRequests.php",
          "line": 59,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Routing\\Middleware\\ThrottleRequests::handle(instance of Illuminate\\Http\\Request, instance of Closure, 60, '1')"
        },
        {
          "file": "E:\\wwwroot\\object\\app\\Http\\Middleware\\ApiHeader.php",
          "line": 23,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "App\\Http\\Middleware\\ApiHeader::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php",
          "line": 56,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Session\\Middleware\\StartSession::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\EncryptCookies.php",
          "line": 67,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\app\\Http\\Middleware\\EncryptCookies.php",
          "line": 30,
          "call": "Illuminate\\Cookie\\Middleware\\EncryptCookies::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "App\\Http\\Middleware\\EncryptCookies::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 105,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
          "line": 683,
          "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
          "line": 658,
          "call": "Illuminate\\Routing\\Router::runRouteWithinStack(instance of Illuminate\\Routing\\Route, instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
          "line": 624,
          "call": "Illuminate\\Routing\\Router::runRoute(instance of Illuminate\\Http\\Request, instance of Illuminate\\Routing\\Route)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
          "line": 613,
          "call": "Illuminate\\Routing\\Router::dispatchToRoute(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
          "line": 170,
          "call": "Illuminate\\Routing\\Router::dispatch(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 130,
          "call": "Illuminate\\Foundation\\Http\\Kernel::Illuminate\\Foundation\\Http\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\barryvdh\\laravel-debugbar\\src\\Middleware\\InjectDebugbar.php",
          "line": 67,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\app\\Http\\Middleware\\ChangeAppUrlMiddleware.php",
          "line": 23,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "App\\Http\\Middleware\\ChangeAppUrlMiddleware::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
          "line": 21,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
          "line": 21,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
          "line": 27,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode.php",
          "line": 63,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\fideloper\\proxy\\src\\TrustProxies.php",
          "line": 57,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Fideloper\\Proxy\\TrustProxies::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\dingo\\api\\src\\Http\\Middleware\\Request.php",
          "line": 111,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 171,
          "call": "Dingo\\Api\\Http\\Middleware\\Request::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
          "line": 105,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
          "line": 145,
          "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
        },
        {
          "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
          "line": 110,
          "call": "Illuminate\\Foundation\\Http\\Kernel::sendRequestThroughRouter(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "E:\\wwwroot\\object\\public\\index.php",
          "line": 57,
          "call": "Illuminate\\Foundation\\Http\\Kernel::handle(instance of Illuminate\\Http\\Request)"
        }
      ]
    }
  ],
  "data": {
    "onlineStoreThemeUnpublish": null
  }
}


4、再次确认验证规则生成的 SQL,生成如下。如图2
再次确认验证规则生成的 SQL,生成如下

图2



select
  count(*) as aggregate
from
  `table`
where
  `theme_id` = '986c9d3e-206c-43c7-8e77-f166731a5df5'
  and (`role` = 'development')


]]>
https://www.shuijingwanwq.com/2023/03/20/7503/feed/ 0
主题编辑器的页面保存后与实际页面不一致的排查分析(源于并发请求时,后端处理完成顺序与前端请求顺序未完全一致所导致) https://www.shuijingwanwq.com/2022/12/12/7263/ https://www.shuijingwanwq.com/2022/12/12/7263/#respond Mon, 12 Dec 2022 01:28:33 +0000 https://www.shuijingwanwq.com/?p=7263 Post Views: 81 1、现在主题编辑器的实现原理为:当编辑器中的某配置项发生变化时,会请求后端的暂存 API,将数据缓存至 Redis 中。当用户点击保存按钮后,会请求后端的保存 API,将会从 Redis 中读取数据存储至 MySQL 中。 2、当用户在快速编辑一些配置项时,可能会出现主题编辑器的页面数据与实际页面不一致的情况。如图1
当用户在快速编辑一些配置项时,可能会出现主题编辑器的页面数据与实际页面不一致的情况

图1

3、查看网络请求的发起顺序,最后一次请求后端的暂存 API,确定是在请求后端的保存 API 之前。那么可以推测得出的结论是,当在请求后端的保存 API ,后端从 Redis 中读取数据时,最后一次请求后端的暂存 API 还未将数据保存至 Redis 中,此时,读取的是上一次的暂存数据。举例说明如下,时间用毫秒计算: 最后一次请求后端的暂存 API 的发起时间点:2022/11/22 10:38:49.200 请求后端的保存 API 的发起时间点:2022/11/22 10:38:49.300 最后一次请求后端的暂存 API 将数据缓存至 Redis 的时间点:2022/11/22 10:38:49.800 请求后端的保存 API 从 Redis 中读取数据存储至 MySQL 的时间点:2022/11/22 10:38:49.600 4、在网络请求中查看最后一次请求后端的暂存 API的响应时间点:Tue, 22 Nov 2022 02:38:49 GMT。如图2
在网络请求中查看最后一次请求后端的暂存 API的响应时间点:Tue, 22 Nov 2022 02:38:49 GMT

图2

5、在网络请求中查看请求后端的保存 API的响应时间点:Tue, 22 Nov 2022 02:38:49 GMT。如图3
在网络请求中查看请求后端的保存 API的响应时间点:Tue, 22 Nov 2022 02:38:49 GMT

图3

6、在网络请求中查看最后一次请求后端的暂存 API的时长:686.69 毫秒。如图4
在网络请求中查看最后一次请求后端的暂存 API的时长:686.69 毫秒

图4

7、在网络请求中查看请求后端的保存 API的时长:380.19 毫秒。如图5
在网络请求中查看请求后端的保存 API的时长:380.19 毫秒

图5

8、现阶段有 2 个方案,第一个方案为前端处理,当用户点击保存按钮后,只有当所有的请求后端的暂存 API 结束后,再请求后端的保存 API。第二个方案为后端处理,只有当所有的请求后端的暂存 API 将数据缓存至 Redis 后,后端的保存 API 才从 Redis 中读取数据存储至 MySQL 中。 9、最终决定采用第二个方案,由后端处理。(1)当后端接收到暂存 API 的请求后,在当前会话中的 key :update_count 加 1,当将数据缓存至 Redis 后,update_count 减 1。当后端接收到保存 API 的请求后,判断当前会话中的 key :update_count,如果大于 0 ,则继续等待,在等待中会每间隔 500毫秒 再次判断当前会话中的 key :update_count,当判断达到 3 次后,继续往后执行。注:1秒= 1000毫秒; 1毫秒= 1000微秒。(2)从 Redis 中读取数据存储至 MySQL 中。 10、暂存 API 中的实现,基于 Redis 的命令:incr、decr 实现如下


    /**
     * 将 更新计数 中储存的数字值增一
     * @param string $sessionId
     * @return int
     */
    public static function incrUpdateCount(string $sessionId): int
    {
        $key = self::getUpdateCountKey($sessionId);
        $incr = Redis::connection('cache')->incr($key);
        Redis::connection('cache')->expire($key, ThemePreviewInterface::TTL);
        return $incr;
    }

    /**
     * 将 更新计数 中储存的数字值减一
     * @param string $sessionId
     * @return int
     */
    public static function decrUpdateCount(string $sessionId): int
    {
        $key = self::getUpdateCountKey($sessionId);
        $decr = Redis::connection('cache')->decr($key);
        Redis::connection('cache')->expire($key, ThemePreviewInterface::TTL);
        return $decr;
    }


11、保存 API 中的实现,基于 while 判断,总计判断 3 次
<pre class="wp-block-syntaxhighlighter-code">

    /**
     * 获取 更新计数 中储存的数字值
     * @param string $sessionId
     * @return mixed
     */
    public static function getUpdateCount(string $sessionId)
    {
        return Redis::connection('cache')->get(self::getUpdateCountKey($sessionId));
    }

	$i = 1;
	while (ThemePreview::getUpdateCount($sessionId) !== null && ThemePreview::getUpdateCount($sessionId) > 0 && $i <= 3) {
		file_put_contents(storage_path() . '/logs/Publish-' . microtime(true) . '-' . mt_rand()  . '.txt', print_r($i, true), FILE_APPEND | LOCK_EX);
		usleep(500000); // 延迟执行 0.5 秒
		$i++;
	}

</pre>
12、输出的文件如下。1669187163.3346 – 1669187162.8234 = 0.5112。1669187162.8234 – 1669187162.3034 = 0.52。创建时间相差大致为 0.5 秒,符合预期。如图8
输出的文件如下。1669187163.3346 - 1669187162.8234 = 0.5112。1669187162.8234 - 1669187162.3034 = 0.52。创建时间相差大致为 0.5 秒,符合预期

图8



Publish-1669187162.3034-776928128.txt
Publish-1669187162.8234-549007488.txt
Publish-1669187163.3346-885156477.txt


13、在本地环境再次复现,已经无法复现,不过确定生成了文件:Publish-1669188133.9627-720653875.txt,其值为 1。表明在 保存 API 中执行了一次等待。如图9
在本地环境再次复现,已经无法复现,不过确定生成了文件:Publish-1669188133.9627-720653875.txt,其值为 1。表明在 保存 API 中执行了一次等待

图9

14、在网络请求中查看最后一次请求后端的暂存 API 的时长:2.79 秒,原因在于本地环境性能不佳。如图10
在网络请求中查看最后一次请求后端的暂存 API 的时长:2.79 秒,原因在于本地环境性能不佳

图10

15、在网络请求中查看请求后端的保存 API 的时长:3.37 秒,其长于暂存 API的时长。如图11
在网络请求中查看请求后端的保存 API 的时长:3.37 秒,其长于暂存 API的时长

图11

16、不过此方案仍然存在一定的概率丢失数据了。如果暂存接口到达服务器的时间晚于保存接口完成的时间的话。最终决定参考 Shopify 的实现。 17、参考 Shopify 的主题编辑器。查看其请求后端的保存 API,请求与响应变量如下。如图12
参考 Shopify 的主题编辑器。查看其请求后端的保存 API,请求与响应变量如下

图12



{
  "eventMetadata": {
    "dirtyTransitionCountInSession": 13,
    "durationSeconds": 0,
    "lastViewport": "desktop",
    "pageType": "JSON"
  },
  "sessionId": "etPGCT4PQr7YyCr87svY9KJz",
  "resourceHashes": [
    {
      "hashValue": "fe8062f520ac1ab9be9a336c76d9f77517551223",
      "resourceId": "gid://shopify/OnlineStoreTheme/130440429753"
    },
    {
      "hashValue": "17183337545547076327",
      "resourceId": "gid://shopify/OnlineStoreThemeJsonTemplate/index?theme_id=130440429753"
    }
  ],
  "ignoreConflicts": null
}




{
    "data": {
        "onlineStoreThemeEditorSessionPublish": {
            "conflicts": null,
            "userErrors": [],
            "updatedHash": {
                "resourceId": "gid:\/\/shopify\/OnlineStoreTheme\/130440429753",
                "hashValue": "fe8062f520ac1ab9be9a336c76d9f77517551223",
                "__typename": "OnlineStoreResourceHash"
            },
            "__typename": "OnlineStoreThemeEditorSessionPublishPayload"
        }
    },
    "extensions": {
        "cost": {
            "requestedQueryCost": 10,
            "actualQueryCost": 10,
            "throttleStatus": {
                "maximumAvailable": 1200.0,
                "currentlyAvailable": 1190,
                "restoreRate": 60.0
            }
        }
    }
}


18、初步推测,应该与 hashValue 有关系,只有当缓存中的数据的 hashValue 与请求参数中的 hashValue 相等时,才执行保存操作。其保存 API 的时长接近 1.5 秒左右。如图13
初步推测,应该与 hashValue 有关系,只有当缓存中的数据的 hashValue 与请求参数中的 hashValue 相等时,才执行保存操作。其保存 API 的时长接近 1.5 秒左右

图13

19、最终设计如下方案,在暂存接口中,基于请求体内容计算出 hashValue,并保存至 Redis 的 key 中。然后在保存接口中,基于请求参数中的 hashValue 与保存至 Redis 的 hashValue 进行比对,如果相等,则保存。如果不相等,但是 ignoreConflicts 下存在对应的 hashValue,也可以保存。 如果不相等,且 ignoreConflicts 下不存在对应的 hashValue,则不保存,在返回的 conflicts 包含对应的 hashValue。 20、现有的请求与响应。如图14
现有的请求与响应

图14

21、暂存 API 中的实现



    /**
     * 获取更新时的哈希的缓存键
     * @param string $sessionId
     * @param string $resourceId
     * @return string
     */
    private static function getUpdateHashKey(string $sessionId, string $resourceId): string
    {
        return ThemePreviewInterface::THEME_EDITOR_SESSION_PREFIX . $sessionId . ':' . $resourceId . ':' . ThemePreviewInterface::UPDATE_HASH_KEY;
    }

    /**
     * 保存更新时的哈希
     * @param string $sessionId
     * @param string $resourceId
     * @param string $hashValue
     * @return bool
     */
    public static function saveUpdateHash(string $sessionId, string $resourceId, string $hashValue): bool
    {
        return self::getCacheTags($sessionId)->put(self::getUpdateHashKey($sessionId, $resourceId), $hashValue, ThemePreviewInterface::TTL);
    }

    /**
     * 获取更新时的哈希
     * @param string $sessionId
     * @param string $resourceId
     * @return mixed
     */
    public static function getUpdateHash(string $sessionId, string $resourceId)
    {
        return self::getCacheTags($sessionId)->get(self::getUpdateHashKey($sessionId, $resourceId));
    }

    /**
     * 判断哈希值是否与缓存中的相等
     * @param string $sessionId
     * @param string $resourceId
     * @param string $hashValue
     * @return bool
     */
    public static function isHashEq(string $sessionId, string $resourceId, string $hashValue): bool
    {
        return self::getUpdateHash($sessionId, $resourceId) == $hashValue;
    }

	$variables = json_encode($args);
	ThemePreview::saveUpdateHash($sessionId, $templateBasename, sha1($variables));


22、保存 API 中的实现



    public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $conflicts = null;
        $ignoreResourceIds = data_get($args, 'ignoreConflicts.*.resourceId');
        $hashValue = $themeId;
        foreach ($args['resourceHashes'] as $resourceHash) {
            if ($this->isConflict($sessionId, $resourceHash, $ignoreResourceIds)) {
                $conflicts[] = [
                    'resourceId' => $resourceHash['resourceId'],
                    'hashValue' => $resourceHash['hashValue']
                ];
                continue;
            }

            if ($resourceHash['resourceId'] == $themeId) {
                $hashValue = $resourceHash['hashValue'];
                $this->themeConfigLoader->publishSettingsData();
            } else {
                $this->rawSchemaLoaderAdapter->publishTemplateSettings($resourceHash['resourceId']);
            }
        }

        return [
            'conflicts' => $conflicts,
            'session' => ['sessionId' => $sessionId],
            'updatedHash' => [
                'resourceId' => $themeId,
                'hashValue' => $hashValue
            ],
            'publishedAt' => $publishedAt
        ];
    }

    /**
     * 判断哈希值是否冲突
     * @param string $sessionId
     * @param array $resourceHash
     * @param ?array $ignoreResourceIds
     * @return bool
     */
    private function isConflict(string $sessionId, array $resourceHash, ?array $ignoreResourceIds): bool
    {
        if (!ThemePreview::isHashEq($sessionId, $resourceHash['resourceId'], $resourceHash['hashValue'])) {
            if (!is_array($ignoreResourceIds) || !in_array($resourceHash['resourceId'], $ignoreResourceIds)) {
                return true;
            }
        }
        return false;
    }


23、第1次请求,当 hashValue 命中时,响应的冲突列表为 null。此时前端不再做处理。如图15
第1次请求,当 hashValue 命中时,响应的冲突列表为 null。此时前端不再做处理

图15

24、第1次请求,当 hashValue 未命中时,响应的冲突列表不为 null。如图16
第1次请求,当 hashValue 未命中时,响应的冲突列表不为 null

图16

25、当第1次请求,响应的冲突列表不为 null 时,此时前端需要第2次请求,请求参数不变化。如图17
当第1次请求,响应的冲突列表不为 null 时,此时前端需要第2次请求,请求参数不变化

图17

26、第2次请求,当 hashValue 命中时,响应的冲突列表为 null。此时前端不再做处理。 27、当第2次请求,响应的冲突列表不为 null 时,此时前端需要第3次请求,请求参数 ignoreConflicts 的值与 resourceHashes 保持一致。如图18 28、参考:https://www.shuijingwanwq.com/2022/12/08/7260/ 。在 Laravel 6、Lighthouse 5 中,方法 __invoke 的参数 $args,其内部顺序与前端请求参数不一致。暂存接口最终实现如下


ThemePreview::saveUpdateHash($sessionId, $templateBasename, sha1($context->request()->getContent()));


]]>
https://www.shuijingwanwq.com/2022/12/12/7263/feed/ 0
在 Laravel 6、Lighthouse 5 中,方法 __invoke 的参数 $args,其内部顺序与前端请求参数不一致 https://www.shuijingwanwq.com/2022/12/08/7260/ https://www.shuijingwanwq.com/2022/12/08/7260/#comments Thu, 08 Dec 2022 01:22:06 +0000 https://www.shuijingwanwq.com/?p=7260 Post Views: 93 1、前端请求参数如下所示。如图1
前端请求参数如下所示

图1



{
  "query": "\nmutation ThemeUpdate($themeId: ID!, $settings: [ThemeSettingDataInput!], $sections: [ThemeSettingsDataSectionInput!], $appEmbeds: [ThemeSettingsDataBlockInput!], $sessionId: String!) {\n  onlineStoreThemeSettingsDataUpdate(\n    themeId: $themeId\n    settings: $settings\n    sections: $sections\n    appEmbeds: $appEmbeds\n    sessionId: $sessionId\n  ) {\n    sessionId\n    __typename\n  }\n}\n",
  "variables": {
    "themeId": "97cdd852-2ca0-48eb-b119-6afb68e2254c",
    "settings": [],
    "sections": [
      {
        "sectionId": "announcement-bar",
        "disabled": false,
        "type": "announcement-bar",
        "settings": [
          {
            "settingId": "enable",
            "value": "true"
          },
          {
            "settingId": "homepage_only",
            "value": "false"
          },
          {
            "settingId": "sticky",
            "value": "false"
          }
        ],
        "blocks": [
          {
            "blockId": "announcement-bar-0",
            "disabled": true,
            "type": "announcement",
            "settings": [
              {
                "settingId": "text",
                "value": "❤Free Shipping Over $100.0❤"
              },
              {
                "settingId": "image",
                "value": ""
              },
              {
                "settingId": "mobile_image",
                "value": ""
              },
              {
                "settingId": "background_color",
                "value": "#000000"
              },
              {
                "settingId": "text_color",
                "value": "#ffffff"
              },
              {
                "settingId": "link",
                "value": ""
              }
            ]
          }
        ]
      },
      {
        "sectionId": "header",
        "disabled": false,
        "type": "header",
        "settings": [
          {
            "settingId": "sticky",
            "value": "false"
          },
          {
            "settingId": "menu",
            "value": ""
          },
          {
            "settingId": "secondary_menu",
            "value": ""
          },
          {
            "settingId": "text_color",
            "value": "#000000"
          },
          {
            "settingId": "background_color",
            "value": "#ffffff"
          },
          {
            "settingId": "transparent",
            "value": "true"
          },
          {
            "settingId": "transparent_text_color",
            "value": "#ffffff"
          },
          {
            "settingId": "logo",
            "value": ""
          },
          {
            "settingId": "logo_max_width",
            "value": "180"
          }
        ],
        "blocks": []
      },
      {
        "sectionId": "footer",
        "disabled": false,
        "type": "footer",
        "settings": [
          {
            "settingId": "style",
            "value": "first"
          },
          {
            "settingId": "1",
            "value": ""
          },
          {
            "settingId": "text_color",
            "value": "#222222"
          },
          {
            "settingId": "background_color",
            "value": "#f7f7f9"
          },
          {
            "settingId": "4",
            "value": ""
          },
          {
            "settingId": "show_payment_icons",
            "value": "false"
          },
          {
            "settingId": "show_dmca",
            "value": "true"
          }
        ],
        "blocks": []
      }
    ],
    "appEmbeds": [],
    "sessionId": "gY32VAiyjotH8tsfJQZ5aopd9okgAZE4dExf"
  },
  "operationName": "ThemeUpdate"
}


2、后端将方法 __invoke 的参数 $args 转换为 json,其对应前端请求体中的字段:variables。代码实现如下


    public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $variables = json_encode($args);
        echo $variables;
        exit;
	}


3、打印 json 如下。多出了字段:directive,然后 json 内部的 key 的顺序与前端不一致。appEmbeds 的位置本应该在第 4 位,现在在第 2 位


{
  "themeId": "97cdd852-2ca0-48eb-b119-6afb68e2254c",
  "appEmbeds": [],
  "settings": [],
  "sections": [
    {
      "sectionId": "announcement-bar",
      "type": "announcement-bar",
      "disabled": false,
      "settings": [
        {
          "settingId": "enable",
          "value": "true"
        },
        {
          "settingId": "homepage_only",
          "value": "false"
        },
        {
          "settingId": "sticky",
          "value": "false"
        }
      ],
      "blocks": [
        {
          "blockId": "announcement-bar-0",
          "type": "announcement",
          "disabled": true,
          "settings": [
            {
              "settingId": "text",
              "value": "❤Free Shipping Over $100.0❤"
            },
            {
              "settingId": "image",
              "value": ""
            },
            {
              "settingId": "mobile_image",
              "value": ""
            },
            {
              "settingId": "background_color",
              "value": "#000000"
            },
            {
              "settingId": "text_color",
              "value": "#ffffff"
            },
            {
              "settingId": "link",
              "value": ""
            }
          ]
        }
      ]
    },
    {
      "sectionId": "header",
      "type": "header",
      "disabled": false,
      "settings": [
        {
          "settingId": "sticky",
          "value": "false"
        },
        {
          "settingId": "menu",
          "value": ""
        },
        {
          "settingId": "secondary_menu",
          "value": ""
        },
        {
          "settingId": "text_color",
          "value": "#000000"
        },
        {
          "settingId": "background_color",
          "value": "#ffffff"
        },
        {
          "settingId": "transparent",
          "value": "true"
        },
        {
          "settingId": "transparent_text_color",
          "value": "#ffffff"
        },
        {
          "settingId": "logo",
          "value": ""
        },
        {
          "settingId": "logo_max_width",
          "value": "180"
        }
      ],
      "blocks": []
    },
    {
      "sectionId": "footer",
      "type": "footer",
      "disabled": false,
      "settings": [
        {
          "settingId": "style",
          "value": "first"
        },
        {
          "settingId": "1",
          "value": ""
        },
        {
          "settingId": "text_color",
          "value": "#222222"
        },
        {
          "settingId": "background_color",
          "value": "#f7f7f9"
        },
        {
          "settingId": "4",
          "value": ""
        },
        {
          "settingId": "show_payment_icons",
          "value": "false"
        },
        {
          "settingId": "show_dmca",
          "value": "true"
        }
      ],
      "blocks": []
    }
  ],
  "sessionId": "gY32VAiyjotH8tsfJQZ5aopd9okgAZE4dExf",
  "directive": null
}


4、基于 Illuminate\Http\Request 获取请求参数。其顺序仍然不一致。appEmbeds 的位置本应该在第 4 位,现在在第 2 位。


print_r($context->request()->input('variables'));
exit;




{
  "themeId": "97cdd852-2ca0-48eb-b119-6afb68e2254c",
  "settings": [],
  "sections": [
    {
      "sectionId": "announcement-bar",
      "disabled": false,
      "type": "announcement-bar",
      "settings": [
        {
          "settingId": "enable",
          "value": "true"
        },
        {
          "settingId": "homepage_only",
          "value": "false"
        },
        {
          "settingId": "sticky",
          "value": "false"
        }
      ],
      "blocks": [
        {
          "blockId": "announcement-bar-0",
          "disabled": false,
          "type": "announcement",
          "settings": [
            {
              "settingId": "text",
              "value": "❤Free Shipping Over $100.0❤"
            },
            {
              "settingId": "image",
              "value": null
            },
            {
              "settingId": "mobile_image",
              "value": null
            },
            {
              "settingId": "background_color",
              "value": "#000000"
            },
            {
              "settingId": "text_color",
              "value": "#ffffff"
            },
            {
              "settingId": "link",
              "value": null
            }
          ]
        }
      ]
    },
    {
      "sectionId": "header",
      "disabled": false,
      "type": "header",
      "settings": [
        {
          "settingId": "sticky",
          "value": "false"
        },
        {
          "settingId": "menu",
          "value": null
        },
        {
          "settingId": "secondary_menu",
          "value": null
        },
        {
          "settingId": "text_color",
          "value": "#000000"
        },
        {
          "settingId": "background_color",
          "value": "#ffffff"
        },
        {
          "settingId": "transparent",
          "value": "true"
        },
        {
          "settingId": "transparent_text_color",
          "value": "#ffffff"
        },
        {
          "settingId": "logo",
          "value": null
        },
        {
          "settingId": "logo_max_width",
          "value": "180"
        }
      ],
      "blocks": []
    },
    {
      "sectionId": "footer",
      "disabled": false,
      "type": "footer",
      "settings": [
        {
          "settingId": "style",
          "value": "first"
        },
        {
          "settingId": "1",
          "value": null
        },
        {
          "settingId": "text_color",
          "value": "#222222"
        },
        {
          "settingId": "background_color",
          "value": "#f7f7f9"
        },
        {
          "settingId": "4",
          "value": null
        },
        {
          "settingId": "show_payment_icons",
          "value": "false"
        },
        {
          "settingId": "show_dmca",
          "value": "true"
        }
      ],
      "blocks": []
    }
  ],
  "appEmbeds": [],
  "sessionId": "gY32VAiyjotH8tsfJQZ5aopd9okgAZE4dExf"
}


5、基于 Illuminate\Http\Request 获取请求体的原始内容。前后端顺序一致。appEmbeds 的位置现在第 4 位。代码实现如下


$content = json_decode($context->request()->getContent(), true);
echo json_encode($content['variables']);
exit;




{
  "themeId": "97cdd852-2ca0-48eb-b119-6afb68e2254c",
  "settings": [],
  "sections": [
    {
      "sectionId": "announcement-bar",
      "disabled": false,
      "type": "announcement-bar",
      "settings": [
        {
          "settingId": "enable",
          "value": "true"
        },
        {
          "settingId": "homepage_only",
          "value": "false"
        },
        {
          "settingId": "sticky",
          "value": "false"
        }
      ],
      "blocks": [
        {
          "blockId": "announcement-bar-0",
          "disabled": false,
          "type": "announcement",
          "settings": [
            {
              "settingId": "text",
              "value": "❤Free Shipping Over $100.0❤"
            },
            {
              "settingId": "image",
              "value": ""
            },
            {
              "settingId": "mobile_image",
              "value": ""
            },
            {
              "settingId": "background_color",
              "value": "#000000"
            },
            {
              "settingId": "text_color",
              "value": "#ffffff"
            },
            {
              "settingId": "link",
              "value": ""
            }
          ]
        }
      ]
    },
    {
      "sectionId": "header",
      "disabled": false,
      "type": "header",
      "settings": [
        {
          "settingId": "sticky",
          "value": "false"
        },
        {
          "settingId": "menu",
          "value": ""
        },
        {
          "settingId": "secondary_menu",
          "value": ""
        },
        {
          "settingId": "text_color",
          "value": "#000000"
        },
        {
          "settingId": "background_color",
          "value": "#ffffff"
        },
        {
          "settingId": "transparent",
          "value": "true"
        },
        {
          "settingId": "transparent_text_color",
          "value": "#ffffff"
        },
        {
          "settingId": "logo",
          "value": ""
        },
        {
          "settingId": "logo_max_width",
          "value": "180"
        }
      ],
      "blocks": []
    },
    {
      "sectionId": "footer",
      "disabled": false,
      "type": "footer",
      "settings": [
        {
          "settingId": "style",
          "value": "first"
        },
        {
          "settingId": "1",
          "value": ""
        },
        {
          "settingId": "text_color",
          "value": "#222222"
        },
        {
          "settingId": "background_color",
          "value": "#f7f7f9"
        },
        {
          "settingId": "4",
          "value": ""
        },
        {
          "settingId": "show_payment_icons",
          "value": "false"
        },
        {
          "settingId": "show_dmca",
          "value": "true"
        }
      ],
      "blocks": []
    }
  ],
  "appEmbeds": [],
  "sessionId": "gY32VAiyjotH8tsfJQZ5aopd9okgAZE4dExf"
}


]]>
https://www.shuijingwanwq.com/2022/12/08/7260/feed/ 1
在 Lighthouse 中,resolver 所对应的类方法 __invoke 未执行的排查分析 https://www.shuijingwanwq.com/2022/08/19/6886/ https://www.shuijingwanwq.com/2022/08/19/6886/#respond Fri, 19 Aug 2022 02:25:22 +0000 https://www.shuijingwanwq.com/?p=6886 Post Views: 152

1、GraphQL 架构定义如下


"获取主题页面的配置"
templateDetails(basename: String!): OnlineStoreThemeTemplateDetails @field(resolver: "Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver")

type OnlineStoreThemeTemplateDetails {
    sectionsData: [OnlineStoreThemeSection]
    sectionSchemas: [OnlineStoreThemeSectionSchema]
}


2、在类 TemplateDetailsResolver 中方法 __invoke 中打印输出 1,未输出。如图1

在类 TemplateDetailsResolver 中方法 __invoke 中打印输出 1,未输出

图1


public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
{
	echo 1;
	exit;
}



        "templateDetails": {
          "sectionsData": null,
          "sectionSchemas": null
        },


3、GraphQL 架构定义还原如下,则对应的类方法 __invoke 能够执行到。如图2

在类 TemplateDetailsResolver 中方法 __invoke 中打印输出 1,未输出

图2


"获取主题页面的配置"
templateDetails(basename: String!): [OnlineStoreThemeSection] @field(resolver: "Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver")

type OnlineStoreThemeTemplateDetails {
    sectionsData: [OnlineStoreThemeSection]
    sectionSchemas: [OnlineStoreThemeSectionSchema]
}


4、决定拆分 GraphQL 架构定义,则对应的类方法 __invoke 能够执行到。


"获取主题页面的配置"
templateDetails(basename: String!): OnlineStoreThemeTemplateDetails

type OnlineStoreThemeTemplateDetails {
    sectionsData: [OnlineStoreThemeSection] @field(resolver: "Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver")
    sectionSchemas: [OnlineStoreThemeSectionSchema]
}


5、GraphQL 架构定义调整解析类,则对应的类方法 __invoke 能够执行到。确认问题根源在于解析类本身。


"获取主题页面的配置"
templateDetails(basename: String!): OnlineStoreThemeTemplateDetails @field(resolver: "Modules\\OnlineStoreThemeGraphQL\\Resolver\\SectionPresetsByBaseNameResolver")

type OnlineStoreThemeTemplateDetails {
    sectionsData: [OnlineStoreThemeSection]
    sectionSchemas: [OnlineStoreThemeSectionSchema]
}


6、还原至步骤 1 ,决定沿用此架构,在类的构造方法中调试程序,查看调用栈。也确认了 __construct 可以执行,但是 __invoke 未执行到。比较类(Modules\\OnlineStoreThemeGraphQL\\Resolver\\SectionPresetsByBaseNameResolver) 与 类(Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver)

<?php
 
namespace Modules\OnlineStoreThemeGraphQL\Resolver;
 
use Modules\OnlineStoreThemeGraphQL\Resolver\Id;
use Modules\ThemeSetting\Schema\RawSchemaLoaderInterface;
use Modules\ThemeSetting\Schema\Builder\SectionBuilder;
use Modules\ThemeSetting\Schema\Dto\Section;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use GraphQL\Type\Definition\ResolveInfo;
use Modules\OnlineStoreThemeGraphQL\Resolver\OnlineStoreTheme\ResolveSectionSchema;
use Modules\ThemeSetting\Schema\Dto\Section\Block;
use Modules\ThemeSetting\Schema\Dto\Section\Setting\BlockSetting;
use Modules\ThemeSetting\Schema\Dto\Section\Setting\SectionDefault;
use Modules\ThemeSetting\Schema\Dto\Section\Setting\SectionSetting;
use Wwwision\RelayPagination\Loader\ArrayLoader;
use Wwwision\RelayPagination\Loader\Loader;
 
class SectionPresetsByBaseNameResolver
{
    use ResolveSectionSchema;
 
    private SectionBuilder $sectionBuilder;
 
    private RawSchemaLoaderInterface $rawSectionSchemaLoader;
 
    public function __construct(RawSchemaLoaderInterface $rawSectionSchemaLoader, SectionBuilder $sectionBuilder)
    {
        $this->sectionBuilder = $sectionBuilder;
        $this->rawSectionSchemaLoader = $rawSectionSchemaLoader;
    }
 
    public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo):Loader
    {
        $basename = $args['basename'];
 
        $query = $args['query']??null;
 
        $themeId = $rootValue['sectionPresetsByBasename']['theme_id'];
 
        $rawSchemas = $this->rawSectionSchemaLoader->loadAllSectionSchemas();
 
        $excludedSections = array_merge([Block::APP_BLOCK_SECTION], config('theme_setting.schema.allowed_sections'));
 
        $sectionSettings = collect($rawSchemas)->filter(function($rawSchema, $type) use($excludedSections){
                // Skip app wrapper 组件
            return !in_array($type, $excludedSections);
        })->map(function($rawSchema, $type) use($themeId){
            $id = Id::createSectionSchemaId($themeId, $type);
            $section = new Section();
            $this->sectionBuilder->build($rawSchema, $section);
 
           return [
                'categoryName' => 'None',
                'id' => $id,
                'name' => $section->getName(),
                'section' => $this->resolveSectionSetting($id, $id, $themeId, $this->preparePreset($section, $type), $section)
            ];
        });
 
        $loader = new ArrayLoader($sectionSettings->toArray());
 
        return $loader;
    }
 
    private function preparePreset(Section $section, string $type): SectionSetting
    {
        $defaultSectionSetting =  $section->hasPresets() ? head(array_values($section->getPresets())): $section->getDefault();
 
        $presetSettings = [];
        foreach($section->getIdentifiedSettings() as $id => $setting) {
            $presetSettings[$id] = $setting->getDefault();
        }
        $presetSettings = array_merge($presetSettings, $defaultSectionSetting? $defaultSectionSetting->getSettings(): []);
 
        $sectionSetting = new SectionSetting();
        $sectionSetting->setSettings($presetSettings );
        $sectionSetting->setBlocks($this->prepareBlockSettings($section, $defaultSectionSetting));
        $sectionSetting->setType($type);
 
        return $sectionSetting;
    }
 
    private function prepareBlockSettings(Section $section, SectionDefault $defaultSectionSetting = null)
    {
        $sectionDefaultBlockSettings = [];
        foreach($section->getBlocks() as $block) {
            $settings = [];
            foreach($block->getSettings() as $setting) {
                $settings[$setting->getId()] = $setting->getDefault();
            }
 
            $blockSetting = new BlockSetting();
            $blockSetting->setType($block->getType());
            $blockSetting->setSettings($settings);
 
            $sectionDefaultBlockSettings[$block->getType()] = $blockSetting;
        }
 
        // Merge block settings
        if ($defaultSectionSetting) {
            $blockSettings =  $defaultSectionSetting->getBlocks();
            foreach($blockSettings as $blockSetting) {
                /**
                 * @var \Modules\ThemeSetting\Schema\Dto\Section\Setting\BlockSetting
                 */
                $defaultBlockSetting = $sectionDefaultBlockSettings[$blockSetting->getType()];
                $blockSetting->setSettings(array_merge($defaultBlockSetting->getSettings(), $blockSetting->getSettings()));
            }
 
            return $blockSettings;
        }
 
 
        return $sectionDefaultBlockSettings;
    }
}
<?php
 
namespace Modules\OnlineStoreThemeGraphQL\Resolver\OnlineStoreTheme;
 
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use GraphQL\Type\Definition\ResolveInfo;
use Modules\OnlineStoreThemeGraphQL\GraphQl\Exception\GraphQlInputException;
use Modules\OnlineStoreThemeGraphQL\Resolver\Id;
use Modules\OnlineStoreThemeGraphQL\ThemeSetting\RawSchemaLoaderAdapter;
use Modules\Theme\ThemeContext;
use Modules\ThemeSetting\Schema\Dto\Template;
use Modules\ThemeSetting\Schema\Builder\TemplateBuilder;
 
class TemplateDetailsResolver
{
    use ResolveSectionSchema;
 
    private ThemeContext $themeContext;
 
    private TemplateBuilder $templateBuilder;
 
    public function __construct(ThemeContext $themeContext, TemplateBuilder $templateBuilder)
    {
        $this->themeContext = $themeContext;
        $this->templateBuilder = $templateBuilder;
    }
 
    public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $themeId = $rootValue['templateSettingsData']['theme_id'];
        $basename = $args['basename'];
        $rawSchemaLoader = $this->themeContext
            ->getThemeViewFactory()
            ->createRawSchemaLoader();
 
        $rawSchemaLoaderAdapter = new RawSchemaLoaderAdapter($rawSchemaLoader);
        $sessionId = $rootValue['session_id']?? null;
        $rawSchemaLoaderAdapter->setSessionId($sessionId);
 
        try {
            $rawSchema = $rawSchemaLoaderAdapter->loadTemplateSchema($basename);
 
            $template = new Template();
 
            $this->templateBuilder->build($rawSchema, $template);
        } catch(\Exception $e) {
            throw new GraphQlInputException($e->getMessage());
        }
 
        $id = new Id([$themeId, $basename]);
 
        return [];
 
        foreach($template as $sectionId => $section) {
            list($schema, $setting) =  $section;
 
            $globalSectionId = $id->with($sectionId);
 
            yield  $this->resolveSectionSetting($globalSectionId, $sectionId, $themeId, $setting, $schema);
        }
    }
}
 

{
  "message": "Call to undefined function Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\checkhere()",
  "exception": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
  "file": "E:\\wwwroot\\object\\Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver.php",
  "line": 24,
  "trace": [
    {
      "function": "__construct",
      "class": "Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Container.php",
      "line": 843,
      "function": "newInstanceArgs",
      "class": "ReflectionClass",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Container.php",
      "line": 681,
      "function": "build",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Application.php",
      "line": 785,
      "function": "resolve",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Container.php",
      "line": 629,
      "function": "resolve",
      "class": "Illuminate\\Foundation\\Application",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Application.php",
      "line": 770,
      "function": "make",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\helpers.php",
      "line": 120,
      "function": "make",
      "class": "Illuminate\\Foundation\\Application",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Utils.php",
      "line": 58,
      "function": "app"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\FieldDirective.php",
      "line": 45,
      "function": "constructResolver",
      "class": "Nuwave\\Lighthouse\\Support\\Utils",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Factories\\FieldFactory.php",
      "line": 69,
      "function": "resolveField",
      "class": "Nuwave\\Lighthouse\\Schema\\Directives\\FieldDirective",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\TypeRegistry.php",
      "line": 414,
      "function": "handle",
      "class": "Nuwave\\Lighthouse\\Schema\\Factories\\FieldFactory",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\UnresolvedFieldDefinition.php",
      "line": 39,
      "function": "Nuwave\\Lighthouse\\Schema\\{closure}",
      "class": "Nuwave\\Lighthouse\\Schema\\TypeRegistry",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php",
      "line": 45,
      "function": "resolve",
      "class": "GraphQL\\Type\\Definition\\UnresolvedFieldDefinition",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Type\\Definition\\TypeWithFields.php",
      "line": 33,
      "function": "findField",
      "class": "GraphQL\\Type\\Definition\\TypeWithFields",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Validator\\Rules\\OverlappingFieldsCanBeMerged.php",
      "line": 256,
      "function": "getField",
      "class": "GraphQL\\Type\\Definition\\TypeWithFields",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Validator\\Rules\\OverlappingFieldsCanBeMerged.php",
      "line": 165,
      "function": "internalCollectFieldsAndFragmentNames",
      "class": "GraphQL\\Validator\\Rules\\OverlappingFieldsCanBeMerged",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Validator\\Rules\\OverlappingFieldsCanBeMerged.php",
      "line": 96,
      "function": "getFieldsAndFragmentNames",
      "class": "GraphQL\\Validator\\Rules\\OverlappingFieldsCanBeMerged",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Validator\\Rules\\OverlappingFieldsCanBeMerged.php",
      "line": 64,
      "function": "findConflictsWithinSelectionSet",
      "class": "GraphQL\\Validator\\Rules\\OverlappingFieldsCanBeMerged",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Language\\Visitor.php",
      "line": 414,
      "function": "GraphQL\\Validator\\Rules\\{closure}",
      "class": "GraphQL\\Validator\\Rules\\OverlappingFieldsCanBeMerged",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Language\\Visitor.php",
      "line": 470,
      "function": "GraphQL\\Language\\{closure}",
      "class": "GraphQL\\Language\\Visitor",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Language\\Visitor.php",
      "line": 277,
      "function": "GraphQL\\Language\\{closure}",
      "class": "GraphQL\\Language\\Visitor",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Validator\\DocumentValidator.php",
      "line": 224,
      "function": "visit",
      "class": "GraphQL\\Language\\Visitor",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Validator\\DocumentValidator.php",
      "line": 116,
      "function": "visitUsingRules",
      "class": "GraphQL\\Validator\\DocumentValidator",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
      "line": 153,
      "function": "validate",
      "class": "GraphQL\\Validator\\DocumentValidator",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
      "line": 94,
      "function": "promiseToExecute",
      "class": "GraphQL\\GraphQL",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
      "line": 268,
      "function": "executeQuery",
      "class": "GraphQL\\GraphQL",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
      "line": 203,
      "function": "executeParsedQuery",
      "class": "Nuwave\\Lighthouse\\GraphQL",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
      "line": 162,
      "function": "parseAndExecuteQuery",
      "class": "Nuwave\\Lighthouse\\GraphQL",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
      "line": 121,
      "function": "executeOperation",
      "class": "Nuwave\\Lighthouse\\GraphQL",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Utils.php",
      "line": 99,
      "function": "Nuwave\\Lighthouse\\{closure}",
      "class": "Nuwave\\Lighthouse\\GraphQL",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
      "line": 120,
      "function": "mapEach",
      "class": "Nuwave\\Lighthouse\\Support\\Utils",
      "type": "::"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Controllers\\GraphQLController.php",
      "line": 32,
      "function": "executeOperationOrOperations",
      "class": "Nuwave\\Lighthouse\\GraphQL",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php",
      "line": 48,
      "function": "__invoke",
      "class": "Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
      "line": 219,
      "function": "dispatch",
      "class": "Illuminate\\Routing\\ControllerDispatcher",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
      "line": 176,
      "function": "runController",
      "class": "Illuminate\\Routing\\Route",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
      "line": 681,
      "function": "run",
      "class": "Illuminate\\Routing\\Route",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 130,
      "function": "Illuminate\\Routing\\{closure}",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AttemptAuthentication.php",
      "line": 34,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AttemptAuthentication",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AcceptJson.php",
      "line": 27,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AcceptJson",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 105,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
      "line": 683,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
      "line": 658,
      "function": "runRouteWithinStack",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
      "line": 624,
      "function": "runRoute",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
      "line": 613,
      "function": "dispatchToRoute",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
      "line": 170,
      "function": "dispatch",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 130,
      "function": "Illuminate\\Foundation\\Http\\{closure}",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\barryvdh\\laravel-debugbar\\src\\Middleware\\InjectDebugbar.php",
      "line": 67,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\app\\Http\\Middleware\\ChangeAppUrlMiddleware.php",
      "line": 23,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "App\\Http\\Middleware\\ChangeAppUrlMiddleware",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
      "line": 21,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
      "line": 21,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
      "line": 27,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode.php",
      "line": 63,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\fideloper\\proxy\\src\\TrustProxies.php",
      "line": 57,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Fideloper\\Proxy\\TrustProxies",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\dingo\\api\\src\\Http\\Middleware\\Request.php",
      "line": 111,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 171,
      "function": "handle",
      "class": "Dingo\\Api\\Http\\Middleware\\Request",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
      "line": 105,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
      "line": 145,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
      "line": 110,
      "function": "sendRequestThroughRouter",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "E:\\wwwroot\\object\\public\\index.php",
      "line": 57,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    }
  ]
}


7、调整类 Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver 中的方法 __invoke。删除掉 yield 的循环。方法 __invoke 能够被执行到。如图3

调整类 Modules\\OnlineStoreThemeGraphQL\\Resolver\\OnlineStoreTheme\\TemplateDetailsResolver 中的方法 __invoke。删除掉 yield 的循环。方法 __invoke 能够被执行到

图3


    public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        echo 44;
        exit;
        $themeId = $rootValue['templateSettingsData']['theme_id'];
        $basename = $args['basename'];
        $rawSchemaLoader = $this->themeContext
            ->getThemeViewFactory()
            ->createRawSchemaLoader();

        $rawSchemaLoaderAdapter = new RawSchemaLoaderAdapter($rawSchemaLoader);
        $sessionId = $rootValue['session_id']?? null;
        $rawSchemaLoaderAdapter->setSessionId($sessionId);

        try {
            $rawSchema = $rawSchemaLoaderAdapter->loadTemplateSchema($basename);

            $template = new Template();

            $this->templateBuilder->build($rawSchema, $template);
        } catch(\Exception $e) {
            throw new GraphQlInputException($e->getMessage());
        }

        $id = new Id([$themeId, $basename]);

    }


]]>
https://www.shuijingwanwq.com/2022/08/19/6886/feed/ 0
在 Laravel 6、Lighthouse 5、nwidart/laravel-modules 7 中,报错:Illuminate\Contracts\Container\BindingResolutionException : Target [Interface] is not instantiable while building [Resolver]. 要延迟加载提供者,未实现 provides 方法所导致 https://www.shuijingwanwq.com/2022/06/29/6707/ https://www.shuijingwanwq.com/2022/06/29/6707/#respond Wed, 29 Jun 2022 01:20:19 +0000 https://www.shuijingwanwq.com/?p=6707 Post Views: 108

1、在 Lighthouse 5 中,报错:Illuminate\Contracts\Container\BindingResolutionException : Target [Interface] is not instantiable while building [Resolver].。如图1

在 Lighthouse 5 中,报错:Illuminate\Contracts\Container\BindingResolutionException  : Target [Interface] is not instantiable while building [Resolver].

图1


PS E:\wwwroot\object> php artisan lighthouse:ide-helper
Wrote schema directive definitions to E:\wwwroot\object/schema-directives.graphql.

   Illuminate\Contracts\Container\BindingResolutionException  : Target [Modules\ThemeStoreGraphQl\ThemeSetting\Schema\TemplateSchemaLoaderInterface] is not instantiable while building [Modules\ThemeStoreGraphQl\Resolver\OnlineStoreTheme\TemplateSettingsDataResolver].

  at E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php:978
    974|         } else {
    975|             $message = "Target [$concrete] is not instantiable.";
    976|         }
    977|
  > 978|         throw new BindingResolutionException($message);
    979|     }
    980|
    981|     /**
    982|      * Throw an exception for an unresolvable primitive.

  Exception trace:

  1   Illuminate\Container\Container::notInstantiable("Modules\ThemeStoreGraphQl\ThemeSetting\Schema\TemplateSchemaLoaderInterface")
      E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php:812

  2   Illuminate\Container\Container::build("Modules\ThemeStoreGraphQl\ThemeSetting\Schema\TemplateSchemaLoaderInterface")
      E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php:681

  Please use the argument -v to see more details.


2、编辑 ThemeStoreGraphQl/Resources/graphql/theme_setting.graphql ,注释掉:templateSettingsData(basename: String!): [ThemeSection] @field(resolver: “Modules\\ThemeStoreGraphQl\\Resolver\\OnlineStoreTheme\\TemplateSettingsDataResolver”)。则不再报错。


    # "获取主题页面的配置"
    # templateSettingsData(basename: String!): [ThemeSection] @field(resolver: "Modules\\ThemeStoreGraphQl\\Resolver\\OnlineStoreTheme\\TemplateSettingsDataResolver")


3、取消注释后,查看 ThemeStoreGraphQl/Resolver/OnlineStoreTheme/TemplateSettingsDataResolver.php 。TemplateSchemaLoaderInterface 被做为构造函数的参数传入。


    private TemplateSchemaLoaderInterface $templateSchemaLoader;

    public function __construct(TemplateSchemaLoaderInterface $templateSchemaLoader)
    {
        $this->templateSchemaLoader = $templateSchemaLoader;
    }


4、查看此模块的服务提供者类,确定是有注册相应的实例的。但是并未执行 register() 。因为未输出 graphql。


    public function register()
    {
        echo 'graphql';
        exit;

        $this->app->singleton(TemplateSchemaLoaderInterface::class, function($app) {
            if ($this->app['config']['theme_store.view_storage'] == 'database') {
                return new DbTemplateSchemaLoader($app->make(ViewStorageInterface::class), $app->make(TemplateBuilder::class));
            } else {
                return new FileTemplateSchemaLoader($app->make('igaster.themes'), $app->make(TemplateBuilder::class));
            }
        });

        $this->app->singleton(TemplateSettingsDataResolver::class, function($app){
            return new TemplateSettingsDataResolver($app->make(TemplateSchemaLoaderInterface::class));
        });

    }


5、查看模块列表,其 Name 为 ThemeStoreGraphQL,Path 为 E:\wwwroot\object\Modules/ThemeStoreGraphQl。如图2

查看模块列表,其 Name 为 ThemeStoreGraphQL,Path 为 E:\wwwroot\object\Modules/ThemeStoreGraphQl

图2

6、查看 /Modules/ThemeStoreGraphQl/composer.json,其路径 ThemeStoreGraphQL 与实际目录名 ThemeStoreGraphQl 不匹配。调整为与实际目录名一致。如图3

查看 /Modules/ThemeStoreGraphQl/composer.json,其路径 ThemeStoreGraphQL 与实际目录名 ThemeStoreGraphQl 不匹配。调整为与实际目录名一致

图3


{

    "extra": {
        "laravel": {
            "providers": [
                "Modules\\ThemeStoreGraphQL\\Providers\\GraphQlResolverServiceProvider"
            ],
            "aliases": {

            }
        }
    },
    "autoload": {
        "psr-4": {
            "Modules\\ThemeStoreGraphQL\\": ""
        }
    }

}



7、重命名目录名为:ThemeStoreGraphQL,然后批量替换 ThemeStoreGraphQl 为 ThemeStoreGraphQL。在 PhpStorm 中重命名失败,提示:java.io.IOException: 无法将 ‘E:\wwwroot\object\Modules\ThemeStoreGraphQl’重命名为 ThemeStoreGraphQL。如图4

重命名目录名为:ThemeStoreGraphQL,然后批量替换 ThemeStoreGraphQl 为 ThemeStoreGraphQL。在 PhpStorm 中重命名失败,提示:java.io.IOException: 无法将 'E:\wwwroot\object\Modules\ThemeStoreGraphQl'重命名为 ThemeStoreGraphQL

图4

8、在操作系统目录中重命名。如图5

在操作系统目录中重命名

图5

9、批量替换后,执行 composer install。仍然报错:Illuminate\Contracts\Container\BindingResolutionException : Target [Modules\ThemeStoreGraphQL\ThemeSetting\Schema\TemplateSchemaLoaderInterface] is not instantiable while building [Modules\ThemeStoreGraphQL\Resolver\OnlineStoreTheme\TemplateSettingsDataResolver].

10、查看 /bootstrap/cache/services.php,GraphQlResolverServiceProvider 在数组键 ‘providers’ 中存在,在数组键 ‘eager’ 中不存在。

<?php return array (
  'providers' => 
  array (
    90 => 'Modules\\ThemeStoreDB\\Providers\\ThemeStoreDBServiceProvider',
    91 => 'Modules\\ThemeLocalization\\Providers\\ThemeLocalizationServiceProvider',
    92 => 'Modules\\ThemeAsset\\Providers\\ThemeAssetServiceProvider',
    93 => 'Modules\\ThemeSetting\\Providers\\ThemeSettingServiceProvider',
    94 => 'Modules\\Theme\\Providers\\ThemeServiceProvider',
    95 => 'Modules\\ThemeStoreGraphQL\\Providers\\GraphQlResolverServiceProvider',
  ),
  'eager' => 
  array (
    74 => 'Modules\\ThemeStoreDB\\Providers\\ThemeStoreDBServiceProvider',
    75 => 'Modules\\ThemeLocalization\\Providers\\ThemeLocalizationServiceProvider',
    76 => 'Modules\\ThemeAsset\\Providers\\ThemeAssetServiceProvider',
    77 => 'Modules\\ThemeSetting\\Providers\\ThemeSettingServiceProvider',
    78 => 'Modules\\Theme\\Providers\\ThemeServiceProvider',
  ),
);

11、编辑 /Modules/ThemeStoreGraphQL/Providers/GraphQlResolverServiceProvider.php,删除掉 implements DeferrableProvider 后,查看 /bootstrap/cache/services.php 的 GraphQlResolverServiceProvider 在数组键 ‘eager’ 中存在,且程序运行不再报错。


use Illuminate\Contracts\Support\DeferrableProvider;

class GraphQlResolverServiceProvider extends ServiceProvider implements DeferrableProvider


12、要延迟加载提供者,需要实现 \Illuminate\Contracts\Support\DeferrableProvider 接口并置一个 provides 方法。这个 provides 方法返回该提供者注册的服务容器绑定


use Illuminate\Contracts\Support\DeferrableProvider;

class GraphQlResolverServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * 获取由提供者提供的服务。
     *
     * @return array
     */
    public function provides()
    {
        return [TemplateSchemaLoaderInterface::class, TemplateSettingsDataResolver::class, SettingPersisterInterface::class];
    }
}


13、查看 /bootstrap/cache/services.php 的 GraphQlResolverServiceProvider 在数组键 ‘eager’ 中不存在,运行时不再报错。如图6

查看 /bootstrap/cache/services.php 的 GraphQlResolverServiceProvider 在数组键 'eager' 中不存在,运行时不再报错

图6


PS E:\wwwroot\object> php artisan lighthouse:ide-helper
Wrote schema directive definitions to E:\wwwroot\object/schema-directives.graphql.
Wrote definitions for programmatically registered types to E:\wwwroot\object/programmatic-types.graphql.
Wrote PHP definitions to E:\wwwroot\object/_lighthouse_ide_helper.php.

It is recommended to add them to your .gitignore file.


]]>
https://www.shuijingwanwq.com/2022/06/29/6707/feed/ 0
在 Laravel 6、LightHouse 5、Module 中使用 @rules 指令时,应用 exists 规则时的本地化实现 https://www.shuijingwanwq.com/2022/04/29/6354/ https://www.shuijingwanwq.com/2022/04/29/6354/#respond Fri, 29 Apr 2022 01:12:38 +0000 https://www.shuijingwanwq.com/?p=6354 Post Views: 109 1、当语言区域为 en 时,当验证的参数不存在时,响应:The selected theme id is invalid.。如图1
当语言区域为 en 时,当验证的参数不存在时,响应:The selected theme id is invalid.

图1



{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeEditorSessionCreate].",
      "extensions": {
        "validation": {
          "themeId": [
            "The selected theme id is invalid."
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeEditorSessionCreate"
      ],
      "trace": ...
    }
  ],
  "data": {
    "onlineStoreThemeEditorSessionCreate": null
  }
}


2、当语言区域为 zh_CN 时,当验证的参数不存在时,响应:所选的theme id无效。预期为:所选的主题ID无效。如图2
当语言区域为 zh_CN 时,当验证的参数不存在时,响应:所选的theme id无效。预期为:所选的主题ID无效。

图2



{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeEditorSessionCreate].",
      "extensions": {
        "validation": {
          "themeId": [
            "所选的theme id无效。"
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeEditorSessionCreate"
      ],
      "trace": ...
    }
  ],
  "data": {
    "onlineStoreThemeEditorSessionCreate": null
  }
}


3、查看 GraphQL 代码实现,/Modules/ThemeStore/Resources/graphql/theme_editor.graphql


extend type Mutation {
    "创建主题编辑会话"
    onlineStoreThemeEditorSessionCreate(themeId: ID! @rules(apply: ["exists:theme_asset,theme_id"])): ThemeEditorSessionCreatePayload @field(resolver: "Modules\\ThemeStore\\Resolver\\ThemeEditor\\CreateThemeEditorSessionResolver")
}

type ThemeEditorSessionCreatePayload
{
    "会话"
    session: Session!
}




4、查看 语言文件 ,/Modules/ThemeStore/Resources/lang/zh_CN/validation.php,已经指定了 theme_id 的自定义属性名称,但是,并未生效
<pre class="wp-block-syntaxhighlighter-code">

<?php

return [

    'attributes' => [
        'theme_id' => '主题ID',
    ],
];


</pre>
5、即使将此配置放至语言文件 /resources/lang/zh_CN/validation.php 中,仍然未生效,响应:所选的theme_id无效。但是,其默认是使用的此语言文件的配置。将验证规则消息放在任何语言的 validation.php 中名为 attributes 的键中,验证消息的 :attribute 部分不会被翻译(模块上下文)。
<pre class="wp-block-syntaxhighlighter-code">

<?php

return [

	'exists' => '所选的:attribute无效。',

    'attributes' => [
        'theme_id' => '主题ID',
    ],
];


</pre>
6、在 Laravel v8.7.90、nwidart/laravel-modules 8.2.0 中,编辑 /resources/lang/zh_CN/validation.php ,attributes 会生效。


    /**
     * 显示创建博客文章的表单
     *
     * @return Response
     */
    public function create(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'theme_id' => 'required|exists:users,id',
        ]);

        if ($validator->fails()) {
            dd($validator->errors());
        }

        // 保存博客文章…

        return view('blog::post.create');
    }


<pre class="wp-block-syntaxhighlighter-code">

<?php
return [
    'exists' => '所选的:attribute无效。resources',

    'attributes' => [
        'theme_id' => '主题ID',
    ],
];


</pre>


Illuminate\Support\MessageBag {#361 ▼
  #messages: array:1 [▼
    "theme_id" => array:1 [▼
      0 => "所选的主题ID无效。resources"
    ]
  ]
  #format: ":message"
}


7、决定在当前版本:Laravel v6.20.43、nwidart/laravel-modules 7.3.0 中,模拟步骤6的实现。以确定是否与框架、模块包的版本有关,进而导致在应用的根目录中,attributes 不生效的问题。确定是无关的。那么原因在于 LightHouse 5。
<pre class="wp-block-syntaxhighlighter-code">

<?php
return [
    'array' => ':attribute必须是一个数组。',

    'attributes' => [
        'filter' => '过滤器'
    ],
];


</pre>


{
  "message": "The given data was invalid.",
  "errors": {
    "filter": [
      "过滤器必须是一个数组。"
    ]
  },
  "status_code": 422,
  "debug": {
    "line": 385,
    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Validation\\Validator.php",
    "class": "Illuminate\\Validation\\ValidationException",
    "trace": ...
  }
}


8、参考:https://github.com/nuwave/lighthouse/issues/1773 ,虽然已经在 @rules 指令上实现了翻译功能,但是实现得不够彻底。即验证属性未支持。开发者建议:https://lighthouse-php.com/master/security/validation.html#validator-classes 。支持通过任意 PHP 代码定义规则。 9、暂时不准备编写一个自定义规则。决定在非英语环境下,属性名未翻译为对应的语言,暂且接受。且语言文件在根目录下,不在对应的模块目录下。  ]]>
https://www.shuijingwanwq.com/2022/04/29/6354/feed/ 0
在 Laravel 6、LightHouse 5、PHPUnit 中编写测试 Mutation,删除缓存中的数据实现 https://www.shuijingwanwq.com/2022/04/27/6350/ https://www.shuijingwanwq.com/2022/04/27/6350/#respond Wed, 27 Apr 2022 01:13:13 +0000 https://www.shuijingwanwq.com/?p=6350 Post Views: 74 1、在程序实现中,先请求 API,生成缓存标识。然后再请求 API,删除缓存标识。如图1
在程序实现中,先请求 API,生成缓存标识。然后再请求 API,删除缓存标识。

图1

2、如果要在测试中通过,前提是必须存在一个明确的缓存标识,以用于删除请求的参数。 3、参考网址:https://learnku.com/laravel/t/22690 。使用 TDD 测试驱动开发来构建 Laravel REST API。测试删除路由,其是先直接操作数据库,生成一条记录,然后再调用 HTTP API,测试删除操作。


// 测试删除路由
public function testDelete(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.delete',['recipe' => $recipe->id]));
    $response->assertStatus(200);
    // 断言没有食谱
    $this->assertEquals(0,$this->user->recipes()->count());
}



4、因此,决定也如此设计。先直接操作缓存,生成对应的缓存标识,然后再执行 HTTP API 请求测试。



    public function testDeleteThemeEditorCode(): void
    {
        $random = Str::random(36);
        $value = [
            'theme_id' => 'vogue'
        ];
        Cache::tags([ThemeEditorResolver::TAG_THEME_EDITOR, ThemeEditorResolver::TAG_THEME_EDITOR_CODE])->put($random, $value, 86400);

        $response = $this->graphQL('
            mutation DeleteThemeEditorCode($themeEditorCode: String!) {
                onlineStoreThemeEditorCodeDelete(themeEditorCode: $themeEditorCode) {
                    deletedThemeEditorCode
                }
            }
        ', [
            'themeEditorCode' => $random,
        ]);

        $response->assertJson(
            [
                'data' => [
                    'onlineStoreThemeEditorCodeDelete' => [
                        'deletedThemeEditorCode' => $random
                    ],
                ],
            ]
        );
    }



5、运行测试,测试通过。如图2
运行测试,测试通过

图2



PS E:\wwwroot\object> .\vendor\bin\phpunit .\Modules\ThemeStore\Tests\Functional\GraphQl\ThemeEditorGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 1.31 seconds, Memory: 72.00 MB

OK (2 tests, 4 assertions)


]]>
https://www.shuijingwanwq.com/2022/04/27/6350/feed/ 0
在 Laravel 6、LightHouse 5、Module 中,自定义验证规则的使用 https://www.shuijingwanwq.com/2022/04/26/6341/ https://www.shuijingwanwq.com/2022/04/26/6341/#respond Tue, 26 Apr 2022 01:21:13 +0000 https://www.shuijingwanwq.com/?p=6341 Post Views: 96 1、现阶段需要在 GraphQL API 中实现一个删除缓存标识的接口。参考 Shopify 的示例。当缓存标识不存在时,响应失败。如图1
现阶段需要在 GraphQL API 中实现一个删除缓存标识的接口。参考 Shopify 的示例。当缓存标识不存在时,响应失败

图1



mutation DeleteThemeSessionId($sessionId: String!) {
  onlineStoreEditorSessionDelete(sessionId: $sessionId) {
    deletedSessionId
    userErrors {
      field
      message
      __typename
    }
    __typename
  }
}





{
  "sessionId": "H9Mc8FwH5Eu5LR37XAzsehh4"
}




{
  "data": {
    "onlineStoreEditorSessionDelete": {
      "deletedSessionId": null,
      "userErrors": [
        {
          "field": [
            "sessionId"
          ],
          "message": "在线商店编辑器访问不存在",
          "__typename": "UserError"
        }
      ],
      "__typename": "OnlineStoreEditorSessionDeletePayload"
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 10,
      "actualQueryCost": 10,
      "throttleStatus": {
        "maximumAvailable": 1000,
        "currentlyAvailable": 990,
        "restoreRate": 50
      }
    }
  }
}


2、由于验证此缓存标识是否存在的规则,会用于大量的 API 中。最张决定自定义验证规则,以复用此验证规则。 3、由于是在模块中编写验证规则。module:make-rule Create a new validation rule for the specified module.
<pre class="wp-block-syntaxhighlighter-code">

PS E:\wwwroot\object> php artisan help module:make-rule
Description:
  Create a new validation rule for the specified module.

Usage:
  module:make-rule <name> [<module>]

Arguments:
  name                  The name of the rule class.
  module                The name of module will be used.

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
PS E:\wwwroot\object> php artisan module:make-rule ThemeEditor/ThemeEditorCodeExistsRule ThemeStore
Created : E:/wwwroot/object/Modules/ThemeStore/Rules/ThemeEditor/ThemeEditorCodeExistsRule.php
PS E:\wwwroot\object>

</pre>
4、编辑规则类,判断缓存标识是否存在。/Modules/ThemeStore/Rules/ThemeEditor/ThemeEditorCodeExistsRule.php
<pre class="wp-block-syntaxhighlighter-code">

<?php

namespace Modules\ThemeStore\Rules\ThemeEditor;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Cache;
use Modules\ThemeStore\Resolver\ThemeEditor\ThemeEditorResolver;

class ThemeEditorCodeExistsRule implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return Cache::tags([ThemeEditorResolver::TAG_THEME_EDITOR, ThemeEditorResolver::TAG_THEME_EDITOR_CODE])->has($value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}


</pre>
5、编辑 /ThemeStore/Resources/graphql/theme_editor.graphql 。使用 @rules 指令,通过完全限定的类名引用自定义验证规则:ThemeEditorCodeExistsRule


extend type Mutation {
    ...
    "删除主题编辑标识"
    onlineStoreThemeEditorCodeDelete(themeEditorCode: String! @rules(apply: ["Modules\\ThemeStore\\Rules\\ThemeEditor\\ThemeEditorCodeExistsRule"])): ThemeEditorCodeDeletePayload @field(resolver: "Modules\\ThemeStore\\Resolver\\ThemeEditor\\DeleteThemeEditorCodeResolver")
}

type ThemeEditorCodeDeletePayload
{
    deletedThemeEditorCode: String
}



6、新生成了一个缓存标识:vRYbn7NQiEWMbH2G6sXB8AE4aoBIk4JQOw2p,先测试验证失败的情况。


mutation {
  onlineStoreThemeEditorCodeDelete(themeEditorCode: "vRYbn7NQiEWMbH2G6sXB8AE4aoBIk4JQOw2p0") {
    deletedThemeEditorCode
  }
}





{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeEditorCodeDelete].",
      "extensions": {
        "validation": {
          "themeEditorCode": [
            "The validation error message."
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeEditorCodeDelete"
      ],
      "trace": [
		...
	  ]
    }
  ],
  "data": {
    "onlineStoreThemeEditorCodeDelete": null
  }
}


7、新建模块下的 /Modules/ThemeStore/Resources/lang/en/validation.php 语言文件。
<pre class="wp-block-syntaxhighlighter-code">

<?php

return [
    'custom' => [
        'theme_editor_code' => [
            'theme_editor_code_exists_rule' => 'The selected :attribute is invalid.',
        ],
    ],

    'attributes' => [],
];


</pre>
8、编辑规则类,/Modules/ThemeStore/Rules/ThemeEditor/ThemeEditorCodeExistsRule.php。从翻译文件中返回一个错误消息,从 message 方法中调用辅助函数 trans。
<pre class="wp-block-syntaxhighlighter-code">

<?php

namespace Modules\ThemeStore\Rules\ThemeEditor;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Cache;
use Modules\ThemeStore\Resolver\ThemeEditor\ThemeEditorResolver;

class ThemeEditorCodeExistsRule implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return Cache::tags([ThemeEditorResolver::TAG_THEME_EDITOR, ThemeEditorResolver::TAG_THEME_EDITOR_CODE])->has($value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return trans('theme_store::validation.custom.theme_editor_code.theme_editor_code_exists_rule');
    }
}


</pre>
9、再次测试验证失败的情况。The validation error message. 已经被替换为:The selected theme editor code is invalid.。如图2
再次测试验证失败的情况。The validation error message. 已经被替换为:The selected theme editor code is invalid.

图2



mutation {
  onlineStoreThemeEditorCodeDelete(themeEditorCode: "vRYbn7NQiEWMbH2G6sXB8AE4aoBIk4JQOw2p0") {
    deletedThemeEditorCode
  }
}





{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeEditorCodeDelete].",
      "extensions": {
        "validation": {
          "themeEditorCode": [
            "The selected theme editor code is invalid."
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeEditorCodeDelete"
      ],
      "trace": [
		...
	  ]
    }
  ],
  "data": {
    "onlineStoreThemeEditorCodeDelete": null
  }
}


10、新建模块下的 /Modules/ThemeStore/Resources/lang/zh_CN/validation.php 语言文件。
<pre class="wp-block-syntaxhighlighter-code">

<?php

return [
    'custom' => [
        'theme_editor_code' => [
            'theme_editor_code_exists_rule' => '所选的主题编辑标识无效。',
        ],
    ],

    'attributes' => [],
];


</pre>
11、当前语言区域被设置为:zh_CN 时,测试失败的响应,响应信息为中文。符合预期。如图3
当前语言区域被设置为:zh_CN 时,测试失败的响应,响应信息为中文。符合预期

图3



{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeEditorCodeDelete].",
      "extensions": {
        "validation": {
          "themeEditorCode": [
            "所选的主题编辑标识无效。"
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeEditorCodeDelete"
      ],
      "trace": [
		...
	  ]
    }
  ],
  "data": {
    "onlineStoreThemeEditorCodeDelete": null
  }
}


12、可将 /Modules/ThemeStore/Resources/lang/en/validation.php 复制为 /Modules/ThemeStore/Resources/lang/en_US/validation.php。当语言区域设置为 en_US 时,/en_US/validation.php 的优先级高于 /en/validation.php。如果在 /en_US/validation.php 中查找不到,则会从 /en/validation.php 查找。测试失败的响应。
<pre class="wp-block-syntaxhighlighter-code">

<?php

return [
    'custom' => [],

    'attributes' => [],
];


</pre>


{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeEditorCodeDelete].",
      "extensions": {
        "validation": {
          "themeEditorCode": [
            "The selected theme editor code is invalid."
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeEditorCodeDelete"
      ],
      "trace": ...
    }
  ],
  "data": {
    "onlineStoreThemeEditorCodeDelete": null
  }
}


13、注:theme_store 的名称源于 /ThemeStore/Providers/ThemeStoreServiceProvider.php


private $moduleNameLower = 'theme_store';


14、测试验证通过的情况,符合预期。如图4
测试验证通过的情况,符合预期

图4



mutation {
  onlineStoreThemeEditorCodeDelete(themeEditorCode: "vRYbn7NQiEWMbH2G6sXB8AE4aoBIk4JQOw2p") {
    deletedThemeEditorCode
  }
}





{
  "data": {
    "onlineStoreThemeEditorCodeDelete": {
      "deletedThemeEditorCode": "vRYbn7NQiEWMbH2G6sXB8AE4aoBIk4JQOw2p"
    }
  }
}


 ]]>
https://www.shuijingwanwq.com/2022/04/26/6341/feed/ 0
在 Lighthouse 5 中,基于 PHPUnit 编写 Mutation 的测试 https://www.shuijingwanwq.com/2022/04/01/6233/ https://www.shuijingwanwq.com/2022/04/01/6233/#respond Fri, 01 Apr 2022 01:27:44 +0000 https://www.shuijingwanwq.com/?p=6233 Post Views: 87

1、在 Altair GraphQl Client 中运行成功。如图1

在 Altair GraphQl Client 中运行成功。

图1


mutation CreateThemeAsset {
  onlineStoreThemeAssetCreate(
    input: { themeId: "vogue", content: "string", key: "js/20220307.4.js" }
  ) {
    themeAsset {
      themeId
      key
      content
    }
  }
}





{
  "data": {
    "onlineStoreThemeAssetCreate": {
      "themeAsset": {
        "themeId": "vogue",
        "key": "js/20220307.4.js",
        "content": "string"
      }
    }
  }
}


2、编辑测试文件,/Modules/ThemeStore/Tests/Functional/GraphQl/MutationThemeAssetGraphQlApiTest.php。

<?php
 
namespace Modules\ThemeStore\Tests\Functional\GraphQl;
 
use Nuwave\Lighthouse\Testing\MakesGraphQLRequests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Nuwave\Lighthouse\Testing\ClearsSchemaCache;
use Sentry\Util\JSON;
use Tests\CreatesApplication;
 
class MutationThemeAssetGraphQlApiTest extends BaseTestCase
{
    use CreatesApplication,
        ClearsSchemaCache,
        MakesGraphQLRequests;
 
    public function testCreateThemeAsset(): void
    {
 
        $input = [
            'themeId' => 'vogue',
            'content' => 'string',
            'key' => 'js/20220307.8.js',
        ];
 
        $response = $this->graphQL('
            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {
                onlineStoreThemeAssetCreate(input: $input) {
                    themeAsset {
                        themeId
                        key
                        content
                    }
                }
            }
        ', [
            'input' => json_encode($input)
        ]);
 
        $response->assertJson(
            [
                'data' => [
                    'onlineStoreThemeAssetCreate' => [
                        'themeAsset' => $input
                    ],
                ],
            ]
        );
    }
 
    protected function setUp(): void
    {
        parent::setUp();
 
        $this->bootClearsSchemaCache();
    }
}
 
 
 

3、运行测试用例,报错:Expected type ThemeAssetCreateInput to be an object.。如图2

运行测试用例,报错:Expected type ThemeAssetCreateInput to be an object.。

图2


PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 2.89 seconds, Memory: 16.00 MB

There was 1 failure:

1) Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest::testCreateThemeAsset
Unable to find JSON:

[{
    "data": {
        "onlineStoreThemeAssetCreate": {
            "themeAsset": {
                "themeId": "vogue",
                "content": "string",
                "key": "js/20220307.8.js"
            }
        }
    }
}]

within response JSON:

[{
    "errors": [
        {
            "message": "Variable \"$input\" got invalid value \"{\"themeId\":\"vogue\",\"content\":\"string\",\"key\":\"js\\/20220307.8.js\"}\"; Expected type ThemeAssetCreateInput to be an object.",
            "extensions": {
                "category": "graphql"
            },
            "locations": [
                {
                    "line": 2,
                    "column": 39
                }
            ]
        }
    ]
}].


Failed asserting that an array has the subset Array &0 (
    'data' => Array &1 (
        'onlineStoreThemeAssetCreate' => Array &2 (
            'themeAsset' => Array &3 (
                'themeId' => 'vogue'
                'content' => 'string'
                'key' => 'js/20220307.8.js'
            )
        )
    )
).
--- Expected
+++ Actual
@@ @@
       ),
     ),
   ),
-  'data' =>
-  array (
-    'onlineStoreThemeAssetCreate' =>
-    array (
-      'themeAsset' =>
-      array (
-        'themeId' => 'vogue',
-        'content' => 'string',
-        'key' => 'js/20220307.8.js',
-      ),
-    ),
-  ),
 )

E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Assert.php:108
E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestResponse.php:479
E:\wwwroot\object\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php:56

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
PS E:\wwwroot\object>


4、重新编辑测试文件

<?php
 
namespace Modules\ThemeStore\Tests\Functional\GraphQl;
 
use Nuwave\Lighthouse\Testing\MakesGraphQLRequests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Nuwave\Lighthouse\Testing\ClearsSchemaCache;
use Sentry\Util\JSON;
use Tests\CreatesApplication;
 
class MutationThemeAssetGraphQlApiTest extends BaseTestCase
{
    use CreatesApplication,
        ClearsSchemaCache,
        MakesGraphQLRequests;
 
    public function testCreateThemeAsset(): void
    {
        $input = [
            'themeId' => 'vogue',
            'content' => 'string',
            'key' => 'js/20220307.9.js',
        ];
 
        $response = $this->graphQL('
            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {
                onlineStoreThemeAssetCreate(input: $input) {
                    themeAsset {
                        themeId
                        key
                        content
                    }
                }
            }
        ', [
            'input' => json_decode(json_encode($input))
        ]);
 
        $response->assertJson(
            [
                'data' => [
                    'onlineStoreThemeAssetCreate' => [
                        'themeAsset' => $input
                    ],
                ],
            ]
        );
    }
 
    protected function setUp(): void
    {
        parent::setUp();
 
        $this->bootClearsSchemaCache();
    }
}
 

5、再次运行测试,测试通过。


PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 2.61 seconds, Memory: 16.00 MB

OK (1 test, 1 assertion)


6、再运行一次测试,测试未通过。原因在于同样的请求参数,不允许重复,否则验证失败,进而测试未通过断言。


PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 2.77 seconds, Memory: 18.00 MB

There was 1 failure:

1) Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest::testCreateThemeAsset
Unable to find JSON:

[{
    "data": {
        "onlineStoreThemeAssetCreate": {
            "themeAsset": {
                "themeId": "vogue",
                "content": "string",
                "key": "js/20220307.9.js"
            }
        }
    }
}]

within response JSON:

[{
    "errors": [
        {
            "message": "Validation failed for the field [onlineStoreThemeAssetCreate].",
            "extensions": {
                "validation": {
                    "input.key": [
                        "The input.key has already been taken"
                    ]
                },
                "category": "validation"
            },
            "locations": [
                {
                    "line": 3,
                    "column": 17
                }
            ],
            "path": [
                "onlineStoreThemeAssetCreate"
            ],
            "trace": [
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\ArgTraversalDirective.php",
                    "line": 29,
                    "call": "Nuwave\\Lighthouse\\Validation\\ValidateDirective::Nuwave\\Lighthouse\\Validation\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Directives\\TrimDirective.php",
                    "line": 56,
                    "call": "Nuwave\\Lighthouse\\Schema\\Directives\\ArgTraversalDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Schema\\Factories\\FieldFactory.php",
                    "line": 99,
                    "call": "Nuwave\\Lighthouse\\Schema\\Directives\\TrimDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 623,
                    "call": "Nuwave\\Lighthouse\\Schema\\Factories\\FieldFactory::Nuwave\\Lighthouse\\Schema\\Factories\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 550,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL\\Type\\Definition\\FieldDefinition, instance of GraphQL\\Language\\AST\\FieldNode, instance of Closure, null, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 474,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 857,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'onlineStoreThemeAssetCreate')"
                },
                {
                    "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'onlineStoreThemeAssetCreate')"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 859,
                    "function": "array_reduce(array(1), instance of Closure, array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 490,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 263,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
                    "line": 215,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\Executor\\Executor.php",
                    "line": 156,
                    "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
                    "line": 162,
                    "call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
                    "line": 94,
                    "call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null, array(29))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 268,
                    "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null, array(29))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 203,
                    "call": "Nuwave\\Lighthouse\\GraphQL::executeParsedQuery(instance of GraphQL\\Language\\AST\\DocumentNode, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 162,
                    "call": "Nuwave\\Lighthouse\\GraphQL::parseAndExecuteQuery('\n            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {\n                onlineStoreThemeAssetCreate(input: $input) {\n                    themeAsset {\n                        themeId\n                        key\n                        content\n                    }\n                }\n            }\n        ', instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, null)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 121,
                    "call": "Nuwave\\Lighthouse\\GraphQL::executeOperation(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Utils.php",
                    "line": 99,
                    "call": "Nuwave\\Lighthouse\\GraphQL::Nuwave\\Lighthouse\\{closure}(instance of GraphQL\\Server\\OperationParams)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\GraphQL.php",
                    "line": 120,
                    "call": "Nuwave\\Lighthouse\\Support\\Utils::applyEach(instance of Closure, instance of GraphQL\\Server\\OperationParams)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Controllers\\GraphQLController.php",
                    "line": 32,
                    "call": "Nuwave\\Lighthouse\\GraphQL::executeOperationOrOperations(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php",
                    "line": 48,
                    "call": "Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController::__invoke(instance of Illuminate\\Http\\Request, instance of Nuwave\\Lighthouse\\GraphQL, instance of Illuminate\\Events\\Dispatcher, instance of Laragraph\\Utils\\RequestParser, instance of Nuwave\\Lighthouse\\Execution\\SingleResponse, instance of Nuwave\\Lighthouse\\Execution\\ContextFactory)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
                    "line": 219,
                    "call": "Illuminate\\Routing\\ControllerDispatcher::dispatch(instance of Illuminate\\Routing\\Route, instance of Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController, '__invoke')"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
                    "line": 176,
                    "call": "Illuminate\\Routing\\Route::runController()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 681,
                    "call": "Illuminate\\Routing\\Route::run()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 130,
                    "call": "Illuminate\\Routing\\Router::Illuminate\\Routing\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AttemptAuthentication.php",
                    "line": 34,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AttemptAuthentication::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Support\\Http\\Middleware\\AcceptJson.php",
                    "line": 27,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AcceptJson::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 105,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 683,
                    "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 658,
                    "call": "Illuminate\\Routing\\Router::runRouteWithinStack(instance of Illuminate\\Routing\\Route, instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 624,
                    "call": "Illuminate\\Routing\\Router::runRoute(instance of Illuminate\\Http\\Request, instance of Illuminate\\Routing\\Route)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
                    "line": 613,
                    "call": "Illuminate\\Routing\\Router::dispatchToRoute(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
                    "line": 170,
                    "call": "Illuminate\\Routing\\Router::dispatch(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 130,
                    "call": "Illuminate\\Foundation\\Http\\Kernel::Illuminate\\Foundation\\Http\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\barryvdh\\laravel-debugbar\\src\\Middleware\\InjectDebugbar.php",
                    "line": 60,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\app\\Http\\Middleware\\ChangeAppUrlMiddleware.php",
                    "line": 23,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "App\\Http\\Middleware\\ChangeAppUrlMiddleware::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
                    "line": 21,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
                    "line": 21,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
                    "line": 27,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode.php",
                    "line": 63,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\fideloper\\proxy\\src\\TrustProxies.php",
                    "line": 57,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Fideloper\\Proxy\\TrustProxies::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\dingo\\api\\src\\Http\\Middleware\\Request.php",
                    "line": 111,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 171,
                    "call": "Dingo\\Api\\Http\\Middleware\\Request::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
                    "line": 105,
                    "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
                    "line": 145,
                    "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
                    "line": 110,
                    "call": "Illuminate\\Foundation\\Http\\Kernel::sendRequestThroughRouter(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Testing\\Concerns\\MakesHttpRequests.php",
                    "line": 470,
                    "call": "Illuminate\\Foundation\\Http\\Kernel::handle(instance of Illuminate\\Http\\Request)"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Testing\\Concerns\\MakesHttpRequests.php",
                    "line": 442,
                    "call": "Illuminate\\Foundation\\Testing\\TestCase::call('POST', 'https://object.local/graphql', array(0), array(0), array(1), array(3), '{\"query\":\"\\n            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {\\n                onlineStoreThemeAssetCreate(input: $input) {\\n                    themeAsset {\\n                        themeId\\n                        key\\n                        content\\n                    }\\n                }\\n            }\\n        \",\"variables\":{\"input\":{\"themeId\":\"vogue\",\"content\":\"string\",\"key\":\"js\\/20220307.9.js\"}}}')"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Testing\\Concerns\\MakesHttpRequests.php",
                    "line": 301,
                    "call": "Illuminate\\Foundation\\Testing\\TestCase::json('POST', 'https://object.local/graphql', array(2), array(3))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Testing\\MakesGraphQLRequests.php",
                    "line": 79,
                    "call": "Illuminate\\Foundation\\Testing\\TestCase::postJson('https://object.local/graphql', array(2), array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\nuwave\\lighthouse\\src\\Testing\\MakesGraphQLRequests.php",
                    "line": 62,
                    "call": "Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest::postGraphQL(array(2), array(0))"
                },
                {
                    "file": "E:\\wwwroot\\object\\Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest.php",
                    "line": 36,
                    "call": "Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest::graphQL('\n            mutation CreateThemeAsset($input: ThemeAssetCreateInput!) {\n                onlineStoreThemeAssetCreate(input: $input) {\n                    themeAsset {\n                        themeId\n                        key\n                        content\n                    }\n                }\n            }\n        ', array(1))"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestCase.php",
                    "line": 1154,
                    "call": "Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest::testCreateThemeAsset()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestCase.php",
                    "line": 842,
                    "call": "PHPUnit\\Framework\\TestCase::runTest()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestResult.php",
                    "line": 693,
                    "call": "PHPUnit\\Framework\\TestCase::runBare()"
                },
                {
                    "file": "E:\\wwwroot\\object\\vendor\\phpunit\\phpunit\\src\\Framework\\TestCase.php",
                    "line": 796,
                    "call": "PHPUnit\\Framework\\TestResult::run(instance of Modules\\ThemeStore\\Tests\\Functional\\GraphQl\\MutationThemeAssetGraphQlApiTest(1))"
                },
                {
                    "file": "C:\\Users\\Lenovo\\AppData\\Local\\Temp\\PHP57E0.tmp",
                    "line": 261,
                    "call": "PHPUnit\\Framework\\TestCase::run(instance of PHPUnit\\Framework\\TestResult(1))"
                },
                {
                    "file": "C:\\Users\\Lenovo\\AppData\\Local\\Temp\\PHP57E0.tmp",
                    "line": 1040,
                    "function": "__phpunit_run_isolated_test()"
                }
            ]
        }
    ],
    "data": {
        "onlineStoreThemeAssetCreate": null
    }
}].


Failed asserting that an array has the subset Array &0 (
    'data' => Array &1 (
        'onlineStoreThemeAssetCreate' => Array &2 (
            'themeAsset' => Array &3 (
                'themeId' => 'vogue'
                'content' => 'string'
                'key' => 'js/20220307.9.js'
            )
        )
    )
).
--- Expected
+++ Actual
@@ @@
   ),
   'data' =>
   array (
-    'onlineStoreThemeAssetCreate' =>
-    array (
-      'themeAsset' =>
-      array (
-        'themeId' => 'vogue',
-        'content' => 'string',
-        'key' => 'js/20220307.9.js',
-      ),
-    ),
+    'onlineStoreThemeAssetCreate' => NULL,
   ),
 )

E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Assert.php:108
E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestResponse.php:479
E:\wwwroot\object\Modules\ThemeStore\Tests\Functional\GraphQl\MutationThemeAssetGraphQlApiTest.php:43

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
PS E:\wwwroot\object>



7、虽然现在针对测试用例配置了一个单独的测试数据库,无需要担心影响到开发数据库。但是要避免多次测试时,测试不通过的问题,决定尽量避免掉请求参数的重复。将变量 key 的值基于时间戳生成。其值示例:js/20220307144656.js。这样可以保证在每一秒钟可以运行一次测试,且能够通过。后续可以在此测试文件中添加一个测试删除此条创建的记录的方法。如图3

虽然现在针对测试用例配置了一个单独的测试数据库,无需要担心影响到开发数据库。但是要避免多次测试时,测试不通过的问题,决定尽量避免掉请求参数的重复。将变量 key 的值基于时间戳生成。其值示例:js/20220307144656.js。这样可以保证在每一秒钟可以运行一次测试,且能够通过。后续可以在此测试文件中添加一个测试删除此条创建的记录的方法。

图3


        $input = [
            'themeId' => 'vogue',
            'content' => 'string',
            'key' => 'js/' . date('YmdHis') . '.js',
        ];


 

]]>
https://www.shuijingwanwq.com/2022/04/01/6233/feed/ 0
编写 Lighthouse 5 的自动化测试用例时,PhpStorm 不能够自动完成(无法找到要转到的声明)的解决 https://www.shuijingwanwq.com/2022/03/11/6113/ https://www.shuijingwanwq.com/2022/03/11/6113/#respond Fri, 11 Mar 2022 01:16:15 +0000 assertJson]]> =4.10.1]]> https://www.shuijingwanwq.com/?p=6113 Post Views: 112 1、编写 Lighthouse 5 的自动化测试用例时,PhpStorm 不能够自动完成。点击 $response->assertJson 时,无法找到要转到的声明。方法 ‘assertJson’ 在 \Illuminate\Testing\TestResponse 中未找到。如图1
编写 Lighthouse 5 的自动化测试用例时,PhpStorm 不能够自动完成。点击 $response->assertJson 时,无法找到要转到的声明。方法 'assertJson' 在 \Illuminate\Testing\TestResponse 中未找到

图1

2、打印 $response 对象,其实际路径为:\Illuminate\Foundation\Testing\TestResponse。如图2
打印 $response 对象,其实际路径为:\Illuminate\Foundation\Testing\TestResponse

图2



PS E:\wwwroot\object> ./vendor/bin/phpunit --process-isolation .\Modules\ThemeStore\Tests\Functional\GraphQl\OnlineStoreThemeGraphQlApiTest.php
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

E..                                                                 3 / 3 (100%)

Time: 11.89 seconds, Memory: 16.00 MB

There was 1 error:

1) Modules\ThemeStore\Tests\Functional\GraphQl\OnlineStoreThemeGraphQlApiTest::testGetThemeById
PHPUnit\Framework\Exception: Illuminate\Foundation\Testing\TestResponse Object
(
    [baseResponse] => Illuminate\Http\Response Object
        (
            [headers] => Symfony\Component\HttpFoundation\ResponseHeaderBag Object
                (
                    [computedCacheControl:protected] => Array
                        (
                            [no-cache] => 1
                            [private] => 1
                        )

                    [cookies:protected] => Array
                        (
                        )

                    [headerNames:protected] => Array
                        (
                            [cache-control] => Cache-Control
                            [date] => Date
                            [content-type] => Content-Type
                        )

                    [headers:protected] => Array
                        (
                            [cache-control] => Array
                                (
                                    [0] => no-cache, private
                                )

                            [date] => Array
                                (
                                    [0] => Thu, 17 Feb 2022 06:16:07 GMT
                                )

                            [content-type] => Array
                                (
                                    [0] => application/json
                                )

                        )

                    [cacheControl:protected] => Array
                        (
                        )

                )

            [content:protected] => {"data":{"onlineStoreTheme":{"id":"vogue","editable":false,"createdAt":"2022-02-17 14:16:07","name":"vogue"}}}
            [version:protected] => 1.1
            [statusCode:protected] => 200
            [statusText:protected] => OK
            [charset:protected] =>
            [original] => Array
                (
                    [data] => Array
                        (
                            [onlineStoreTheme] => Array
                                (
                                    [id] => vogue
                                    [editable] =>
                                    [createdAt] => 2022-02-17 14:16:07
                                    [name] => vogue
                                )

                        )

                )

            [exception] =>
        )

    [streamedContent:protected] =>
)

phpvfscomposer://E:\wwwroot\object\vendor\phpunit\phpunit\phpunit:60



3、在另一个项目中,PhpStorm 能够自动完成。如图3
在另一个项目中,PhpStorm 能够自动完成

图3

4、在另一个项目中,打印 $response 对象,其实际路径为:\Illuminate\Testing\TestResponse。相对于无法定位方法的项目,缺少了 \Foundation。如图4
在另一个项目中,打印 $response 对象,其实际路径为:\Illuminate\Testing\TestResponse。相对于无法定位方法的项目,缺少了 \Foundation

图4



PS E:\wwwroot\lighthouse-tutorial> ./vendor/bin/phpunit --process-isolation .\tests\Feature\PostsGraphQlApiTest.php
PHPUnit 9.5.11 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 00:07.704, Memory: 12.00 MB

There was 1 error:

1) Tests\Feature\PostsGraphQlApiTest::testQueriesPosts
PHPUnit\Framework\Exception: Illuminate\Testing\TestResponse Object
(
    [baseResponse] => Illuminate\Http\Response Object
        (
            [headers] => Symfony\Component\HttpFoundation\ResponseHeaderBag Object
                (
                    [computedCacheControl:protected] => Array
                        (
                            [no-cache] => 1
                            [private] => 1
                        )

                    [cookies:protected] => Array
                        (
                        )

                    [headerNames:protected] => Array
                        (
                            [cache-control] => Cache-Control
                            [date] => Date
                            [content-type] => Content-Type
                        )

                    [headers:protected] => Array
                        (
                            [cache-control] => Array
                                (
                                    [0] => no-cache, private
                                )

                            [date] => Array
                                (
                                    [0] => Thu, 17 Feb 2022 06:25:24 GMT
                                )

                            [content-type] => Array
                                (
                                    [0] => application/json
                                )

                        )

                    [cacheControl:protected] => Array
                        (
                        )

                )



5、参考网址:https://www.shuijingwanwq.com/2022/01/26/5827/ ,怀疑与此问题类似。在 Laravel Framework 6.20.44、nuwave/lighthouse v5.36.0 中,版本不匹配所导致。因为在另一个项目中,版本分别为:Laravel Framework 8.78.1、nuwave/lighthouse v5.35.0,版本是匹配的。 6、计划在当前项目中,将 nuwave/lighthouse v5.36.0 回退至 ~4.10.1。执行 composer remove nuwave/lighthouse 命令,卸载 nuwave/lighthouse。执行 composer require nuwave/lighthouse:~4.10.1 命令,安装 nuwave/lighthouse。回退至 nuwave/lighthouse v4.10.2 后,已经能够自动完成。如图5
计划在当前项目中,将 nuwave/lighthouse v5.36.0 回退至 ~4.10.1。执行 composer remove nuwave/lighthouse 命令,卸载 nuwave/lighthouse。执行 composer require nuwave/lighthouse:~4.10.1 命令,安装 nuwave/lighthouse。回退至 nuwave/lighthouse v4.10.2 后,已经能够自动完成

图5

7、点击跳转,其文件路径为:\Illuminate\Foundation\Testing\TestResponse。如图6
点击跳转,其文件路径为:\Illuminate\Foundation\Testing\TestResponse

图6

8、在 Issues 中搜索:\Illuminate\Testing\TestResponse,确认已经有人遇见过类似的问题:Refine test helper return types for static analysis。如图7
在 Issues 中搜索:\Illuminate\Testing\TestResponse,确认已经有人遇见过类似的问题:Refine test helper return types for static analysis

图7

9、因为 Laravel 在 v7 中移动了 TestResponse。官方建议使用:https://github.com/nuwave/lighthouse/releases/tag/v4.12.2 。可与 Laravel 7 完美配合。仍然与 Laravel 6 兼容。如图8
因为 Laravel 在 v7 中移动了 TestResponse。官方建议使用:https://github.com/nuwave/lighthouse/releases/tag/v4.12.2 。可与 Laravel 7 完美配合。仍然与 Laravel 6 兼容

图8

]]>
https://www.shuijingwanwq.com/2022/03/11/6113/feed/ 0