1

Banging my head on this one and have not been able to come up with a solution to this. I'm wanting to create a default template for all single entries that checks for all of the fields I have created if they exist and the position in that section.

This template would have all code for all possible field types. So far this doesn't work the way I was hoping it would as it renders the page based on the order of the code in the template. So if a client adds a new field to a section it doesn't matter what order they put the field in the list of fields, in the end it is determined by where the code to render that field is in the template.

I have developed with wordpress, acf, and visual page builders where you can drag drop elements into different positions which are just shortcodes but the page ends up rendering based on the position of the elements "shortcodes".

Is this at all possible? Here is some examples of some code in said template...

<!-- Testimonials Block -->
{% if entry.testimonials|length %}
<div class="container-fluid add-top">
    <div class="row">
        <div class="text-center testimonials">
        {% for block in entry.testimonials %}
            {% if block.type == "testimonial" %}
                {% set image = block.image.first() %}
                <blockquote class="blockquote-unstyled" style="display:none;">
                    <footer class="text-uppercase">
                        <span title="Source Title">{{block.sourceTitle}}</span>
                    </footer>
                    <p class="lead">“{{block.lead}}”</p>
                </blockquote>
            {% endif %}
        {% endfor %} 
        <div class="list-inline">
        {% for block in entry.testimonials %}
            {% if block.type == "testimonial" %}
                {% set image = block.image.first() %}
                    <li><img class="img-responsive img-circle" alt="Profile" src="{{ image.getUrl('testimonials') }}" width="{{ image.getWidth('testimonials') }}" height="{{ image.getHeight('testimonials') }}"/></li>
            {% endif %}
        {% endfor %} 
        </div>
        </div>
    </div>
</div>
{% endif %}

<!-- Impact Statement -->
{% if entry.impactStatement|length %}
    {% for block in entry.impactStatement %}
        {% if block.type == "impactStatement" %}
            <div class="container-fluid add-top">
                <div class="row">
                    <div class="text-center impact-highlight">
                        <p>{{block.heading}}</p>
                    </div>
                </div>
            </div>
        {% endif %}
    {% endfor %} 
{% endif %}

<!-- Large Headline Blocks -->
{% if entry.largeHeadline|length %}
<div class="container-fluid add-top">
    <div class="row">
    {% for block in entry.largeHeadline %}
            {% if block.type == "headline" %}
                {% set image = block.image.first() %}
                    <div class="col-sm-6 col-md-10 col-md-push-1 add-top">
                        <div class="row">
                            <div class="col-sm-6 hidden-xs {{block.imagePosition}}">
                                <!-- Img Logo -->
                                <img src="{{image.url}}"/>
                            </div>
                            <div class="col-sm-6">
                                <h3 class="remove-top">{{block.heading}}</h3>
                                <small>{{block.paragraph}}</small>
                            </div>
                        </div>
                    </div>
            {% endif %}
    {% endfor %} 
    </div>
    <hr class="add-top">
</div>
{% endif %}

As you can see this is just three field types but I have a lot. But for this example the template has them in this order.

Testimonials Matrix Field

Impact Statement

Large Headline

If I move impact statement in the section above testimonials, that has no relation on where that impact statement gets rendered in the entry...

I also tried to do ifelse thinking it would loop through the template outputting the blocks in the order they are in the section for that single if they exist. But this just outputs the testimonials block and stops haha.

{% if entry.testimonials|length %}
//Block Code
{% elseif entry.testimonials|length %}
//Block Code
{% elseif entry.testimonials|length %}
//Block Code
{% endif %}

I thought one way to solve it would just be to have one matrix field with all of the possible fields as a block with each field in that block, but then I think I run into issues wrapping each element in a container as for each block I end up outputting a container so they end up nested.

Any solutions or possible tips would be great. Hope this makes sense.

1
  • In the craft_fieldlayoutfields table there's a sortOrder column...
    – tom
    Commented May 8, 2015 at 16:12

2 Answers 2

1

If I understand you correctly, you need to be able to create unique "pages", and through some sort of "layout/page builder" create those page's layouts on a page-by-page basis.

Using Singles sections to achieve this is the wrong approach, in my opinion – in fact, creating/managing sections should never be part of the regular editorial workflow, and honestly I can't really imagine a use case where such an approach would even be needed. If it exists, I'd say Craft may not be the best platform for your project.

In your case, I'd suggest creating a Structure or Channel section called "Pages" to hold your "page" entries. If you want to be able to nest your pages – or need them to be in a specific order – use a Structure, if not a Channel will suffice.

For building the layout, I'd add a single Matrix field to the "Pages" section's entry type, called "Layout" or the like, containing all the possible "fields" or layouts (like "Impact statement") as block types.

The one thing I'm a bit confused about is your issue about having "nested containers" with the above approach. I'm not seeing the problem as you could basically just use your current markup like this, and you would have the same end result as you currently have, only with the correct order:

