3

I have 2 sections, events and activities, that share many fields. But activities doesn't have an eventLevel field as there isn't a level. eventLevel is a radio fieldtype. So I want to insert the eventLevel when it exists and if it doesn't just insert the text "Activity". I've tried various tests on if eventLevel is defined, is null, and length. If there is an eventLevel it displays, but if it's an activity I end up with an empty cell.

{% set events = craft.entries.find({section:'events,activities', eventDate : ">= " ~ now.w3cDate() , order:'eventDate'}) %}
{% if events %}
    {% for entry in events %}
    <tr>
        <td class="col-date">{{ entry.eventDate.format('D j M') }}</td>
        <td><a href="{{ entry.url }}" title="More info about {{ entry.eventName }}">{{ entry.eventName }}</a></td>
        <td>{{ entry.nearestTown }}</td>
        <td>{% if entry.eventLevel is defined %}{{ entry.eventLevel }}{% else %}Activity{% endif %}</td>
    </tr>
    {% endfor %}
{% endif %}

I've also tried the twig default filter with the same result:

{{ entry.eventLevel|default('Activity') }}
Paul Frost
  • 1,060
  • 10
  • 23

3 Answers3

4

To test if a field exists, you'll have to use brackets notation, i.e.

{% if entry['eventLevel'] is defined %}
   ...
{% endif %}

When the field exists, but there isn't a selected option, the Radio FieldType returns an empty string, so you also need to test for entry.eventLevel != ''.

Putting it together, I believe a ternary conditional would perhaps be the most succinct solution in your particular case:

<td>{{ entry['eventLevel'] is defined and entry.eventLevel != '' ? entry.eventLevel : 'Activity' }}</td>

What the above does is to first check if the entry has a property 'eventLevel' – the brackets notation is needed here, to avoid Twig throwing an exception if the entry variable doesn't have the property – before checking if the attribute's value is not an empty string. If both of those statements evaluate to true, then the attribute's value is printed; if not, the string Activity outputs.

This is a bit beside the point, but if you're wondering about the syntax here, the so called ternary operator (e.g. {{ variable ? 'yes' : 'no' }}) is just a kind of a short-hand way to write conditionals. Ternary operators are supported in many programming languages (including JavaScript and PHP).

If you wanted to, you could write out the full conditional and it would work exactly the same:

<td>
    {% if entry['eventLevel'] is defined and entry.eventLevel != '' %}
       {{ entry.eventLevel }}
    {% else %}
       Activity
    {% endif %}
</td>

I'd argue that a ternary operator is more readable, shorter and also makes it easier to deal with potential whitespace issues etc. when inlined in markup like this, though.

Mats Mikkel Rummelhoff
  • 22,361
  • 3
  • 38
  • 69
  • That didn't work, still blank if no eventLevel. – Paul Frost Feb 19 '16 at 12:13
  • @PaulFrost Right – I assumed the Radio FieldType returned a falsey value for fields without a selected value, but it actually returns an empty string. I edited my answer; should work now – the correct test is entry[eventLevel] is defined and entry.eventLevel != '' – Mats Mikkel Rummelhoff Feb 19 '16 at 12:27
  • The if entry.eventLevel is defined test won't work in this case, because it is truthy even if the field isn't attached to this entry type's field layout but to any other entry type. A simple if entry.eventLevel test should work though. – carlcs Feb 19 '16 at 13:03
  • Your revised example works. But I'm confused by the structure of the query, I haven't seen the the ? and colon used in examples before, where is that documented?. I had tried {% if entry.eventLevel %}{{ entry.eventLevel }}{% else %}Activity{% endif %} in the first place and when that didn't work I tried to be more specific. So both James and Mats solutions work but neither seem very intuitive. – Paul Frost Feb 19 '16 at 13:53
  • I'm really struggling with the conversion from using ExpressionEngine to Craft, some simple things seem to require very complex, unintuitive code and with so many ways to do the same thing finding examples is hard. – Paul Frost Feb 19 '16 at 13:58
  • @PaulFrost No worries – I amended my answer to include some more details for both the conditional itself, and so-called ternary operators in general. Hope it helps. As for EE vs. Craft, I've worked with both and I'd argue that Twig is way easier to deal with than EE's parser. However, the two are very different and of course there'll be a learning curve. I recommend checking out Straight Up Craft's introductory Twig tutorials; they're pretty good for getting a jump start on all of the basic concepts. – Mats Mikkel Rummelhoff Feb 19 '16 at 14:09
  • Also, Craft Cookbook is a great place to find code examples. – Mats Mikkel Rummelhoff Feb 19 '16 at 14:10
  • Thanks Mike, I've browsed those links and keep referring/searching through them whenever I want to do something, but as this thread shows there are at least 2 solutions and they are very different. I'm not a programmer, so it seems that many solutions require a bit more of a PHP general knowledge/thought process than I have. But I don't think I'm going back to EE. – Paul Frost Feb 19 '16 at 14:51
  • @PaulFrost you have an array of entries and they are from two different sections. If an entry is from section A, it has a value for the radio field and if it's from section B it has no value (stored as NULL). When looping through your entries you have now two options, check the field value or check the section handle. Hope this makes it clearer for you. – carlcs Feb 19 '16 at 15:26
  • @carlcs I still don't understand why {% if entry.eventLevel %}{{ entry.eventLevel }}{% else %}Activity{% endif %} or adding is defined, is null, or length doesn't work. Section B doesn't have the eventLevel field in it's field layout, so I would expect it to be empty or null or not defined. It's the need to wrap eventLevel in brackets that confuses me. I'm not sure when or why I should do that, as I have other IF variable loops that work without brackets. – Paul Frost Feb 19 '16 at 16:33
  • Yeah, I think if entry.eventLevel or if entry.eventLevel is null should work and the is defined test isn't necessary. There's one exception though, which is if you had this field attached to section B and later removed it, because removing a field doesn't delete the values from the db. Maybe this is the case here? – carlcs Feb 19 '16 at 16:47
  • @PaulFrost It's because for some of your entries (i.e. the Section B ones), the eventLevel attribute won't exist, and attempting to access a non-existing attribute isn't allowed (even as a part of a conditional). See this page for reference. I'd recommend turning on devMode for your dev environment if you haven't; it'll make debugging your code a lot easier. – Mats Mikkel Rummelhoff Feb 19 '16 at 16:52
  • @carlcs The issue is that {% if entry.attributeThatDoesntExist is defined %} will always evaluate to true (you can test this with {{ dump(entry.attributeThatDoesntExist is defined) }}). I don't know exactly why it this is, and I admit it seems less than logical/intuitive, but the gist is that whenever an attribute may or may not exist, you have to use brackets notation to test for it before attempting to access the attribute using dot notation. – Mats Mikkel Rummelhoff Feb 19 '16 at 16:59
