**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
(
$u
)
{
if
(
is_array
(PHP: is_array - Manual)
(
$json
=
json_decode
(PHP: json_decode - Manual)
(
$u
)
)
OR
is_object
(PHP: is_object - Manual)
(
$json
)
)
return
(
array
(PHP: Arrays - Manual)
)
$json
;
}
We can declare a new `(DATA)` loop in a `inc/my_source_to_array.php` file
function
inc_my_source_to_array_dist
(
$data
,
$param1
=
''
,
$param2
=
''
)
{
// $data contains (local | distant) file's content or, variable's value
transmitted in criteria.
// $param1, $param2... are optional parameters
...
}
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/)
![]()
![]()