Proposé : Iterators for SPIP - the (DATA) loop

**SPIP**

# Article proposé

L’article "**Iterators for SPIP - the (DATA) loop**
(SPIP)" est proposé
à la publication depuis le dimanche 20 janvier 2019.

* * *

## Iterators for SPIP - the (DATA) loop

dimanche 20 janvier 2019 , par [Fil](.././?page=auteur&id_auteur=1&) ,
[Loiseau2nuit](.././?page=auteur&id_auteur=5162&) , [Matthieu
Marcillaud](.././?page=auteur&id_auteur=3109&)

SPIP 3.0 brought a brand new kind of loops, based on PHP’s iterators.

Not only `(DATA)` loops can execute SQL requests [1] , but they also loop
on every kind of data lists.

For example :

* a data array produced by any function (including PHP iterators)
* a local file’s content, format XML, CSV, JSON, YAML, ...and so on.
* a files list in a server’s directory
* a web service request
* a SQL request (e.g. as calculated by SPIP)
* and so on. (LDAP, ...).

## `(DATA)` loop’s syntax and formats

That loop can iterate on any data array. It’s basic syntax is following :

    <BOUCLE_example

    (DATA)

    {source format, data}

    >

    #TAGS

    </BOUCLE_example>

The `{source format, data}` criteria defines the data on which the loop
should iterate.

A data source definition needs two elements :

— **The `data` part** : this element can be of various natures :
- a data array, for example : `#ENV*`
- path to a file on the hard drive, example : `sources/definitions.csv`
- a file or a web service’s URL, for example :
`http://per.sonn.es/bases/phobia.fr.yaml\`
- or any other string the format will be able to convert to a data array,
ex : `"select * from flickr.photos.search where text='spip'"`

— **The `format` part** is to be chosen from the following list :
- `table` (a.k.a. `array` or `tableau`), for a previously declared array
- `csv`, `json`, `yaml` for any file using one of those formats
- `file` to loop on some file’s lines
- `glob` or `pregfiles` to loop on files in a given directory (and more...)
  
- `rss` (a.k.a `atom`) to read any news feed
- `plugins` to list all enabled plugins on the website
- `yql` to query on Yahoo Query Language web service
- `sql` to send a raw request to the SQL server (you should use `{source
sql, connector:query}` to query an external database)
- `ics` to loop on calendars (needs *icalendar* plugin : read Plugin
iCalendar (Plugin iCalendar - SPIP-Contrib) for more)
- and so on.

All those formats are already available and, it’s very easy to add new
ones, creating a simple function `inc_FORMAT_to_array($u)`. As an example,
here is the function that converts a JSON file to an array :

    function
    
     inc_json_to_array_dist
    
    &#40;

    $u

    &#41;

    &#123;

    if

    &#40;

    is_array
    
     (PHP: is_array - Manual)
    
    &#40;

    $json

    =

    json_decode
    
     (PHP: json_decode - Manual)
    
    &#40;

    $u

    &#41;

    &#41;

      OR
    
    is_object
    
     (PHP: is_object - Manual)
    
    &#40;

    $json

    &#41;

    &#41;

    return

    &#40;

    array
    
     (PHP: Arrays - Manual)
    
    &#41;

    $json

    ;

    &#125;

We can declare a new `(DATA)` loop in a `inc/my_source_to_array.php` file

    function
    
     inc_my_source_to_array_dist
    
    &#40;

    $data

    ,

    $param1

    =

    ''

    ,

    $param2

    =

    ''

    &#41;

    &#123;

    // $data contains (local | distant) file's content or, variable's value
transmitted in criteria.

    // $param1, $param2... are optional parameters

    ...

    &#125;

and then display it :

    <BOUCLE_example

    (DATA)

    {source my_source,test}

    >

    #VALEUR

    </BOUCLE_example>

**Good to know :** A `(DATA)` loop’s result is cached. We can disable
this behavior with `{datacache 0}` criteria.

## Filtering, ordering, paginating, merging

**Filtering.** Like any other SQL loops, `(DATA)` loops can be filtered
with criterias like `{valeur=x}` : available operators are `=`, `>`, `<`,
`>=`, `<=`, `==` (rational expression) and `LIKE`

Although, that filtering is not done up — when querying, like we do in
SQL — but down the line, on the data array we just got.

**Ordering.** All kind of `{par xx}` orderings are also possible, with
their variant `{!par xx}` to backward orders.

**Paginating.** Pagination works normally, so does the offset/limit `{a,b}`
criteria.

**Merging.** The `{fusion /x/y}` criteria also works. For example, on a CSV
file containing addresses, if the email field is numbered 3, we’ll then
be able to keep only one record per email address with the following loop :

    <BOUCLE_csv

    (DATA)

    {source csv, addresses.csv}

    {fusion /3}

    {par /0}

    {'<br/>'}

    >

    #VALEUR

    {0}
    
     :
    
    #VALEUR

    {3}

    </BOUCLE_csv>

Merging is done after ordering and, only retains the first element he met.
That way, if an array is ordered by `{!par date}` then merged on the email
field, the entry that will be retained for each email address, will be the
latest one.

## `{liste ...}` criteria

To lighten the way we write data arrays, when it comes to a simple list of
items we want to manually provide, the `(DATA)` loop also accepts `{liste
...}` criteria, which let you build an array, separating each data with a
comma ",".

Loop :

    <BOUCLE_i

    (DATA)

    {liste 3,4,5}

    {"<br>"}

    >

    <BOUCLE_j

    (DATA)

    {liste 6,7,8}

    {" "}

    >

    [

    (

    #VALEUR

    |mult

    {
    
    #
    
    _i:
    
    VALEUR
    
    }

    )

    ]

    </BOUCLE_j>

    </BOUCLE_i>

returns :

18 21 24
24 28 32
30 35 40

*Note :* We used `#_i:VALEUR` in `j` loop, as a reference for the value
calculated by `i` loop.