{% for block in entry.pageLayout %}

    {% switch block.type %}

        {% case 'impactStatement' %}
        <div class="container-fluid add-top">
            <div class="row">
                 <div class="text-center impact-highlight">
                     <p>{{block.heading}}</p>
                 </div>
            </div>
        </div>

        {% case 'largeHeading' %}
        ...

    {% endswitch %}

{% endfor %}
5
  • Right I was going to try this, but the issue I had with the containers seemed to be related to the for loop. Say each block type needs its own container div. When I tried this before for each block type it would output that same container div and for some reason was nesting them. I will update example code if this ends up working. Thanks for the suggestion for creating a channel or stucture just not sure what the difference is vs creating a section and editing the section.
    – Bowenac
    Commented May 9, 2015 at 14:33
  • Your nesting issue sounds like a markup thing - maybe an unclosed tag? You might want to put that issue up as a separate question; specifically highlighting the point where your markup turns weird. Using sections as entries is a bad idea for many reasons, but the main problem is that you're setting your client/editors up with a needlessly complex and error prone workflow. Commented May 9, 2015 at 17:02
  • Wanted to say thanks again for your help, this worked out great, and used this craftcms.stackexchange.com/a/1747/3579 for my nesting issue. I created a section called pages, and a matrix field called layout with all the fields so far so good.
    – Bowenac
    Commented May 11, 2015 at 17:47
  • That's great, @Bowenac - happy to hear it! Commented May 11, 2015 at 17:49
  • Ran into a nesting issue with testimonials haha craftcms.stackexchange.com/questions/9691/…
    – Bowenac
    Commented May 12, 2015 at 2:07
1

You could possibly loop through the field layout for that section, and then output the fields in the order that they are defined. In this example, I'm using macros, but you could just as easily place the code within the switch statement directly.

{% macro testimonials(matrix) %}
    {% import _self as self %}

    <div class="container-fluid add-top">
        <div class="row">
            <div class="text-center testimonials">

                {% for block in matrix %}
                    {% if block.type == "testimonial" %}

                        <blockquote class="blockquote-unstyled" style="display:none;">
                            <footer class="text-uppercase">
                                <span title="Source Title">{{ block.sourceTitle }}</span>
                            </footer>
                            <p class="lead">“{{ block.lead }}”</p>
                        </blockquote>

                    {% endif %}
                {% endfor %}

                <div class="list-inline">

                    {% for block in matrix %}
                        {% if block.type == "testimonial" %}
                            {% set image = block.image.first() %}
                            <li><img class="img-responsive img-circle" alt="Profile" src="{{ image.getUrl('testimonials') }}" width="{{ image.getWidth('testimonials') }}" height="{{ image.getHeight('testimonials') }}"/></li>
                        {% endif %}
                    {% endfor %}

                </div>
            </div>
        </div>
    </div>

{% endmacro %}

{% macro impactStatment(matrix) %}
    {% import _self as self %}
    {# format/output matrix data #}
{% endmacro %}

{% macro largeHeadline(matrix) %}
    {% import _self as self %}
    {# format/output matrix data #}
{% endmacro %}


{% for tab in entry.getFieldLayout.getTabs %}

    {% for field in tab.getFields %}

        {% switch field.getField.handle %}

            {% case 'testimonials' %}
                {{ macros.testimonials(entry.testimonials) }}

            {% case 'impactStatement' %}
                {{ macros.impactStatement(entry.impactStatement) }}

            {% case 'largeHeadline' %}
                {{ macros.largeHeadline(entry.largeHeadline) }}

        {% endswitch %}

    {% endfor %}

{% endfor %}

You would obviously need to process and format the output (i.e. loop through the matrix, add html formatting, etc.)

5
  • Wouldn't this be the same as adding all input fields as content blocks of a matrix field? Then I would end up having issues wrapping each section in a container or so I think I would, not sure how to stop the loop from nesting containers.
    – Bowenac
    Commented May 8, 2015 at 17:25
  • I'm not sure you get what I was saying — I'm suggesting that you continue to use your matrix fields, but just output them in the order that the fields are defined — that should take care of the nesting issues. Personally I wouldn't try and make such a catch all template to begin with. As I'm not sure I see the advantage — but if you wanted to do that, this is one way to go about it. I'll update the answer to show an example of a matrix output. Commented May 8, 2015 at 17:58
  • The reason I want to do this is so a client can add new "pages" sections without needing to know which template they need to choose... Then they can add what ever input fields they want to that section and not have to worry about the order and output.
    – Bowenac
    Commented May 8, 2015 at 17:59
  • You might want to check out superTable plugin... putting a superTable field in a matrix field is essentially the same as placing a matrix in a matrix (which is currently not possible). Then your client could potentially build pages/sections in a structure on the fly without having to resort to creating new sections or editing fieldLayouts in the settings, but with advantage of being able to group items together nicely (i.e. testimonials). Commented May 8, 2015 at 18:27
  • FYI... they are working on a version of superTable that supports data added in a single column vs columns/rows — I think that might suit your use case better. Might want to reach out to the developer and see where they are with that. Commented May 8, 2015 at 18:37

Your Answer

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

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