Created
December 10, 2018 11:25
-
-
Save matt-adigital/e79b2fd2e14327b3f306a907f4e7e02e to your computer and use it in GitHub Desktop.
Craftcms - Search both products and entries in a single search with custom pagination
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Product & Entry Search plugin for Craft CMS 3.x | |
* | |
* Use site search across both products and entries allowing for pagination. | |
* | |
* @link https://adigital.agency | |
* @copyright Copyright (c) 2018 A Digital | |
*/ | |
namespace adigital\productentrysearch\services; | |
use adigital\productentrysearch\ProductEntrySearch; | |
use Craft; | |
use craft\base\Component; | |
/** | |
* ProductEntrySearchService Service | |
* | |
* All of your plugin’s business logic should go in services, including saving data, | |
* retrieving data, etc. They provide APIs that your controllers, template variables, | |
* and other plugins can interact with. | |
* | |
* https://craftcms.com/docs/plugins/services | |
* | |
* @author A Digital | |
* @package ProductEntrySearch | |
* @since 1.0.0 | |
*/ | |
class ProductEntrySearchService extends Component | |
{ | |
// Public Methods | |
// ========================================================================= | |
/** | |
* This function can literally be anything you want, and you can have as many service | |
* functions as you want | |
* | |
* From any other plugin file, call it like this: | |
* | |
* ProductEntrySearch::$plugin->productEntrySearchService->getSearchResults() | |
* | |
* @return mixed | |
*/ | |
public function getSearchResults($query, $orderByScore, $lower, $upper) | |
{ | |
$count = 0; | |
$elements = []; | |
$entries = Craft::$app->search->filterElementIdsByQuery([], $query, $orderByScore, null, true); | |
foreach($entries as $id => $score) { | |
$element = Craft::$app->elements->getElementById($id); | |
if ($element && $element->uri <> '' && $element->enabled == 1) { | |
$count++; | |
if ($lower < $count && $count <= $upper) { | |
$elements[] = $element; | |
} | |
} | |
} | |
return ["elements" => $elements, "count" => $count]; | |
} | |
/** | |
* This function can literally be anything you want, and you can have as many service | |
* functions as you want | |
* | |
* From any other plugin file, call it like this: | |
* | |
* ProductEntrySearch::$plugin->productEntrySearchService->getPagination() | |
* | |
* @return mixed | |
*/ | |
public function getPagination($pageNum, $last, $lower, $upper, $url, $urlParts, $limit, $count) | |
{ | |
$pagination = []; | |
$pagination['currentPage'] = $pageNum; | |
$prevLink = ''; | |
$nextLink = ''; | |
if (preg_match('/^p\d+$/', $last)) { | |
// we have pagination already | |
// get number from $last | |
$prevNum = $pageNum - 1; | |
$nextNum = $pageNum + 1; | |
array_pop($urlParts); | |
$url = implode("/", $urlParts); | |
if ($pageNum == 2) { | |
$prevLink = $url; | |
} else { | |
$prevLink = $url.'/p'.$prevNum; | |
} | |
$nextLink = $url.'/p'.$nextNum; | |
} else { | |
$nextLink = $url.'/p2'; | |
$nextNum = 2; | |
} | |
if ($limit != $upper) { | |
// show prev page link | |
$pagination['prevUrl'] = $prevLink; | |
$pagination['prevPage'] = $prevNum; | |
$pagination['firstPage'] = $url; | |
} | |
if ($upper < $count) { | |
// show next page link | |
$pagination['nextUrl'] = $nextLink; | |
$pagination['nextPage'] = $nextNum; | |
$pagination['lastPage'] = $url.'/p'.ceil($count / $limit); | |
} | |
return $pagination; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Product & Entry Search plugin for Craft CMS 3.x | |
* | |
* Use site search across both products and entries allowing for pagination. | |
* | |
* @link https://adigital.agency | |
* @copyright Copyright (c) 2018 A Digital | |
*/ | |
namespace adigital\productentrysearch\variables; | |
use adigital\productentrysearch\ProductEntrySearch; | |
use Craft; | |
use craft\helpers\UrlHelper; | |
/** | |
* Product & Entry Search Variable | |
* | |
* Craft allows plugins to provide their own template variables, accessible from | |
* the {{ craft }} global variable (e.g. {{ craft.productEntrySearch }}). | |
* | |
* https://craftcms.com/docs/plugins/variables | |
* | |
* @author A Digital | |
* @package ProductEntrySearch | |
* @since 1.0.0 | |
*/ | |
class ProductEntrySearchVariable | |
{ | |
// Public Methods | |
// ========================================================================= | |
/** | |
* Whatever you want to output to a Twig template can go into a Variable method. | |
* You can have as many variable functions as you want. From any Twig template, | |
* call it like this: | |
* | |
* {{ craft.productEntrySearch.exampleVariable }} | |
* | |
* Or, if your variable requires parameters from Twig: | |
* | |
* {{ craft.productEntrySearch.exampleVariable(twigValue) }} | |
* | |
* @param null $params | |
* @return string | |
*/ | |
public function search($params) | |
{ | |
$url = Craft::$app->request->url; | |
$url = UrlHelper::stripQueryString($url); | |
$urlParts = explode('/', $url); | |
$last = end($urlParts); | |
if (preg_match('/^p\d+$/', $last)) { | |
$pageNum = (int)str_replace('p', '', $last); | |
} else { | |
$pageNum = 1; | |
} | |
$lower = $params['limit'] * ($pageNum - 1); | |
$upper = $lower + $params['limit']; | |
$results = ProductEntrySearch::$plugin->productEntrySearchService->getSearchResults($params['query'], $params['orderByScore'], $lower, $upper); | |
$pagination = ProductEntrySearch::$plugin->productEntrySearchService->getPagination($pageNum, $last, $lower, $upper, $url, $urlParts, $params['limit'], $results['count']); | |
if ($upper >= $results['count']) { | |
$upper = $results['count']; | |
} | |
return [ | |
'lower' => $lower, | |
'upper' => $upper, | |
'total' => $results['count'], | |
'data' => $results['elements'], | |
'pagination' => $pagination | |
]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% set query = craft.app.request.getParam('q') %} | |
{% set result = craft.productEntrySearch.search({ | |
query: '*'~query~'*', | |
orderByScore: true, | |
limit: 20 | |
}) %} | |
<h1>Search Results for '{{ query }}'</h1> | |
<p>Total results: <b>{{ result.total }}</b></p> | |
{% if result.data|length %} | |
<p>Showing results: {{ result.lower + 1 }} - {{ result.upper }}</p> | |
{% for block in result.data %} | |
<div> | |
<h3><a href="{{ block.url }}" title="{{ block.title }}">{{ block.title }}</a></h3> | |
{% if block.excerpt|length %} | |
<p>{{ block.excerpt }}</p> | |
{% endif %} | |
</div> | |
{% else %} | |
<p>Sorry, your search term <b>{{ query }}</b> didn't return any results. Use the search box to try a different search term.</p> | |
{% endfor %} | |
<div> | |
{% if result.pagination.firstPage is defined %}<a href="{{ result.pagination.firstPage }}?{{ craft.request.getQueryStringWithoutPath() }}">Newer</a>{% else %}<div> </div>{% endif %} | |
<ul> | |
{% if result.pagination.prevUrl is defined %} | |
<li><a href="{{ result.pagination.prevUrl }}?{{ craft.request.getQueryStringWithoutPath() }}">{{ result.pagination.prevPage }}</a></li> | |
{% endif %} | |
<li>{{ result.pagination.currentPage }}</li> | |
{% if result.pagination.nextUrl is defined %} | |
<li><a href="{{ result.pagination.nextUrl }}?{{ craft.request.getQueryStringWithoutPath() }}">{{ result.pagination.nextPage }}</a></li> | |
{% endif %} | |
</ul> | |
{% if result.pagination.lastPage is defined %}<a href="{{ result.pagination.lastPage }}?{{ craft.request.getQueryStringWithoutPath() }}">Older</a>{% else %}<div> </div>{% endif %} | |
</div> | |
{% endif %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment