2

I have two channels set up, the first creates entries called "databytes" the second is a channel for relating certain databytes to each other called "databyteSeries". In my template I want to output all databytes but if they are related just show the first in the series. I have the following code which is displaying the first in the databyteSeries no problem (sectionId 8):

{% paginate craft.entries.section(['databytes','databyteSeries']).limit(6) as entries %}

{% for entry in entries %}

  {% if entry.sectionId == 8 %}

  {% for series in entry.relatedDatabytes %}
    {% if loop.first %}
      {# TEMPLATE CODE FOR ENTRY HERE #}
    {% endif %}
  {% endfor %}

  {% else %}

    {# THIS NEEDS TO DISPLAY ALL DATABYTES THAT ARE NOT RELATED #}

  {% endif %}

{% endfor %}

{# PAGINATION CODE HERE #}

{% endpaginate %}

It's the second part where I now only want to display the databytes that have NOT been related where I am struggling. I guess it needs some kind of reverse relationship where I have put the {# THIS NEEDS TO DISPLAY ALL DATABYTES THAT ARE NOT RELATED #} but I am not sure.

Any help would be appreciated.

4
  • You make it really hard for anyone willing to help you. Could you please slim down your code and leave out irrelevant parts (pagination links, loops for your assets, html only parts)?
    – carlcs
    Jul 4, 2014 at 9:17
  • 1
    Sorry, does the new code help and make sense?
    – Gareth
    Jul 4, 2014 at 9:28
  • Thanks Gareth. I suppose you probably use an Entries field to make up your relations. Is there a reason that I don't get yet why you don't use the relateTo parameter to query the entries?
    – carlcs
    Jul 4, 2014 at 9:54
  • Hi Christian, yes I have used the Entries field to make the relations and to be honest I am just trying to work my way through a possible solution, it may well be I have missed something really simple and obvious. All I need to do is output all entries from both channels but when an entry is part of a series (which is the databyteSeries channel) just display the first entry in that series and don't display it from the databytes channel.
    – Gareth
    Jul 4, 2014 at 10:05

3 Answers 3

3

Your question is just about entries without relationships but your code example is attempting to display both items with a relationship and without a relationship. I think your example is making things a bit more complicated than this needs to be. Rather than looping through both sections (databytes and databyteSeries), I believe you only need to loop through one section. The databyteSeries section, as you've described it, has a relation field for databytes, so all the info you need to know about databytes should be available to you in the relation field.

Let me know if the following example gets at what you are going for. It simplifies your loop to just the databyteSeries section, and then uses Twig's length filter to test the entry.relatedDatabytes field to see if it has any entries. If there are related databytes, it displays one output, and if there are not, it displays an alternative output.

I've also updated the method in which you were using to output the first item in the series. Rather than loop through the entire entry.relatedDatabytes field and just output the first item using a loop.first conditional, I've updated the code to use entry.relatedDatabytes.first(). Using the first() method on an EntryModel will just grab the first item, and then you can output it as you wish with whatever variable you assign it to. Below, I've assigned the first item to the firstInSeries variable.

{% paginate craft.entries
     .section('databyteSeries')
     .limit(6)
     as entries 
%}

    {# Loop through databyteSeries entries #}
    {% for entry in entries %}

        {% if entry.relatedDatabytes|length %}

            {# Display the first item in a Databyte Series that has a relationship #}
            {% set firstInSeries = entry.relatedDatabytes.first() %}
        {{ firstInSeries.title }}

      {% else %}

        {# Display the Databyte Series name that has no relationships #}
        {{ entry.title }}

      {% endif %}

    {% endfor %}

{% endpaginate %}

UPDATE

Based on your clarification in the comments and your potential solution, here's another method to consider using Craft's without filter:

{# Get all databyteSeries and databytes #}
{% set databyteSeries = craft.entries.section('databyteSeries').limit(null) %}
{% set databytes = craft.entries.section('databytes').limit(null).indexBy('id') %}

{# Determine which databytes already have a relationship to a databyteSeries #}
{% set alreadyUsedDatabytes = craft.entries.section('databytes').relatedTo(databyteSeries).indexBy('id') %}

{# Use the without filter to remove all 'alreadyUsedDatabytes' from the complete 'databytes' result, so you are left with only 'unusedDatabytes'  #}
{% set unusedDatabytes = databytes|without(alreadyUsedDatabytes) %}

{# Output the unusedDatabytes #}
<ul>
{% for entry in unusedDatabytes %}
  <li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
{% endfor %}
</ul>

I have not tested this code so let me know if you run into any issues to troubleshoot.

7
  • Hi Ben, thank you for your answer. I am probably not explaining myself very well so apologies. What I have is a channel called databytes which holds all the entries. The channel databyteSeries is just for selecting certain databytes to display as a "collection". If a databyte has been selected in a databyteSeries then I want to display the first one in that "collection" on the page. What I then need to do is display all other databytes that are not part of a collection. You can see an example here: graph.domaintesting.me/databytes hopefully that makes sense.
    – Gareth
    Jul 5, 2014 at 19:27
  • Do these two queries need to happen in the same page? Or can we break this up into two problems 1) Display the first databyte entry in each databyteSeries, 2) Display all databytes that do not belong to a databyteSeries ? Jul 5, 2014 at 20:24
  • Hi Ben, yes these two queries need to run on the same page I have added my attempt at a solution below. It seems to work in a simple test I have run, just need to add it to the main site template now.
    – Gareth
    Jul 5, 2014 at 20:33
  • Hey Gareth, based on your clarification I've updated my answer above. Please consider the possible solution outlined in the 'UPDATE' section. If that works and is more on target, I can clean up the rest of my answer and get rid of the code around my initial misunderstanding. Jul 5, 2014 at 21:03
  • Hi Ben, the updated code looks like its just what I am after only for some reason its outputting all the databytes. I have tested the alreadyUsedDatabytes variable and that is working but for some reason {% set unusedDatabytes = databytes|without(alreadyUsedDatabytes) %} still outputs all the databytes with non of the alreadyUsedDatabytes removed.
    – Gareth
    Jul 6, 2014 at 6:52
1

I think I have a solution: if I create an array and add the entry id's for the databytes that are being used in the databyteSeries collections, then I can check that the entries are not in the array and display all the remaining entries from the databytes and databyteSeries channels. Here is a simple test that I am working with:

{% set alreadyUsedDatabytes = [] %}

{% set databyteSeries = craft.entries.section('databyteSeries') %}
{% for entry in craft.entries.section('databytes').relatedTo(databyteSeries) %}
  {% set alreadyUsedDatabytes = alreadyUsedDatabytes|merge([entry.id]) %}
{% endfor %}
<ul>
  {% for entry in craft.entries.section(['databytes','databyteSeries']) %}
  {% if entry.id not in alreadyUsedDatabytes %}
  <li>
    <a href="{{ entry.url }}">{{ entry.title }}</a>
  </li>
  {% endif %}
  {% endfor %}
</ul>
1

I think you should consider using a structure for your entries. Try a structure with Max Levels = 2 and two field types: single and series. Organize the structure like this:

  • single
  • single
  • series
    • single
    • single
  • etc.

Then to loop over the entries:

{% for dataByte in craft.entries.section('dataByte').level(1) %}
  {% switch dataByte.type %}
  {% case 'single' %}
      {# TEMPLATE CODE FOR STAND ALONE ENTRY HERE #}
      single: {{dataByte.title}}<br/>
  {% case 'series' %}
    {% set firstInSeries = dataByte.getChildren().first() %}
    {% if firstInSeries %}
      {# TEMPLATE CODE FOR FIRST ENTRY IN SERIES HERE #}
      seriesFirst: {{firstInSeries.title}}<br/>
    {% endif %}
  {% endswitch %}
{% endfor %}
1
  • Thank you Marion, I hadn't looked at this for an option. I have now come up with a solution that is a mixture of my answer and Ben's but I appreciate your help and will definitely consider this in the future.
    – Gareth
    Jul 7, 2014 at 11:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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