4

This works:

{{ craft.entries.section('products').relatedTo(object).first.uri }}/{slug}

However, in this case, I need to use the extended relatedTo property, which includes curly braces:

{{ craft.entries({relatedTo: {targetElement: object, field: 'productConfigs'}}).first.uri }}/{{ object.slug }}

Because of the way renderObjectTemplate replacement seems to happen, the braces get screwed up.

Any way to work around this?

I'd actually rather do this in PHP/plugin if possible, but I'm not sure if a relevant hook/event exists.

Brad Bell
  • 67,440
  • 6
  • 73
  • 143
Tim Kelty
  • 3,081
  • 1
  • 18
  • 29

3 Answers3

7

It's not documented so I guess not officially supported, but you can use template variables in the URL formats. For example if you had a UrlFormats plugin, you could use something like this:

<?php
namespace Craft;

class UrlFormatsVariable
{
    // Use in the URL Format box:
    // 
    //  {{ craft.urlFormats.yourSectionHandle(object) }}
    public function yourSectionHandle($entry)
    {
        // Craft checks the URL format with an array "object" first, to see if there's a {slug},
        // and so whether it should be checked for uniqueness and incremented if necessary.
        if (!$entry || !($entry instanceof EntryModel))
        {
            return '{slug}';
        }

        $relation = craft()->elements->getCriteria(ElementType::Entry)
            ->relatedTo(array(
                'targetElement' => $entry,
                'field'         => 'productConfigs',
            ))->first();

        if (!$relation)
        {
            // throw an exception or default to something
        }

        return $relation->uri.'/'.$entry->slug;
    }
}

Then you can keep your URL Format and Nested URL Format inputs cleaner:

{{ craft.urlFormats.yourSectionHandle(object) }}
Mike Pepper
  • 4,391
  • 17
  • 26
  • returning '{slug}' didn't work for me. I would end up with an error message like "URI "{slug}" has already been taken."

    Instead, this seems to do the trick: {{ craft.urlFormats.yourSectionHandle(object)|default(object.slug) }}

    – Tim Kelty Aug 09 '16 at 19:52
  • This is great - very handy if you want to get a little more complex with titles. The twig mini templates are tricky/limited, but with this you can do basically anything. Thanks! – Jeremy Daalder Feb 27 '23 at 03:44
3

Using template variables in URL formats is brilliant, but if you don't want to write your own plugin, you could use the fantastic Preparse plugin as well.

First, create a Preparse field that contains the following Twig code, and add the field to your entry type's field layout:

{% spaceless %}
    {{ craft.entries({
        relatedTo: {
            targetElement: entry,
            field: 'productConfigs'
        }
    }).first().uri|default('') }}
{% endspaceless %}

Then, add the following to your URL format setting (preparseFieldHandle should be the handle for your Preparse field, obviously):

{{object.preparseFieldHandle ? object.preparseFieldHandle ~ '/'}}{slug}    

Note that any existing entries in the relevant section would have to be re-saved in order to have the Preparse field actually parse.

Mats Mikkel Rummelhoff
  • 22,361
  • 3
  • 38
  • 69
2

I'm not sure this is the best answer, but since others can post answers too:

One solution to this may be to disable the Section setting Entries in this section have their own URLs and manage your URLs manually using a custom route and the appropriate template logic.

Ben Parizek
  • 13,448
  • 1
  • 33
  • 98
  • By disabling that setting you also disable live preview. – carlcs Aug 05 '16 at 06:54
  • 2
    Good point. It would be possible to have URLs enabled for Live Preview and route things differently on the front-end. Not saying this is ideal. It always feels wrong when you have URLs enabled just for Live Preview or in a situation where the CP and front-end URLs need to be different. – Ben Parizek Aug 05 '16 at 14:53