Restrict content search to a single Collection

If you read the post Content Search, you know you can restrict perch_content_search() to only search Collcetion items:

$search_items = perch_content_search(perch_get('q'), [
    'apps' => ['PerchContent_RunwaySearch'],
]);

It’s also possible to restrict the search to a single Collection. Well, sort of. You can’t actually do that. There are 2 approaches you can take here:

1. Exclude in the template

If you use perch:showall to inspect the IDs you have access to in this context, you should find result_region_key which in this context represents a Collection key.

So you can use conditional tags to only display result items that belong to a certain Collection:

<perch:if id="result_region_key" match="eq" value="Movies">
    <!--* result item is in the Movies collection *-->
</perch:if>

However, this approach has some disadvantages.

Firstly, if you’re paginating the results, it’s very likely that pages would have different numbers of items. This is because you’re potentially hiding some items per page.

Another pagination-related issue is: what happens if a page doesn’t not include any result items from the Collection you want? The user would need to navigate to the next page which may have the same issue.

A third potential issue: it is not clear whether the search returned no results. If the search returned 25 items but none of them is in the Collection you’re after, there’s no way for you to check for this from within the template.

2. Search then Display

With this approach you also have two options for how you display the results. I’ll refer to these options as (a) and (b).

  1. Perform a Collections-restricted search (results include items from all Collections)
  2. Grab (a) the items or (b) the item IDs of the items that belong to the Collection you want
  3. Display the items with (a) perch_template() or (b) perch_collection()

The first step is to perform the search. Skip the templating and disable pagination so you can inspect the returned items in PHP:

// perform a search
$search_items = perch_content_search(perch_get('q'), [
    'apps' => ['PerchContent_RunwaySearch'],
    'skip-template' => true,
    'count' => null,
]);

Grabbing items from matching Collection

Then inspect the returned items and check the result_region_key value. If it matches the Collection key you want, grab (a) the item or (b) the item’s ID _id depending on how you want to output the items.

(a) Grabbing the items

// loop through search_items and find the ones that are in the desired Collection
$display_items = array();
foreach($search_items as $item) {
    if($item['result_region_key'] === $collection_key) {
        $display_items[] = $item;
    }
}

(b) Grabbing the item IDs

If you want to grab the item ID instead, you’d only change this line:

$display_items[] = $item['_id'];

Displaying the items

Now the array display_items holds the Collection items or their IDs depending on the approach you’re using. If the array is empty, none of the result items belong to the Collection you’re after:

if($display_items) {
    // display the items
} else {
    // display a "not found" message
}

(a) Displaying the items: perch_template()

If you went with option (a) and grabbed the items, you can render the results with perch_template().

If you are unfamiliar with perch_template(), you can refer to the documentation and this blog post.

Perch Content’s search handler returns all your item’s data (including blocks), so you can render the items with the same template you normally use for displaying a list with perch_collection().

So if you normally display a movies list with perch_collection() like so:

perch_collection('Movies', [
    'template' => 'movies/list.html',
]);

You would render the result items with perch_template() like this:

if($display_items) {
    perch_template('content/movies/list.html', $display_items);
}

Note that you can’t paginate or sort the result items with perch_template().

(b) Displaying the items: perch_collection()

Another approach for displaying the items is to use the item IDs _id and filter for them with perch_collection():

if($display_items) {
    perch_collection('Movies', [
        'template' => 'movies/list.html',
        'filter' => '_id',
        'match' => 'in',
        'value' => implode(',', $display_items)
    ]);
}

'value' => implode(',', $display_items) evaluates to something like 'value' => '14,19,82,107'.

While you can’t paginate or sort the items with perch_template(), you can with perch_collection().

Another advantage of this approach is the ability to apply multiple filters and category filters to further refine the results. You may have an advanced search form where the use can search for movies via a combination of input fields such as genre, actors and year:

if($display_items) {
    perch_collection('Movies', [
        'template' => 'movies/list.html',
        'category' => ['genres/action/', 'genres/comedy/'],
        'category-match' => 'all',
        'filter' => [
            [
                'filter' => '_id',
                'match' => 'in',
                'value' => implode(',', $display_items)
            ],
            [
                'filter' => 'actors.slug',
                'match' => 'eq',
                'value' => 'tom-hanks'
            ],
            [
                'filter' => 'release_year',
                'match' => 'gt',
                'value' => 2000
            ]
        ],
        'sort' => 'release_year',
        'sort-order' => 'DESC',
    ]);
}
link

Related articles