5

Problem

I'm building a .json file for a search component of a used cars website. The file needs to be built from the output of two sections within CraftCMS, Used Cars and Vans & Trucks. It outputs the "make" of vehicle and it's "models".

My problem is that there are duplicate "makes"/"models" because they exist in both sections.

Code:

{% set allStock = craft.entries.section(['usedCars','vansTrucks']).level(2) %}
{% for stock in allStock %}
{% set models = stock.getChildren() %}
"{{ stock.make }}" : "{% for stock in models %}{{ stock.model }}{% if not loop.last %}, {% endif %}{% endfor %}"
{% endfor %}

Output:

"Holden" : "Commodore, Ute, Viva"

"Nissan" : "ATLAS, CARAVAN"

"Mazda" : "ATENZA, Axela, CX-7, MAZDA 6, MPV, RX-8, mpv"

"Nissan" : "CARAVAN, DUALIS, FAIRLADY, Liberty, Maxima, NAVARA, NOTE"

Updated

{% set allStock = craft.entries.section(['usedCars','vansTrucks']).level(2) %}
{% set newArray = [] %}
{% for stock in allStock %}
{% set models = stock.getChildren() %}
{% if stock.make not in newArray %}
"{{ stock.make }}" : "{% for stock in models %}{{ stock.model }}{% if not loop.last %}, {% endif %}{% endfor %}"
{% set newArray = newArray|merge([stock.make]) %}
{% endif %}
{% endfor %}

This code will remove the duplicate 'make' and merge but it also removes the models with it.

Output:

"Holden" : "Commodore, Ute, Viva"

"Mazda" : "ATENZA, Axela, CX-7, MAZDA 6, MPV, RX-8, mpv"

"Nissan" : "CARAVAN, DUALIS, FAIRLADY, Liberty, Maxima, NAVARA, NOTE"

Any help or direction on how to go about merging the models would be greatly appreciated.

1
  • If you are comfortable with PHP, I would highly recommend building a simple plugin to handle this. That's a notable amount of logic for Twig to handle, you'd be better served putting it directly into PHP.
    – Lindsey D
    Commented Dec 12, 2014 at 7:01

2 Answers 2

9

You could do it in the twig template

But a template isn't really the best place for something that is this codey, it gets pretty unreadable pretty quick.

Notes:

  • I start by building arrays of models and afterwards I convert those arrays to the comma-separated strings you require.
  • It is case-sensitive, you probably have models or makes likes HILUX, hilux, Hilux, these sort of duplicates will not be removed. You can achieve a modicum of case-insensitivity by chucking a few lowers in there.
  • Instead of hand-writing the JSON, this uses twig's json_encode filter; tweak the options to suit you.

The template code:

{% set makes = craft.entries.section(['usedCars','vansTrucks']).level(2) %}
{% set data = {} %}

{% for make in makes %}
    {% set make_name = make.make %}

    {# Get the list of models for this make if we have already seen it before, otherwise an empty array #}
    {% if data[make_name] is not defined %}
        {% set data = data|merge({(make_name):[]})%}
    {% endif %}
    {% set make_data = data[make_name] %}

    {# Build an array of model names #}
    {% set models = make.getChildren() %}
    {% for model in models %}
        {% set model_name = model.model %}

        {# Only add this model if we have not seen it before #}
        {% if model_name not in make_data %}
            {% set make_data = make_data|merge([model_name]) %}
        {% endif %}
    {% endfor %}

    {% set data = data|merge({(make_name):make_data}) %}
{% endfor %}

{# Replace the arrays of models we previously built with a comma-separated string of models #}
{% for make_name, models in data %}
    {% set models_string = models|join(",") %}
    {% set data = data|merge({(make_name):models_string}) %}
{% endfor %}

{{ data|json_encode(constant('JSON_PRETTY_PRINT'))|raw }}

Or do it with code

Add your own plugin as per the instructions here: Setting things up

Add a template variable as per the instructions here: Template variables

Your template variable could look like this:

namespace Craft;

class MyAwesomePluginVariable
{
    public function getSearchData()
    {
        // find all makes
        $make_criteria = craft()->elements->getCriteria(ElementType::Entry);
        $make_criteria->level = 2;
        $make_criteria->section = array('usedCars','vansTrucks');

        $data = array();

        $makes = $make_criteria->find();
        foreach ($makes as $make) 
        {
            // build array of models for this make
            $model_names = array();
            $models = $make->getChildren();
            foreach ($models as $model) {
                $model_names[]= $model->model;
            }

            // combine with previous models for this make and remove duplicates
            if (array_key_exists($make->make, $data)) {
                $data[$make->make] = array_unique( array_merge($data[$make->make], $model_names) );
            } else {
                $data[$make->make] = array_unique( $model_names );
            }
        }

        // convert model arrays to comma separated strings
        foreach ($data as $make => $models) {
            $data[$make] = implode(",", $models);
        }

        return $data;
    }

}

And then in your template:

{{ craft.myAwesomePlugin.getSearchData|json_encode(constant('JSON_PRETTY_PRINT'))|raw }}
1
  • Wow thanks! I made the Craft template variable as a plugin and it worked first time. Appreciate the help, learnt plenty from your examples.
    – R0wan
    Commented Dec 16, 2014 at 0:14
6

The accepted answer here should hopefully help you out.

Basically:

{% set newArray = [] %}

{% for name in array %}
   {% if name not in newArray %}
      My name is {{name}}
      {% set newArray = newArray|merge([name]) %}
   {% endif %}
{% endfor %}
1
  • Thanks Steve I've updated my question with your code sample.
    – R0wan
    Commented Dec 12, 2014 at 0:51

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.