diff --git a/Postman_APIs/Collections/GraphQL-API.postman_collection.json b/Postman_APIs/Collections/GraphQL-API.postman_collection.json index 38e4d5d7..5a40cfd4 100644 --- a/Postman_APIs/Collections/GraphQL-API.postman_collection.json +++ b/Postman_APIs/Collections/GraphQL-API.postman_collection.json @@ -8250,7 +8250,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "query allProducts {\n\tallProducts (\n page: 1,\n first: 15,\n input: [{\n key: \"name\"\n value: \"downloadable\"\n }, {\n #key: \"new\"\n #value: \"1\"\n }, {\n #key: \"featured\"\n #value: \"1\"\n }, {\n #key: \"type\"\n #value: \"simple\"\n }, {\n #key: \"sort\"\n #value: \"name-desc\"\n }, {\n #key: \"category_id\"\n #value: \"4\"\n }, {\n #key: \"category_slug\"\n #value: \"men\"\n }, {\n #key: \"price\"\n #value: \"5,100\"\n }, {\n #key: \"url_key\"\n #value: \"simple-product\"\n }\n ]) { \n paginatorInfo {\n count\n currentPage\n lastPage\n total\n } data {\n id\n sku\n type\n name\n shortDescription\n urlKey\n shareURL\n new\n featured\n status\n guestCheckout\n visibleIndividually\n price\n specialPrice\n specialPriceFrom\n specialPriceTo\n isInWishlist\n isInSale\n isSaleable\n priceHtml {\n id\n type\n minPrice\n priceHtml\n priceWithoutHtml\n regularPrice\n formattedRegularPrice\n finalPrice\n formattedFinalPrice\n currencyCode\n }\n reviews {\n id\n name\n title\n rating\n comment\n }\n averageRating\n percentageRating\n downloadableSamples {\n id\n url\n file\n fileName\n fileUrl\n type\n sortOrder\n productId\n createdAt\n updatedAt\n translations {\n id\n locale\n title\n productDownloadableSampleId\n }\n }\n downloadableLinks {\n id\n title\n price\n url\n file\n fileName\n fileUrl\n type\n sampleUrl\n sampleFile\n sampleFileName\n sampleType\n sortOrder\n productId\n downloads\n translations {\n id\n locale\n title\n productDownloadableLinkId\n }\n }\n }\n }\n}", + "query": "query allProducts {\n\tallProducts (input: {\n page: 1,\n limit: 12,\n # urlKey: \"simple-product\"\n # name: \"downloadable\"\n # categoryId: \"4\"\n # type: \"simple\"\n # price: \"50,100\"\n # new: true\n # featured: true\n # sort: \"name-asc\"\n }) { \n paginatorInfo {\n count\n currentPage\n lastPage\n total\n } data {\n id\n sku\n type\n name\n shortDescription\n urlKey\n shareURL\n new\n featured\n status\n guestCheckout\n visibleIndividually\n price\n specialPrice\n specialPriceFrom\n specialPriceTo\n isInWishlist\n isInSale\n isSaleable\n priceHtml {\n id\n type\n minPrice\n priceHtml\n priceWithoutHtml\n regularPrice\n formattedRegularPrice\n finalPrice\n formattedFinalPrice\n currencyCode\n }\n reviews {\n id\n name\n title\n rating\n comment\n }\n averageRating\n percentageRating\n downloadableSamples {\n id\n url\n file\n fileName\n fileUrl\n type\n sortOrder\n productId\n createdAt\n updatedAt\n translations {\n id\n locale\n title\n productDownloadableSampleId\n }\n }\n downloadableLinks {\n id\n title\n price\n url\n file\n fileName\n fileUrl\n type\n sampleUrl\n sampleFile\n sampleFileName\n sampleType\n sortOrder\n productId\n downloads\n translations {\n id\n locale\n title\n productDownloadableLinkId\n }\n }\n }\n }\n}", "variables": "" } }, diff --git a/src/BagistoGraphql.php b/src/BagistoGraphql.php index 58f6016c..56cbf1fe 100755 --- a/src/BagistoGraphql.php +++ b/src/BagistoGraphql.php @@ -859,4 +859,17 @@ public function getImageMIMEType($filename, $type = 'images') return $mimetype; } + + /** + * To get the paginator info + */ + public function getPaginatorInfo(object $collection): array + { + return [ + 'count' => $collection->count(), + 'current_page' => $collection->currentPage(), + 'last_page' => $collection->lastPage(), + 'total' => $collection->total(), + ]; + } } diff --git a/src/Queries/Shop/Common/HomePageQuery.php b/src/Queries/Shop/Common/HomePageQuery.php index a15467ff..421ee18c 100755 --- a/src/Queries/Shop/Common/HomePageQuery.php +++ b/src/Queries/Shop/Common/HomePageQuery.php @@ -3,15 +3,18 @@ namespace Webkul\GraphQLAPI\Queries\Shop\Common; use Illuminate\Support\Facades\DB; -use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; -use Webkul\Attribute\Repositories\AttributeRepository; -use Webkul\Category\Repositories\CategoryRepository; -use Webkul\Customer\Repositories\CustomerRepository; +use Webkul\Product\Helpers\Toolbar; +use Illuminate\Pagination\Paginator; use Webkul\GraphQLAPI\Queries\BaseFilter; +use Illuminate\Pagination\LengthAwarePaginator; use Webkul\GraphQLAPI\Validators\CustomException; -use Webkul\Marketing\Repositories\SearchSynonymRepository; -use Webkul\Product\Helpers\Toolbar; use Webkul\Product\Repositories\ProductRepository; +use Webkul\Category\Repositories\CategoryRepository; +use Webkul\Customer\Repositories\CustomerRepository; +use Webkul\Attribute\Repositories\AttributeRepository; +use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; +use Webkul\Marketing\Repositories\SearchSynonymRepository; +use Webkul\Product\Repositories\ElasticSearchRepository; use Webkul\Theme\Repositories\ThemeCustomizationRepository; class HomePageQuery extends BaseFilter @@ -29,6 +32,7 @@ class HomePageQuery extends BaseFilter public function __construct( protected AttributeRepository $attributeRepository, protected ProductRepository $productRepository, + protected ElasticSearchRepository $elasticSearchRepository, protected CategoryRepository $categoryRepository, protected CustomerRepository $customerRepository, protected SearchSynonymRepository $searchSynonymRepository, @@ -156,21 +160,26 @@ public function getCategories(mixed $rootValue, array $args) * * @return \Illuminate\Support\Collection */ - public function getAllProducts(object $query, array $input) + public function getAllProducts(mixed $rootValue, array $input) { - $params = [ + $searchEngine = core()->getConfigData('catalog.products.search.engine') === 'elastic' + ? core()->getConfigData('catalog.products.search.storefront_mode') + : 'database'; + + $params = array_merge($input, [ + 'channel_id' => core()->getCurrentChannel()->id, 'status' => 1, 'visible_individually' => 1, - 'channel_id' => core()->getCurrentChannel()->id, - ]; + ]); - $filters = array_filter($input); + $products = $searchEngine === 'elastic' + ? $this->searchFromElastic($params) + : $this->searchFromDatabase($params); - foreach ($filters as $input) { - $params[$input['key']] = $input['value']; - } - - return $this->searchFromDatabase($params); + return [ + 'paginator_info' => bagisto_graphql()->getPaginatorInfo($products), + 'data' => $products, + ]; } /** @@ -194,29 +203,12 @@ public function searchFromDatabase($params) ->where('product_price_indices.customer_group_id', $customerGroup->id); }); - if ( - ! empty($params['category_id']) - || ! empty($params['category_slug']) - ) { - $qb->leftJoin('product_categories', 'product_categories.product_id', '=', 'products.id'); - - /** - * Handle category_id filtering. - */ - if (! empty($params['category_id'])) { - $qb->whereIn('product_categories.category_id', explode(',', $params['category_id'])); - } - - /** - * Handle category_slug filtering. - */ - if (! empty($params['category_slug'])) { - $category = $this->categoryRepository->findBySlug($params['category_slug']); - - if ($category) { - $qb->where('product_categories.category_id', $category->id); - } - } + /** + * Handle category_id filtering. + */ + if (! empty($params['category_id'])) { + $qb->leftJoin('product_categories', 'product_categories.product_id', '=', 'products.id') + ->whereIn('product_categories.category_id', explode(',', $params['category_id'])); } if (! empty($params['channel_id'])) { @@ -369,7 +361,48 @@ public function searchFromDatabase($params) return $qb->inRandomOrder(); } - return $qb->groupBy('products.id'); + $qb = $qb->groupBy('products.id'); + + $limit = $this->getPerPageLimit($params); + + return $qb->paginate($limit, ['*'], 'page', $params['page'] ?? 1); + } + + /** + * Search product from elastic search. + * + * @return \Illuminate\Support\Collection + */ + public function searchFromElastic(array $params = []) + { + $currentPage = $params['page'] ?? 1; + + $limit = $this->getPerPageLimit($params); + + $sortOptions = $this->getSortOptions($params); + + $indices = $this->elasticSearchRepository->search($params, [ + 'from' => ($currentPage * $limit) - $limit, + 'limit' => $limit, + 'sort' => $sortOptions['sort'], + 'order' => $sortOptions['order'], + ]); + + $query = $this->productRepository + ->whereIn('products.id', $indices['ids']) + ->orderBy(DB::raw('FIELD(id, '.implode(',', $indices['ids']).')')); + + $items = $indices['total'] ? $query->get() : []; + + return new LengthAwarePaginator($items, $indices['total'], $limit, $currentPage); + } + + /** + * Fetch per page limit from toolbar helper. Adapter for this repository. + */ + public function getPerPageLimit(array $params): int + { + return product_toolbar()->getLimit($params); } /** @@ -435,4 +468,4 @@ public function getFilterAttributes(mixed $rootValue, array $args, GraphQLContex 'sort_orders' => $availableSortOrders, ]; } -} +} \ No newline at end of file diff --git a/src/graphql/common.graphql b/src/graphql/common.graphql index 178619cb..555e0c2f 100644 --- a/src/graphql/common.graphql +++ b/src/graphql/common.graphql @@ -18,6 +18,13 @@ type StatusResponse { message: String } +type PaginatorInfo { + count: Int + currentPage: Int @rename(attribute: "current_page") + lastPage: Int @rename(attribute: "last_page") + total: Int +} + type FilterOption { key: String value: String diff --git a/src/graphql/shop/common/home_page.graphql b/src/graphql/shop/common/home_page.graphql index 43267774..7f8193bc 100755 --- a/src/graphql/shop/common/home_page.graphql +++ b/src/graphql/shop/common/home_page.graphql @@ -6,31 +6,34 @@ extend type Query { homeCategories( getCategoryTree: Boolean @rename (attribute: "get_category_tree") - input: [FilterHomeCategoriesInput] + input: [FilterOption] ): [Category!] @field(resolver: "Webkul\\GraphQLAPI\\Queries\\Shop\\Common\\HomePageQuery@getCategories") allProducts( - input: [FilterAllProductsInput] - @builder(method: "Webkul\\GraphQLAPI\\Queries\\Shop\\Common\\HomePageQuery@getAllProducts") - ): [Product!] @paginate( - type: "PAGINATOR" - defaultCount: 10 - model: "Webkul\\Product\\Models\\Product" - ) + input: FilterAllProductsInput @spread + ): ProductPaginatorResponse! @field(resolver: "Webkul\\GraphQLAPI\\Queries\\Shop\\Common\\HomePageQuery@getAllProducts") getFilterAttribute( categorySlug: String! @rename (attribute: "category_slug") ): FilterAttribute @field(resolver: "Webkul\\GraphQLAPI\\Queries\\Shop\\Common\\HomePageQuery@getFilterAttributes") } -input FilterHomeCategoriesInput { - key: String - value: String +input FilterAllProductsInput { + page: Int! + limit: Int! + urlKey: String @rename(attribute: "url_key") + name: String + categoryId: ID @rename(attribute: "category_id") + type: String + price: String + new: Boolean + featured: Boolean + sort: String } -input FilterAllProductsInput { - key: String - value: String +type ProductPaginatorResponse { + paginatorInfo: PaginatorInfo! @rename(attribute: "paginator_info") + data: [Product!] @rename(attribute: "data") } type FilterAttribute {