2

I have a front end form with checkboxes which submits the data as an array into a plain text field in craft (named logGeneral_logInputIndications).

Here's an example of what the content looks in the plain text field in Craft CP:

["Abdominal Pain","RUQ","Vomiting",""]

Instead of outputting the field content in it's default state into my template, I'd like to convert it to an unordered list. If I were to code it by hand it would look like this:

<ul>
    <li>Abdominal Pain</li>
    <li>RUQ</li>
    <li>Vomiting</li>
</ul>

Any ideas if it's possible to do this?

(Mats answered the above question and his suggestion worked perfectly.)

However, I just realized that I have checkbox choices that contain non-alphanumeric content like Normal CBD (<0.4cm) Is there a way to pick and choose which non-alphanumeric content I'd like to strip out vs keep?

Here's a new question that builds upon my initial one...

Is it possible to add a conditional to this? Some of the text is really a child so it should be inset by another unordered list. What my initial thought was is that perhaps I could add an identifier to the form input text if it's a child like "•" or something else, and then in the template, use that identifier in a conditional to say if you see "•" indent it and then strip the marker "•".

So then my plain text content would be:

["Abdominal Pain","•RUQ","Vomiting",""]

And my template code would look like this:

<ul>
    <li>Abdominal Pain</li>
    <ul>
        <li>RUQ</li>
    <ul>
    <li>Vomiting</li>
</ul>

I was able to combine the suggestion Mats gave me with the following to conditionally indent child items.

{% set listItems = entry.logGeneral_logInputIndications|replace('/[^,()<>• a-zA-Z0-9_-]|[, ]$/s','')|split(',') %}
<ul>
    {% if entry.logGeneral_logInputIndications|length %}
        {% for listItem in listItems %}
            {% if listItem starts with '•' %}
                <ul><li>{{ listItem|trim|slice(1) }}</li></ul>
            {% else %}
                <li>{{ listItem|trim }}</li>
            {% endif %}
        {% endfor %}
    {% endif %}
</ul>
2

Something like this should work:

{% set listItems = plainTextValue|replace('/[^, a-zA-Z0-9_-]|[, ]$/s','')|split(',') %}

<ul>
  {% for listItem in listItems %}
    {% if listItem|length %}
      <li>{{ listItem|trim }}</li>
    {% endif %}
  {% endfor %}
</ul>

What the above does is to first use the replace filter to remove any character that isn't alphanumeric (except spaces and commas), before the resulting string (which would look something like 'Abdominal Pain,RUQ,Vomiting') is split on the commas. The resulting array is stored in a variable listItems. Finally, it's just a matter of looping through that array, to create the <li> items.

Note that in the above example, plainTextValue is a variable containing the string '["Abdominal Pain","RUQ","Vomiting",""]' – so swap that out with entry.logGeneral_logInputIndications, or whatever fits.

Edit (OP added more info to the original question)

If your "symptoms" are in fact hierarchical, I'd suggest not using a PlainText field at all. You're quickly running into a situation where your back-end solution is way too simple, which will make your front-end solution way too complex.

My definitive recommendation would be to create a Categories group for the symptoms, and swap out the PlainText field for a Categories field.

Note that you can still render the actual symptoms form element on the front end as checkboxes, you'd just pass in an array of category IDs instead of a plain text string (the following example assumes a Categories group with the handle symptoms and a Categories field with the handle selectedSymptoms):

{# Get all available symptoms #}
{% set symptoms = craft.categories.group('symptoms').limit(null) %}

{# Get the symptoms that have been selected for this entry earlier #}
{% set selectedSymptoms = entry.selectedSymptoms.ids() %}

{# Loop through the available symptoms, creating checkboxes #}
{% for symptom in symptoms %}
    <input type="checkbox" name="fields[selectedSymptoms][]" value="{{ symptom.id }}"{% if symptom.id in selectedSymptoms %} checked{% endif %} />{{ symptom.title }}<br />
{% endfor %}

...and to display whatever symptoms are already selected – in nested lists, as per the category hiearchy, you can use the {% nav %} tag:

<ul>
{% nav symptom in entry.selectedSymptoms %}
    <li>
        {{ symptom.title }}
        {% ifchildren %}
            <ul>
                {% children %}
            </ul>
        {% endifchildren %}
    </li>
{% endnav %}
</ul>
  • Your suggestion worked perfectly @Mats thank you! I just realized that I have checkbox choices that contain non-alphanumeric content like Normal CBD (<0.4cm) Is there a way to pick and choose which non-alphanumeric content I'd like to strip out vs keep? Also, I edited my initial content with another layer to the question. I wasn't sure if what I was trying to do was possible therefore I didn't want to add more complexity to my question initially. I hope this is okay to add to my initial question. – Lori Sep 21 '16 at 19:32
  • Yes, you can add any additional characters you want to keep to the regular expression, e.g. if you want to keep (, ) and < you would write '/[^,()< a-zA-Z0-9_-]|[, ]$/s' (notice the extra characters after the ^). – Mats Mikkel Rummelhoff Sep 21 '16 at 20:07
  • However, I don't think you're on the right track if the symptoms are hierarchical/nested, see my edited answer above :) – Mats Mikkel Rummelhoff Sep 21 '16 at 20:24
2

Is the data a pseudo-syntax, or is it JSON?

If the answer is JSON, then the following may be a more robust solution:

  1. Create a barebones plugin (craft/plugins/helpers/HelpersPlugin.php)

    <? namespace Craft;
    
    class HelpersPlugin extends BasePlugin
    {
      public function getName()
      {
        return Craft::t('Helpers');
      }
    
      public function getVersion()
      {
        return '1.0';
      }
    
      public function getDeveloper()
      {
        return 'Your Name';
      }
    
      public function getDeveloperUrl()
      {
        return 'http://domain.com/';
      }
    
      public function hasCpSection()
      {
        return false;
      }
    }
    
  2. Add the class that will get loaded as a set of helpers within your templates: (craft/plugins/helpers/variables/HelpersVariable.php)

    <? namespace Craft;
    
    class HelpersVariable
    {
      public function parseSymptoms(String $json)
      {
        return array_filter(json_decode($json));
      }
    }
    
  3. Use the helper like this: (craft/templates/your-template.twig)

    <ul>
      {% for ailment in craft.helpers.parseSymptoms(entry.logGeneral_logInputIndications) %}
        <li>{{ ailment }}</li>
      {% endfor %}
    </ul>
    

You could add methods to the HelpersVariable class do additional checks— you'll be able to use the full range of PHP methods to manipulate the data, rather than rely on the subset of functions available as Twig filters.

  • Thank you for your response, August. I hate to say this, but I'm afraid your answer is way over my head. I have a front end form with checkboxes and instead of using the checkbox Craft field I used a plain text field for a few different reasons. I really appreciate you taking the time to send a suggestion and hopefully your suggestion will help others who have more development skill than I do ;) – Lori Sep 21 '16 at 19:20
  • Hey! No sweat— I was (admittedly) answering a question that wasn't really being asked. A bit of context: If the text stored in your field is JSON, you should be using a native parser, not Regex. As you discovered, above, there will always be breaking exceptions to what characters are allowed in a symptom, but if the stored value obeys an existing syntax like JSON, you can reliably expand it back into usable values with this plugin method. Best of luck! – August Miller Sep 22 '16 at 19: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.