Coded something up in Couch in an interesting way? Have a snippet or shortcode to share? Post it here for the community to benefit.
52 posts Page 1 of 6
Previous 1, 2, 3, 4, 5, 6 Next
A little background about the problem first.
As mentioned in the docs on DBF (http://docs.couchcms.com/concepts/databound-forms.html), apart from using type 'bound' inputs to capture data into editable regions, we can also explicitly set them through direct parameters of <cms:db_persist_form> (and its non-DBF counterpart <cms:db_persist>) tag e.g. as follows where 'my_field' and 'my_other_field' are the names of some editable regions -
Code: Select all
<cms:db_persist_form
    my_field="hello world"
    my_other_field=some_variable
/>

This technique, however, was not applicable to repeatable-regions as they are composed of multiple editable regions in multiple rows.

With the current version of Couch (GitHub v2.1b of the day of this post), this finally becomes possible.

For our tests, let use a non-clonable template defining only a single repeatable-region within it with three fields as follows -
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<cms:template>
    <cms:repeatable name='my_repeatable' order='-2'>
        <cms:editable name='my_text' type='text' />
        <cms:editable name='my_checkbox' type='checkbox' opt_values='Newspaper || Website || Phone Book' />
        <cms:editable name='my_dropdown' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' />
    </cms:repeatable>
</cms:template>



<?php COUCH::invoke(); ?>

Register the template by visiting it as super-admin on the front-end.
Then coming back to the admin-panel, add some data to the repeatable-region we have.
For example, my system at this points looks like this -
Untitled-1.png
Untitled-1.png (15.6 KiB) Viewed 4732 times

With the setup complete, let us begin our tests.
Add the following statement in the body of our test template -
Code: Select all
<cms:show_repeatable 'my_repeatable' as_json='1' />

I am sure you are familiar with <cms:show_repeatable> tag used to display data from repeatable-regions.
The 'as_json' parameter, however, would be new for you. That is because it has just been added to v2.1b.

Refreshing the page, you'll see the template outputting something like this -
Code: Select all
[{"my_text":"One","my_checkbox":"Newspaper","my_dropdown":"-"},{"my_text":"Two","my_checkbox":"Website","my_dropdown":"1"},{"my_text":"Three","my_checkbox":"Newspaper|Phone Book","my_dropdown":"2"}]

That, of course, is the JSON representation of the data currently contained in our repeatable-region.
To make the data more readable, I used an online formatter (https://jsonformatter.curiousconcept.com/) and got the following -
Code: Select all
[
   {
      "my_text":"One",
      "my_checkbox":"Newspaper",
      "my_dropdown":"-"
   },
   {
      "my_text":"Two",
      "my_checkbox":"Website",
      "my_dropdown":"1"
   },
   {
      "my_text":"Three",
      "my_checkbox":"Newspaper|Phone Book",
      "my_dropdown":"2"
   }
]

With the structure now more legible, I think it should be easy to figure out that it is an array where each array element represents one row from the repeatable-region and that each row consists of the names of the editable-regions with their values.

I am stressing on this format because to explicitly set data for repeatable-regions, we'll need to specify that data in exactly the format seen above.

To see that happening, let us add a Data-bound Form to the template -
Code: Select all
<cms:set submit_success="<cms:get_flash 'submit_success' />" />
<cms:if submit_success >
    <h4>Saved.</h4>
</cms:if>

<cms:form
    masterpage=k_template_name
    mode='edit'
    enctype='multipart/form-data'
    method='post'
    anchor='0'
    >

    <cms:if k_success >
        <cms:db_persist_form
            _invalidate_cache='0'
        />

        <cms:if k_success >
            <cms:set_flash name='submit_success' value='1' />
            <cms:redirect k_page_link />
        </cms:if>
    </cms:if>

    <cms:if k_error >
        <div class="error">
            <cms:each k_error >
                <br><cms:show item />
            </cms:each>
        </div>
    </cms:if>

    <!--
        bound inputs can be placed here
    -->
   
    <cms:input type='submit' name='submit' value='Submit' />
</cms:form>

As you can see, it is a regular DBF. We have attached it to the template it is placed in and at this point there are no type 'bound' inputs within it. The intention of this post is to see how we can explicitly set the data for a repeatable-region, so the action we are interested in would lie almost entirely in the <cms:db_persist_form /> statement within the success block of the form.

To see that clearly, empty our repeatable-region by deleting all rows currently in it and save.
Now modify our form to make it as follows -
Code: Select all
..
<cms:if k_success >
    <cms:capture into='my_data' is_json='1' >
    [
       {
          "my_text":"One",
          "my_checkbox":"Newspaper",
          "my_dropdown":"-"
       },
       {
          "my_text":"Two",
          "my_checkbox":"Website",
          "my_dropdown":"1"
       },
       {
          "my_text":"Three",
          "my_checkbox":"Newspaper|Phone Book",
          "my_dropdown":"2"
       }
    ]
    </cms:capture >

    <cms:db_persist_form
        _invalidate_cache='0'
        my_repeatable=my_data
    />
    ..

As you can see, we have made two changes -
1. Added a <cms:capture> block just above the <cms:db_persist_form> statement.
This block sets a variable named 'my_data' using the same JSON that we saved earlier.

2. Explicitly set my_repeatable=my_data within <cms:db_persist_form> (which is what the entire exercise is all about).

Submit the form and in the admin-panel you should see that our three original rows have been restored using the explicit data we passed to <cms:db_persist_form>.

Of course, in a real-world scenario, we'll be crafting our own JSON instead of using hard-coded data as in our example above but this should serve as a clear example of what needs to be done.

Continuing using the hard-coded JSON data - press submit once again. What do you think will the repeatable-region (which at this point has three rows) end up containing now? Take a look and you'll find that no matter how many times you submit the form, the repeatable-regions always ends up having the same three rows.

That is because the value we pass on to <cms:db_persist_form> completely overwrites whatever existing data is contained in the repeatable-region.
If your use-case dictates that each form submission preserves the existing data and appends the new data as a new row, you can use the following method -
Code: Select all
<cms:if k_success >
    <cms:capture into='my_data' is_json='1'>
        <cms:show_repeatable 'my_repeatable' as_json='1' />
    </cms:capture >
   
    <cms:capture into='my_data.' is_json='1'>
       {
          "my_text":"Three",
          "my_checkbox":"Newspaper|Phone Book",
          "my_dropdown":"2"
       }
    </cms:capture >

    <cms:db_persist_form
        _invalidate_cache='0'
        my_repeatable=my_data
    />
    ..

The code requires some explanation.
The first <cms:capture> sets our 'my_data' variable by reading in the current data from the repeatable-region.
So we begin with 'my_data' as an array containing all the current rows of the region.

Look carefully at the second <cms:capture> block - the variable it is setting has a trailing '.' (dot).
As explained in the docs on "Multi-value variables (or Arrays)" viewtopic.php?f=5&t=10892, the dot serves to add a new row (unnamed key, actually) to an existing array.

So basically the second <cms:capture> appends our (hardcoded at this point) data as a new row to the existing rows of the repeatable-region.
Try it and you'll see that each submission adds a new row while preserving the existing rows.

Up until this point, we have been working with hard-coded JSON.
It is finally time to use submitted data to create JSON dynamically.

Add three inputs to the form as follows -
Code: Select all
<!-- 
    bound inputs can be placed here
-->
<cms:input name='my_text2' type='text' /><br>
<cms:input name='my_checkbox2' type='checkbox' opt_values='Newspaper || Website || Phone Book' /><br>
<cms:input name='my_dropdown2' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' /><br>

<cms:input type='submit' name='submit' value='Submit' />
..

Please notice that these are not 'bound' inputs. Just plain cms:inputs with parameters that exactly match those used by the constituent editable regions used within our repeatable-region.

As we know, upon successful form submission, Couch will make available the contents of these inputs as variables with 'frm_' prepended to their names. We'll now make use of those variables to make our JSON dynamic as follows -
Code: Select all
<cms:capture into='my_data.' is_json='1'>
   {
      "my_text" : "<cms:show frm_my_text2 />",
      "my_checkbox" : "<cms:show frm_my_checkbox2 />",
      "my_dropdown" : "<cms:show frm_my_dropdown2 />"
   }
</cms:capture >

The code above should serve as the final template for you to generate your JSON (names of fields in quotes followed by their values in quotes and no comma after the last field) but there is a potential problem that needs to be addressed here before we call it quits.

JSON format is pretty unforgiving and explicitly forbids several character in its input. According to http://json.org/, following characters must be escaped before being used in JSON strings
quotation mark "
forward slash /
back slash \
new line n
carriage return r
tab t

To see what I mean, try inputting hello \ world in the text box. The resulting row will come up blank as the generated JSON is considered malformed.

To prevent that from happening, we'll make a final change to our code -
Code: Select all
<cms:capture into='my_data.' is_json='1'>
   {
      "my_text" : <cms:escape_json><cms:show frm_my_text2 /></cms:escape_json>,
      "my_checkbox" : <cms:escape_json><cms:show frm_my_checkbox2 /></cms:escape_json>,
      "my_dropdown" : <cms:escape_json><cms:show frm_my_dropdown2 /></cms:escape_json>
   }
</cms:capture >

Instead of quotes around the values, we use <cms:escape_json></cms:escape_json> instead. This tag takes care of escaping all JSON problem characters (and also surrounds the resulting string in quotes - which is why we are no longer using quotes ourselves).

And that is it. Final code stands as follows -
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
    <cms:template>
        <cms:repeatable name='my_repeatable' order='-2'>
            <cms:editable name='my_text' type='text' />
            <cms:editable name='my_checkbox' type='checkbox' opt_values='Newspaper || Website || Phone Book' />
            <cms:editable name='my_dropdown' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' />
        </cms:repeatable>
    </cms:template>


    <cms:set submit_success="<cms:get_flash 'submit_success' />" />
    <cms:if submit_success >
        <h4>Saved.</h4>
    </cms:if>

    <cms:form
        masterpage=k_template_name
        mode='edit'
        enctype='multipart/form-data'
        method='post'
        anchor='0'
        >

        <cms:if k_success >
            <cms:capture into='my_data' is_json='1'>
                <cms:show_repeatable 'my_repeatable' as_json='1' />
            </cms:capture >
           
            <cms:capture into='my_data.' is_json='1'>
               {
                  "my_text" : <cms:escape_json><cms:show frm_my_text2 /></cms:escape_json>,
                  "my_checkbox" : <cms:escape_json><cms:show frm_my_checkbox2 /></cms:escape_json>,
                  "my_dropdown" : <cms:escape_json><cms:show frm_my_dropdown2 /></cms:escape_json>
               }
            </cms:capture >

            <cms:db_persist_form
                _invalidate_cache='0'
                my_repeatable=my_data
            />

            <cms:if k_success >
                <cms:set_flash name='submit_success' value='1' />
                <cms:redirect k_page_link />
            </cms:if>
        </cms:if>

        <cms:if k_error >
            <div class="error">
                <cms:each k_error >
                    <br><cms:show item />
                </cms:each>
            </div>
        </cms:if>

        <!--
            bound inputs can be placed here
        -->
        <cms:input name='my_text2' type='text' /><br>
        <cms:input name='my_checkbox2' type='checkbox' opt_values='Newspaper || Website || Phone Book' /><br>
        <cms:input name='my_dropdown2' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' /><br>

        <cms:input type='submit' name='submit' value='Submit' />
    </cms:form>   


<?php COUCH::invoke(); ?>

Hope this helps.
An elegant solution to this limitation - thanks @KK!
I'm glad you liked it @cheesypoof :)
I remember us discussing the possible use of JSON as a solution a long time back.
It happens that repeatable region might be empty before adding to it. In such cases my_data variable must be correctly set so my_data. (with dot) becomes available. A simple change by adding a check will make repeatable to save correctly in the above's final example:

Code: Select all
<cms:capture into='my_data' is_json='1'>
    <cms:show_repeatable 'my_repeatable' as_json='1' />
</cms:capture >

<cms:if "<cms:not "<cms:is_array my_data />" />"><cms:ignore>
       
        // If repeatable is empty, prepare the variable
       
    </cms:ignore>
    <cms:set my_data = '[]' is_json='1' scope='global'/>
</cms:if>

<cms:capture into='my_data.' is_json='1'>
   {
      "my_text" : <cms:escape_json><cms:show frm_my_text2 /></cms:escape_json>,
      "my_checkbox" : <cms:escape_json><cms:show frm_my_checkbox2 /></cms:escape_json>,
      "my_dropdown" : <cms:escape_json><cms:show frm_my_dropdown2 /></cms:escape_json>
   }
</cms:capture >


@KK, therefore either the sample in your post should be corrected or, maybe, "cms:capture" might behave with such assumption (automatically set array, if not exist)?

Edit: To make myself clear - above example of full template code is correct. My suggestion can happen if people copy-paste this data-bound-form and change masterpage to some other template and forget to get the my_repeatable repeatable's data correctly as in:

Code: Select all
<cms:capture into='my_data' is_json='1'>
    <cms:pages masterpage='target-template.php' id='target-id'>
        <cms:show_repeatable 'my_repeatable' as_json='1' />
    </cms:pages>
</cms:capture >


Thanks.

P.S. I join the green-team in all the praisals and throwing of a big party regarding this solution (finally!) as it was asked for many times by various folks here and there :)
@trendoman, that check won't be necessary as an empty repeatable region will output an empty array (i.e. '[]').
So we can be sure to be always working with a valid array.
KK wrote: @trendoman, that check won't be necessary as an empty repeatable region will output an empty array (i.e. '[]').
So we can be sure to be always working with a valid array.


Please see my edit of the above post.
Also, I might add to it another request (since requests are a common thing about @trendoman) - make "cms:get_field" get the repeatable:
Code: Select all
<cms:get_field 'my_repeatable' masterpage='data.php' id='37660' as_json='1' />
Very interesting!

Only thing though is that when I try your code, and click "SAVE" on the front-end, it overwrites the region in the admin page.
Nothing is added in the admin panel.

I copied and pasted the code you have posted, and made sure I was using Couchcms 2.1b
When using this code:
Code: Select all
..
<cms:if k_success >
    <cms:capture into='my_data' is_json='1' >
    [
       {
          "my_text":"One",
          "my_checkbox":"Newspaper",
          "my_dropdown":"-"
       },
       {
          "my_text":"Two",
          "my_checkbox":"Website",
          "my_dropdown":"1"
       },
       {
          "my_text":"Three",
          "my_checkbox":"Newspaper|Phone Book",
          "my_dropdown":"2"
       }
    ]
    </cms:capture >

    <cms:db_persist_form
        _invalidate_cache='0'
        my_repeatable=my_data
    />
    ..


It is properly saving the content of that local JSON data to the repeatable regions when clicking "SAVE" on the front-end form. But when I change it to:

Code: Select all
<cms:if k_success >
            <cms:capture into='my_data' is_json='1'>
                <cms:show_repeatable 'my_repeatable' as_json='1' />
            </cms:capture >
           
            <cms:capture into='my_data.' is_json='1'>
               {
                  "my_text" : <cms:escape_json><cms:show frm_my_text2 /></cms:escape_json>,
                  "my_checkbox" : <cms:escape_json><cms:show frm_my_checkbox2 /></cms:escape_json>,
                  "my_dropdown" : <cms:escape_json><cms:show frm_my_dropdown2 /></cms:escape_json>
               }
            </cms:capture >


Clicking SAVE will delete all the data from that repeatable region. It will not add anything to it.

Any idea what's going on? Thanks!
@larin555, the original post provides the full template as the final code.

It is tested and works just fine on my setup.
Please copy and use it in its entirety - it should work on yours too.

Let me know how it goes.
Thanks KK.
I forgot to replace all files in the Addons folder in my project to the new ones from Couch 2.1b.
After I did, everything worked out. I guess there was something in there that needed to be upgraded to work with this new method.

Thanks a lot
It would be super useful to be able to access repeatable regions as a frm_ prefix on submission. It would be useful for backing up the data on form submission, merging the contents of multiple repeatable regions etc.

As far as I can tell it is not possible to access repeatable regions as a frm_ variable. It is possible to intercept the PHP POST variable prefixed with f_ but it would be much more accessible with native couch.
Previous 1, 2, 3, 4, 5, 6 Next
52 posts Page 1 of 6