'integer']), new \ApiPlatform\OpenApi\Model\Parameter(name: 'status', in: 'query', required: false, schema: ['type' => 'string', 'enum' => ['pending', 'approved', 'rejected'], 'default' => 'approved']), new \ApiPlatform\OpenApi\Model\Parameter(name: 'page', in: 'query', required: false, schema: ['type' => 'integer', 'default' => 1]), new \ApiPlatform\OpenApi\Model\Parameter(name: 'per_page', in: 'query', required: false, schema: ['type' => 'integer', 'default' => 30, 'maximum' => 50]), new \ApiPlatform\OpenApi\Model\Parameter( name: 'with_answers', in: 'query', required: false, description: 'Embed top approved answers inside each question. Default: `true`. Pass `false` to skip.', schema: ['type' => 'boolean', 'default' => true], ), new \ApiPlatform\OpenApi\Model\Parameter( name: 'answers_per_question', in: 'query', required: false, description: 'How many answers to embed per question (1–20). Default: 5. Pinned answers appear first, then sorted by `useful_count` descending.', schema: ['type' => 'integer', 'default' => 5, 'minimum' => 1, 'maximum' => 20], ), new \ApiPlatform\OpenApi\Model\Parameter( name: 'answer_status', in: 'query', required: false, description: 'Status of embedded answers to return. Default: `approved`.', schema: ['type' => 'string', 'enum' => ['approved', 'pending', 'rejected'], 'default' => 'approved'], ), ], ), ), new Get( uriTemplate: '/questions/{id}', openapi: new Operation( tags: ['Product Q&A'], summary: 'Get a single question by ID', ), ), new Post( uriTemplate: '/questions', processor: ProductQuestionProcessor::class, denormalizationContext: ['groups' => ['mutation'], 'allow_extra_attributes' => true], openapi: new Operation( tags: ['Product Q&A'], summary: 'Submit a product question', description: 'Submit a question about a product. **Requires authentication** (Bearer token). New questions start in `pending` status pending admin approval.', requestBody: new \ApiPlatform\OpenApi\Model\RequestBody( required: true, content: new \ArrayObject([ 'application/json' => [ 'schema' => [ 'type' => 'object', 'required' => ['product_id', 'question', 'customer_name'], 'properties' => [ 'product_id' => ['type' => 'integer', 'example' => 1], 'question' => ['type' => 'string', 'example' => 'Does this come in blue?'], 'customer_name' => ['type' => 'string', 'example' => 'Jane Doe'], 'customer_email'=> ['type' => 'string', 'format' => 'email', 'example' => 'jane@example.com'], ], ], ], ]), ), responses: [ '201' => new \ApiPlatform\OpenApi\Model\Response(description: 'Question submitted. Starts in `pending` status.'), '422' => new \ApiPlatform\OpenApi\Model\Response(description: 'Validation error.'), ], ), ), new Patch( uriTemplate: '/questions/{id}', processor: ProductQuestionProcessor::class, denormalizationContext: ['groups' => ['mutation'], 'allow_extra_attributes' => true], openapi: new Operation( tags: ['Product Q&A — Admin'], summary: 'Update question status (admin)', description: 'Approve or reject a question. Intended for admin use.', requestBody: new \ApiPlatform\OpenApi\Model\RequestBody( required: true, content: new \ArrayObject([ 'application/merge-patch+json' => [ 'schema' => [ 'type' => 'object', 'properties' => [ 'status' => ['type' => 'string', 'enum' => ['pending', 'approved', 'rejected']], ], ], ], ]), ), ), ), new Delete( uriTemplate: '/questions/{id}', processor: ProductQuestionProcessor::class, openapi: new Operation( tags: ['Product Q&A — Admin'], summary: 'Delete a question', responses: [ '204' => new \ApiPlatform\OpenApi\Model\Response(description: 'Deleted.'), '404' => new \ApiPlatform\OpenApi\Model\Response(description: 'Not found.'), ], ), ), ], graphQlOperations: [ new QueryCollection( provider: ProductQuestionProvider::class, args: [ 'product_id' => ['type' => 'Int', 'description' => 'Filter by product ID'], 'status' => ['type' => 'String', 'description' => 'Filter by status (approved/pending/rejected)'], 'first' => ['type' => 'Int'], 'last' => ['type' => 'Int'], 'after' => ['type' => 'String'], 'before' => ['type' => 'String'], ] ), new Query(resolver: BaseQueryItemResolver::class), new Mutation( name: 'create', input: CreateProductQuestionInput::class, output: self::class, processor: ProductQuestionProcessor::class, ), new Mutation( name: 'updateStatus', processor: ProductQuestionProcessor::class, description: 'Update question status (admin)', ), new DeleteMutation( name: 'delete', description: 'Delete a question', ), ] )] #[ApiResource( routePrefix: '/api/shop', shortName: 'ProductQuestion', uriTemplate: '/products/{productId}/questions', uriVariables: [ 'productId' => new Link( fromClass: Product::class, fromProperty: 'questions', identifiers: ['id'] ), ], operations: [ new GetCollection( provider: ProductQuestionProvider::class, openapi: new Operation( tags: ['Product Q&A'], summary: 'List approved questions for a product', parameters: [ new \ApiPlatform\OpenApi\Model\Parameter(name: 'status', in: 'query', required: false, schema: ['type' => 'string', 'default' => 'approved']), new \ApiPlatform\OpenApi\Model\Parameter(name: 'page', in: 'query', required: false, schema: ['type' => 'integer', 'default' => 1]), new \ApiPlatform\OpenApi\Model\Parameter(name: 'per_page', in: 'query', required: false, schema: ['type' => 'integer', 'default' => 30, 'maximum' => 50]), new \ApiPlatform\OpenApi\Model\Parameter( name: 'with_answers', in: 'query', required: false, description: 'Embed top approved answers inside each question. Default: `true`.', schema: ['type' => 'boolean', 'default' => true], ), new \ApiPlatform\OpenApi\Model\Parameter( name: 'answers_per_question', in: 'query', required: false, description: 'Max answers embedded per question (1–20). Default: 5.', schema: ['type' => 'integer', 'default' => 5, 'minimum' => 1, 'maximum' => 20], ), new \ApiPlatform\OpenApi\Model\Parameter( name: 'answer_status', in: 'query', required: false, description: 'Status of embedded answers. Default: `approved`.', schema: ['type' => 'string', 'enum' => ['approved', 'pending', 'rejected'], 'default' => 'approved'], ), ], ), ), ], graphQlOperations: [] )] class ProductQuestion extends \Webkul\Product\Models\ProductQuestion { protected $fillable = [ 'product_id', 'customer_id', 'customer_name', 'customer_email', 'question', 'status', 'answers_count', ]; protected $casts = [ 'id' => 'int', 'product_id' => 'int', 'customer_id' => 'int', 'answers_count' => 'int', 'question' => 'string', 'customer_name' => 'string', 'status' => 'string', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; public function __get($key) { if ($this->hasAttribute($key)) { return $this->getAttribute($key); } return parent::__get($key); } public function __isset($key) { if ($this->hasAttribute($key)) { return true; } return parent::__isset($key); } public function __set($key, $value) { $own = ['id', 'product_id', 'customer_id', 'customer_name', 'customer_email', 'question', 'status', 'answers_count', 'created_at', 'updated_at']; if (in_array($key, $own)) { $this->setAttribute($key, $value); } else { parent::__set($key, $value); } } public function getId(): int { return (int) $this->getAttribute('id'); } }