Single item URL limitations and workarounds

Configuring the single item URL for Perch Collections and multiple-item Content Regions is essential for providing a good editing experience for the CMS editors as it provides them with an easy way to access the item’s page with a single click without having to manually navigate through the site to find it. This is because it is used for generating the URL for “View Page” button on the control panel.

This URL is also used in the search results if you use the search functions. If you implement a content search on your site, it is important to configure the single item URL so Perch knows what URL to use for the matching items.

Configuring the URL

The URL you enter can include dynamic content from your item. To add a field value to the URL include its field ID inside curly brackets {}.

Let’s say we have a Team collection with the following template:

<perch:content id="name" type="text" label="Name" required title>
<perch:content id="slug" for="name">
<perch:categories id="department" label="Department" set="departments" max="1" required />

A common URL for a single item for such a Collection would be /team/name-slug-here. To set this URL, you need to go to the the Collection’s options tab and enter the following value to the “URL for single items” field :

/team/{slug}

One limitation with the single item URL is you cannot use the value of a category field or a related field. So we cannot use a category’s slug as part of the URL for instance:

/team/department-slug/name-slug-here

This actually makes sense as these fields accept multiple values (e.g. adding multiple categories to the same field). Even though it is true that both the category and related fields allow you to restrict the selection to a maximum of one item with the max attribute, they link to an item (e.g. a category) that has multiple values (e.g. catSlug, catTitle). Hence you cannot simply use the field ID {department}.

Formatting field values

Another way you may want to configure the item URLs is to include parts of a date:

/events/2019/02/some-event

Unfortunately you cannot format field values here. So if you have a date field, it is not possible to to format it as {year}/{month} in the single item URL setting.

Workaround: Runway API endpoints

Perch Runway has a headless mode. The headless requests are sent to /api which routes to the files in the perch/templates/api directory.

Although the main purpose for /api is for serving headless views, it is an ideal place for creating endpoints for other purposes such as webhooks. We can also make use of these endpoints to work around the limitations of the single item URL.

Let’s continue with the Team collection example. The template includes a category field for adding categories from a category set called Departments. We want the URL of a Team item to be /team/{department-slug}/{name-slug}. Here’s the Team Collection template for reference:

<perch:content id="name" type="text" label="Name" required title>
<perch:content id="slug" for="name">
<perch:categories id="department" label="Department" set="departments" max="1" required />

For the “URL for single items” field, add a URL that points to an API endpoint:

/api/team?id={_id}

Note that the above URL includes a query parameter ?id=. The {_id} represents the Collection item ID. It is not a field you can edit, but rather a value that Perch automatically assigns to the item. We can use the _id option to retrieve an item with perch_collection():

perch_collection('Team', [
    '_id' => 24,
]);

We can retrieve the item dynamically by using perch_get() to get the value of the URL query parameter ?id=:

perch_collection('Team', [
    '_id' => perch_get('id'),
]);

We need a way to generate the item’s URL. An easy way is to create a Perch template perch/templates/content/team/item_url.html with nothing else but the URL. Don’t include whitespace of any kind before or after the URL.

/team/<perch:categories id="department" set="departments"><perch:category id="catSlug"></perch:categories>/<perch:content id="slug">

Create the file for our team endpoint perch/templates/api/team.php:

perch_collection('Team', [
    '_id' => perch_get('id'),
    'template' => 'team/item_url.html',
]);

The above outputs the URL which is helpful to confirm we’re on the right track. However, we want to use the URL for redirection. Setting the third argument of perch_collection() to true returns the value instead of outputting it:

$item_url = perch_collection('Team', [
    '_id' => perch_get('id'),
    'template' => 'team/item_url.html',
], true);

We can use $item_url for the redirection:

PerchUtil::redirect($item_url);

We should perform some checks and add a fallback:

// check we have ?id=
if(perch_get('id')) {

    // get the rendered item URL
    $item_url = perch_collection('Team', [
        '_id' => perch_get('id'),
        'template' => 'team/item_url.html',
    ], true);

    // Found a matching item? Redirect to it
    if($item_url) {
        PerchUtil::redirect($item_url);
    }
}

// Redirect to the home page as a fallback
PerchUtil::redirect('/');

Search results URL

If you are using perch_content_search() to perform a content search, Perch uses the URL you configured for the single items as the result URL for matching items. So in our example here Perch would use /api/team?id={_id}.

If you don’t want to expose this URL to users and prefer to use the actual item’s URL right away, you can check for the search result source and include the content/team/item_url.html' we used above:

<perch:if id="result_region_key" match="eq" value="Team">
    <!--* result item is in the Team collection *-->
    <a href="<perch:template path="content/team/item_url.html" rescope="parent">">
        Read more
    </a>

<perch:else>
    <!--* all other search results *-->
    <a href="<perch:search id="result_url">">
        Read more
    </a>

</perch:if>

Note the use of rescope="parent". It allows to include a sub-template with the perch:content namespace when we are in a template that uses a different namespace (in this case perch:search).

link

Related articles