3

How about a conditional to check the entry.type rather than the field? Perhaps more appropriate in this case rather than checking if the field is defined. Something like this:

{% if entry.type.handle == 'events' %}
    {{ entry.eventLevel }}
{% else %}
    Activity
{% endif %}

Alternatively, check the section rather than the entry type with entry.section.handle instead of entry.type.handle.

James
  • 1,138
  • 8
  • 20
  • I'm not sure what you mean, by check the entry.type. Plus if it's an event it must have a level set, can't be empty. – Paul Frost Feb 19 '16 at 12:16
  • @PaulFrost I’ve just edited the answer to include an example. – James Feb 19 '16 at 12:38
  • @James I think he's confused because he's getting the entries from multiple sections. Your answer probably works anyways, because there's a good chance he's got this set up with different entry type handles. – carlcs Feb 19 '16 at 13:10
  • @carlcs good point. Paul, you could also check against the entry.section rather than the entry type if that makes more sense. – James Feb 19 '16 at 13:23
  • Yes, I changed it to section and that works. But I would never have found that solution from the docs. Why do I need to check the section first? – Paul Frost Feb 19 '16 at 13:41
  • You check for the entry's section and conditional on that either output the field value or something else. The code simply expresses what you are askin for, @PaulFrost... – carlcs Feb 19 '16 at 14:04
2

Is there a chance you once had this field added to both sections? Because this all looks like there's data still left set and you're only expecting "eventLevel" fields to be empty / null for your "activities" section entries, but in fact they are not.

See this SE for why this can happen:
Entries retain field data that has been removed from entry type layout

To solve the problem you could now clear the data, by (a) recreating the radio field with the same settings but with a different handle / name, go through your entries to copy the values, then delete the old field and give the new one the handle and name of the old one. Or you could (b) directly edit the values in the database, the problem with this approach is, you can't sort the records by section.

This would also mean alot of work and I think it's actually not worth it at all, because there's a better way to distinguish whether to show the field's value or not: just check for the entry's section!

Here's how I would do it:

{% set isEvent = entry.section.handle == 'events' %}
{% set isActivity = entry.section.handle == 'activities' %}

{{ isEvent ? entry.eventLevel : 'Activity' }}

You just evaluate entry.section.handle == 'events' and store the result as a variable at the top of your template code. The variable is now available troughout the template and it is now very easy to use this info. You currently only need it once in your code, but this probably changes as you progress with the project.

carlcs
  • 36,220
  • 5
  • 62
  • 139
  • Whilst similar to James's answer this one has a bit more flexibility, so I changed my selected answer. – Paul Frost Feb 19 '16 at 21:42
  • It does also reflect the additional help that carlcs gave me getting to the root of the problem via a Slack DM conversation. – Paul Frost Feb 19 '16 at 22:06