10

I'm setting up my first Craft project and I have a lot of different content. I have around 6 different Channels (News, Events, Blog, Pages, Products, Staff) and ~10 Singles.

I have this idea of using Structures as my sorting and "Navigation" "content types" only. Meaning I'll for example have one Structure called "Navigation" in which I have the default "Title" field and one "Entries" field (for linking to other entries in channels and singles). This I'll output in my template and fetching each linked entries page.link to build my main site navigation.

This should give my client the ability to control the main navigation structure of the entire site 100% with sorting, linking and naming each navigation item ("Title").

Is this a good approach on using a Structure this way? Or will the extra field "Entries" give me a massive query count when the Structure contains (let's say) 100 entries?

  • Just made an edit to your question title, naboovalley. You don't want the channels' entries in you nav, right? – carlcs Jul 28 '14 at 20:02
  • Depends on what you mean :) I want my client to create a navigation tree (Structure..) with the ability to link to individual entries across multiple channels and singles. – naboovalley Jul 29 '14 at 9:07
  • Made another edit ;) – carlcs Jul 29 '14 at 10:23
  • Naboo, we should keep answers an questions separate with Stack Exchange. If you don't mind, I'd update my answer with the {% nav %} part worked in (even if the "run the relations query in the {% nav %} loop" idea was yours)! – carlcs Jul 29 '14 at 18:30
  • @carlcs perfect! :) – naboovalley Jul 29 '14 at 19:10
12

Structure for "sorting-only"

I haven't tried the approach that you are thinking of yet, but your concerns regarding the query count are not that unfounded. You would need to use the relatedTo() parameter with the ElementCriteriaModel you create on your pages (to get the single's content) on your navigation include (even if you only want to get the entries URL) and relations can have a rather big performance hit. Hopefully this is not true for a simple relation like this; otherwise, you'd still have the cache tag. But I'd suggest you to cache the whole include (this is a perfect example for caching being really useful). Additionally setting the limit to '1' will probably help to keep the query count low if the cache is cleared.

{% cache for 3 years %}

    {% set pages = craft.entries.section('pagesSectionHandle').level(1) %}

    {% for page in pages %}

        {% set params = {
            relatedTo: { sourceElement: page, field: "entriesFieldHandle" },
            limit: '1'
        } %}

        {% set linkedEntry = craft.entries(params).first() %}

        {# Get the title from the structure (why set this twice?) #}
        <a href="{{ linkedEntry.url }}">{{ page.title }}</a>

        {# Get the title from the entry #}
        <a href="{{ linkedEntry.url }}">{{ linkedEntry.title }}</a>

    {% endfor %}

{% endcache %}

Structure containing "all the content"

What I'd suggest you to do is to not only use the structure as a "sorting only" structure, but to also add content directly to it.

Use entry types for your singles, if you need to load individual templates for them, and further entry types for your channel index pages. If it's thought-out carefully (modular site design) you probably won't end up with lots of entry types.

Combining those two

If you have a need to link to specific channel entries from your navigation, you could make an entry type in your structure specifically for those links to channel entries. All other content could still be added directly to the structure itself as described above.

I think that being able to edit the content directly in the pages structure is a very convenient and less geeky like experience for the client.

Edit:

This is what Naboovalley and I came up with in the chat (we should have done this before spamming the comments section). It "will give you a fully dynamic menu for your client linking between Singles, Channels and even other Structure entries":

{% cache globally for 3 years %}
{% set entries = craft.entries.section('navigation') %}

<ul>
    {% nav entry in entries %}
        <li>

            {# Check for entry type / get related entry #}
            {% if entry.type == 'linkedChannelEntry' %}
                {% set linkedEntry = entry.entriesField.limit(1).first() %}
            {% endif %}

            {% if linkedEntry %}

                {# Link to a linked channel entry #}
                <a href="{{ linkedEntry.url }}">{{ linkedEntry.title }}</a>

            {% else %}

                {# Link to a structure entry #}
                <a href="{{ entry.url }}">{{ entry.title }}</a>

            {% endif %}

            {# Repeat this for child entries #}
            {% ifchildren %}
                <ul>{% children %}</ul>
            {% endifchildren %}

        </li>
    {% endnav %}
</ul>
{% endcache %}
  • I would only be interested in the page.link as the relation, no content. It's basically using a Structure to make a navigation tree of slugs to all different sections. Let's say my client want to have a navigation where he/she links to 3 singles and 10 entries from different channels. – naboovalley Jul 29 '14 at 9:06
  • @naboovalley ok. I changed my answer a bit and added example code to better see if I got you right. – carlcs Jul 29 '14 at 9:50
  • Yeah that's basically what I was looking for, but with the exception of not using the "level(1)" since I want the client to be able to build a <ul><li></li></ul> with as many depths as it might like. I tried your code and as you'd expected it's not really optimal since it adds queries for each related linked field. You could say I'm looking for the same functionality as "Taxonomy" for EE - "one navigation to rule them all". I'm guessing a Structure isn't the best way to achieve this. – naboovalley Jul 29 '14 at 16:42
  • New idea! How about I build a Plugin that let's me add a URL/slug link to another entry and each time it's fetched fetches the entire current Structure links and cache them during page load to only make it use 1 query for each Structure loop. Is there any easy way for me to build the <ul><li></li></ul> list without using the {% nav %} tag for a Structure? – naboovalley Jul 29 '14 at 16:46
  • I wouldn't overcomplicate this, @naboovalley. As I wrote, I'd definitely add all the content (all what you'd normally put in singles) in the structure itself, then I'd make a _navigation.html template as an include and apply the cache tag to the whole navigation loop. Try it without caching (with the params I pointed out), maybe it is not that query heavy as you think. But if it is, this will only happen once (because of the 3yrs caching time) after you did an edit to the navigation! – carlcs Jul 29 '14 at 17:02
5

If you want to be able to add a class="active" to the selected URL you can use Twig's replace filter. Since the code will be cached you can't do this at runtime.

{% set navigation %}
{% cache globally for 3 years %}
{% set entries = craft.entries.section('navigation') %}

<ul>
{% nav entry in entries %}
    <li>

        {# Check for entry type / get related entry #}
        {% if entry.type == 'linkedChannelEntry' %}
            {% set linkedEntry = entry.entriesField.limit(1).first() %}
        {% endif %}

        {% if linkedEntry %}

            {# Link to a linked channel entry #}
            <a href="{{ linkedEntry.url }}" data-url="{{ linkedEntry.url }}">{{ linkedEntry.title }}</a>

        {% else %}

            {# Link to a structure entry #}
            <a href="{{ entry.url }}" data-url="{{ entry.url }}">{{ entry.title }}</a>

        {% endif %}

        {# Repeat this for child entries #}
        {% ifchildren %}
            <ul>{% children %}</ul>
        {% endifchildren %}

    </li>
{% endnav %}
</ul>
{% endcache %}
{% endset %}

{{ navigation|replace({('data-url="' ~ craft.request.getUrl() ~ '"'): 'class="active"'})|raw }}
  • 1
    Really clever idea! You could also do a separate Q/A only for this particular problem (simplified or better: completely generalized), naboo. It's probably somewhat hidden in here and could come handy in many situations where caching is required, not only for adding a class to a navigation. Good work! – carlcs Jul 30 '14 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.