Skip to content

Instantly share code, notes, and snippets.

@matt-adigital
Created December 10, 2018 11:25
Show Gist options
  • Save matt-adigital/e79b2fd2e14327b3f306a907f4e7e02e to your computer and use it in GitHub Desktop.
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
<?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;
}
}
<?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
];
}
}
{% 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>&nbsp;</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>&nbsp;</div>{% endif %}
</div>
{% endif %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment