How to write custom search using GraphQl in magento 2? – Today’s article is about how to create a custom GraphQl schema for custom search page.
Which includes Search,Filter and Pagination. In This article i am going to start with the schema.graphqls. To explain How to Pass the search string, pass the filtering inputs, Page size and current-page for pagination.
Below is the basic idea of the page i am going to cover by today’s article.

I am not going to consider creating custom module explanation here. Hope that is pretty familiar with every one. Who are searching for this type of custom graphql related stuffs. As usual we need to create a schema.graphqls, Resolver file and Data provider file.
Lets see how our schema.graphqls files looks like. This is something different rather than our previous custom modules like below.
- How to create GraphQl schema for Magento 2 custom module with custom table?
- How to create a GraphQl schema by passing argument for custom magento 2 module with custom table ?
- How to get product attribute value, label using GraphQl in Magento 2 ?
Schema.graphqls file ( app/code/Ayakil/News/etc/schema.graphqls )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
type Query { news( search: String @doc(description: "Performs a full-text search using the specified key words."), filter: NewsFilterInput @doc(description: "Identifies which colomns to search for and return."), pageSize: Int = 6 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1.") ): NewsData @resolver(class: "Ayakil\\News\\Model\\Resolver\\News") @doc(description: "The news query returns the news according to passed arguments(search text, filter)") } input NewsFilterInput @doc(description: "NewsFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { archive: FilterTypeInput @doc(description: "The News Monthly Archive to filter") news_type:FilterTypeInput @doc(description: "The News Category to filter.") tags:FilterTypeInput @doc(description: "The News Category to filter.") } type NewsData @doc(description: "The News Data is the top-level object returned in a News search") { total_count: Int @doc(description: "Total count") total_pages: Int @doc(description: "Total count") newsList: [News] } type News @doc(description: "The News object is the top-level object returned in a News search") { id: Int @doc(description: "ID of the news") news_type:String @doc(description: "News Type of the news") news_title: String @doc(description: "News Title of the news") news_date: String @doc(description: "News Date of the news") description: String @doc(description: "Description of the news") short_description: String @doc(description: "Short Description of the news") image: String @doc(description: "Image of the news") tags: String @doc(description: "Tags of the news") } |
I will explain one by one in this query news. Search is the direct input field which pass the search string to resolver. Filter is the input type, which is passing the FilterTypeInput to the resolver. So wants to define input NewsFilterInput after the query. pageSize and currentPage are common used for pagination.
Our resolver returning the NewsData. Which contain the total_count, total_pages and newsList array.
As we mentioned in our query. Resolver file is located at (app/code/Ayakil/News/Model/Resolver/News.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<?php namespace Ayakil\News\Model\Resolver; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; class News implements ResolverInterface { private $newsDataProvider; private $logger; /** * @param DataProvider\News $NewsRepository */ public function __construct( \Ayakil\News\Model\Resolver\DataProvider\News $newsDataProvider, \Psr\Log\LoggerInterface $logger ) { $this->newsDataProvider = $newsDataProvider; $this->logger = $logger; } /** * @inheritdoc */ public function resolve( Field $field, $context, ResolveInfo $info, array $value = null, array $args = null ) { $search_text = $args['search']; $filter_news_type = $args['filter']['news_type']['eq']; $filter_archive = $args['filter']['archive']['eq']; $filter_tags = $args['filter']['tags']['eq']; $pageSize = $args['pageSize']; $currentPage = $args['currentPage']; $newsData = $this->newsDataProvider->getNews( $search_text, $filter_news_type , $filter_archive, $filter_tags , $pageSize , $currentPage ); return $newsData; } } |
In the resolver file we can capture the data passed from the query and post it to the data provider file and return the result.
Data provider file is located in ( app/code/Ayakil/News/Model/Resolver/DataProvider/News.php )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
<?php namespace Ayakil\News\Model\Resolver\DataProvider; class News { protected $_newsFactory; private $logger; public function __construct( \Ayakil\News\Model\NewsFactory $newsFactory, \Psr\Log\LoggerInterface $logger ) { $this->_newsFactory = $newsFactory; $this->logger = $logger; } /** * getNews * @param $search_text, $filter_news_type ,$filter_archive, $filter_tags, $pageSize, $currentPage * @return $news_data */ public function getNews($search_text, $filter_news_type ,$filter_archive, $filter_tags, $pageSize, $currentPage){ try { $newscollection = $this->_newsFactory->create(); $collection = $newscollection->getCollection(); $currentPage = ($currentPage)? $currentPage : 1; //default page 1 //get values of current limit $pageSize =($pageSize)? $pageSize : 6; //default number of news per page is 6 if($search_text!=""){ $collection->addFieldToFilter( array( 'news_title',//attribute_1 with key 0 'description',//attribute_2 with key 1 ), array( array('like' => '%'.$search_text.'%'),//condition for attribute_1 with key 0 array('like' => '%'.$search_text.'%'),//condition for attribute_2 ) ); } if($filter_news_type !==""){ $collection->addFieldToFilter('news_type',$filter_news_type); } if($filter_archive !==""){ $collection->addFieldToFilter('archive',$filter_archive); } if($filter_tags !==""){ $collection->addFieldToFilter('tags',array('like' => '%'.$filter_tags.'%')); } //Common $collection->addFieldToFilter('news_date', array('lteq' => date('Y-m-d'))); // this line to avoid to show future news $collection->addFieldToFilter('news_active', 1); $collection->setOrder('sort_order', 'ASC'); $count = $collection->getSize(); $total_pages = ceil($count / $pageSize); $collection->setPageSize($pageSize)->setCurPage($currentPage); $news_data = []; foreach($collection as $news){ $news_id = $news->getId(); $news_data[$news_id]['id'] = $news->getId(); $news_data[$news_id]['news_type'] = $news->getNewsType(); $news_data[$news_id]['news_title'] = $news->getNewsTitle(); $news_data[$news_id]['news_date'] = $news->getNewsDate(); $news_data[$news_id]['description'] = $news->getDescription(); $news_data[$news_id]['short_description'] = $news->getShortDescription(); $news_data[$news_id]['image'] = $news->getImage(); $news_data[$news_id]['tags'] = $news->getTags(); } } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); } //return $news_data; return [ 'total_count'=>$count, 'total_pages'=>$total_pages, 'newsList'=>$news_data]; }//End of function getNews() } |
Hope the data provider file contents are clear. Want to add the values to $news_data array by iterating through records and return the final data array like
1 2 |
return [ 'total_count'=>$count, 'total_pages'=>$total_pages, 'newsList'=>$news_data]; |
GraphQl query to get the records. You may read What is GraphQl in Magento 2 and how to access it? for more clarification on GraphQl magento 2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
query news{ news( search: "" filter: { news_type: {eq: ""} archive: {eq: ""} tags: {eq: ""} } pageSize: 3 currentPage: 1 ){ total_count total_pages newsList{ id news_type news_title news_date short_description description image tags } } } |
Below is the sample results with the above query. In this query we are passing empty arguments along with page size 3. So the total record is 16 and it have 6 pages returned.

If we want to pass the argument for filter type, We just need to pass the argument like below. I am just passing the argument ( tourist ) for tags in filter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
query news{ news( search: "" filter: { news_type: {eq: ""} archive: {eq: ""} tags: {eq: "tourist"} } pageSize: 3 currentPage: 1 ){ total_count total_pages newsList{ id news_type news_title news_date short_description description image tags } } } |
So the result looks like below.

We can pass the search text , We want to pass like below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
query news{ news( search: "colombo" filter: { news_type: {eq: ""} archive: {eq: ""} tags: {eq: ""} } pageSize: 3 currentPage: 1 ){ total_count total_pages newsList{ id news_type news_title news_date short_description description image tags } } } |
So the result is looks like below.

So this is going to be the end of the lengthy article of how to create custom GraphQl search query. I explained step by steps from the starting of creating the GraphQl schema to get the results. Along with passing the argument for the queries and get the results.
Hope this may be help some one. All these steps are checked in my projects. A happy stuff was we launched our PWA first project. Which is totally handled by magento 2( for back-end) and React ( for front-end).
That’s it for today for the article of how to write magento custom search using graphQl in magento 2. Share your thoughts and stay touch with me by comments. Tell me some thing that this article was really helpful, it may motivate me to write more with some fantastic articles in future.
That’s it. Have a nice day.Enjoy coding , Learn , Experience , Teach and Help.
Hi, is there a way to add filter in category query ? like this:
query category {
category(id: 563) {
name
url_key
id
children {
id
name
url_key
products( filter: {need: { in: [194]}}) {
items {
id
name
need
}
}
}
}
}
Let me explain better, I have already made a plugin that enables filters on the product in this way. But they don’t work on multiselect attributes with filter type in as above. they only work with like eq, etc. but not with in. I need to use in.
Thanks in advance.
Thanks for reading. Can you please Check my this article. You may find some tips there.
This class have a problem with foreach run an exception and broken code:
Invalid argument supplied to ‘foreach’. Expected types: ‘array’ or ‘object’, ‘string’ provided.
Nice post but i think use of api repositories & interfaces are much better & fastest than accessing modal directly.What you think?
Thanks for reading. Agreed.