See also Two simple iterators : lists and enumerations
(SPIP)

## `{enum ...}` criteria

    <BOUCLE_enumerate

    (DATA)

    {enum 2,10,2}

    >

    #VALEUR

    </BOUCLE_enumerate>

returns :

2 4 6 8 10

    <BOUCLE_enum

    (DATA)

    {enum g,m}

    {", "}

    >

    #VALEUR

    </BOUCLE_enum>

returns :

g, h, i, j, k, l, m

`val1` and `val2` are two numeric values or, 2 characters. SPIP finding out
which one, of the 2 values, is the smallest, this loop is going to
enumerate values between `val1` and `val2`. In the first variant, no
`interval` is set : it’s worth 1 by default.

See also Two simple iterators : lists and enumerations
(SPIP)

## Protecting iterated data

`(DATA)` loop automatically protects every tags inside it, because the data
we carry on often comes from unknown, potentially harmful sources. This
also affects forms tags (`#FORMULAIRE_xx`) that you’ll have to call with
a "*", as explained in following article on #TAG* and #TAG**
(SPIP)

    <BOUCLE_foreach_protect

    (DATA)

    {liste a,b,c,d,e}

    >

    #FORMULAIRE_XXX

    *

    {
    
    #VALEUR
    
    }

    </BOUCLE_foreach_protect>

*Note that, if you use "Bonux" plugin (Bonux for SPIP 3.* - SPIP-Contrib),
it’s going to take a simple loop `(POUR)`, which does not have that
behavior.*

    <BOUCLE_Foreach_non_protect

    (POUR)

    {liste a,b,c,d,e}

    >

    #FORMULAIRE_XXX

    {
    
    #VALEUR
    
    }

    </BOUCLE_Foreach_non_protect>

**Read more** : have fun with some `BOUCLE(DATA)` loop examples
(SPIP) !

You can also read the dedicated mini-website on SPIP and DATA loop uses
http://spip-loves-opendata.spip.net
(http://spip-loves-opendata.spip.net/la-boucle-data)

[1] With no surprise, a classic SPIP’s iterator is named SQL : it
executes the request, as SPIP calculated it and, knows how to browse
results list to send them to the loop.

— Envoyé par SPIP (https://www.spip.net/)

rubon135-80ef7.jpg

puce.gif