| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- <?php
- namespace Webkul\BagistoApi\Models;
- use ApiPlatform\Metadata\ApiResource;
- use ApiPlatform\Metadata\Delete;
- use ApiPlatform\Metadata\Get;
- use ApiPlatform\Metadata\GetCollection;
- use ApiPlatform\Metadata\Link;
- use ApiPlatform\Metadata\Patch;
- use ApiPlatform\Metadata\Post;
- use ApiPlatform\Metadata\GraphQl\DeleteMutation;
- use ApiPlatform\Metadata\GraphQl\Mutation;
- use ApiPlatform\Metadata\GraphQl\Query;
- use ApiPlatform\Metadata\GraphQl\QueryCollection;
- use ApiPlatform\OpenApi\Model\Operation;
- use Webkul\BagistoApi\Dto\CreateProductQuestionInput;
- use Webkul\BagistoApi\Resolver\BaseQueryItemResolver;
- use Webkul\BagistoApi\State\ProductQuestionProvider;
- use Webkul\BagistoApi\State\ProductQuestionProcessor;
- /**
- * Product Q&A — Questions resource
- *
- * Storefront: list approved questions, post new questions.
- * Admin: update status (approve/reject), delete.
- */
- #[ApiResource(
- routePrefix: '/api/shop',
- shortName: 'ProductQuestion',
- uriTemplate: '/questions',
- operations: [
- new GetCollection(
- uriTemplate: '/questions',
- provider: ProductQuestionProvider::class,
- openapi: new Operation(
- tags: ['Product Q&A'],
- summary: 'List product questions',
- description: 'Returns product questions. Defaults to `approved` status. Supports `product_id`, `status`, `page`, `per_page` filters.',
- parameters: [
- new \ApiPlatform\OpenApi\Model\Parameter(name: 'product_id', in: 'query', required: false, schema: ['type' => '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');
- }
- }
|