`BOUCLE(DATA)` loop examples

Some (DATA) loop examples, made possible by SPIP’s iterators.

  • New in : SPIP 3.0

Loop on an array

We can loop arbitrarily on an array like those returned by the following tags: #ARRAY, #GET, #SESSION, etc.

Loop on connected user’s session data:

<BOUCLE_session(DATA){source table,#SESSION}>
#CLE: #VALEUR <br>
</BOUCLE_session> 

Loop on an array containing words defined with #SET / #GET [1]:

<BOUCLE_mot(DATA){source table,#GET{mots}}>
#VALEUR 
</BOUCLE_mot> 

Loop on any array containing colours:

<BOUCLE_colours(DATA)
  {source table,#ARRAY{0,#ARRAY{colour,green},1,#ARRAY{colour,blue}}}
  {"<br>"}
>
  [(#VALEUR{colour})] is equivalent to [(#COLOUR)]
</BOUCLE_colours>

Returns:

green is equivalent to green
blue is equivalent to blue

Note: when #VALEUR is an array, we can access #VALEUR{my_key} which is equivalent to #MY_KEY

Listing enabled plugins

The source format plugins does not require any data set on input.

Loop:

<BOUCLE_pl(DATA){source plugins}>
#VALEUR
</BOUCLE_pl>

Returns:

crayons memoization icalendar

Filtering data with {cle==...}

Let’s iterate on the tag #CONFIG which contains all the site’s configuration settings stored in the table spip_meta.

As the tag #CONFIG is a data array, we use the table format. A criterion {cle==version} filter the array retaining the configuration values whose key contains the word "version" only.

Loop:

<BOUCLE_cfg(DATA){source table, #CONFIG*}{cle==version}>
  <dt>#CLE</dt>
  <dd>#VALEUR</dd>
</BOUCLE_cfg>

Returns:

version_installee
16428
revisions_base_version
1.1
Indexation_base_version
0.4
...

Listing a directory’s content

The PHP function glob() lists the files corresponding to a mask; it has been adapted to the (DATA) loop.

A loop listing the IMG sub-directories by alphabetical order:

<BOUCLE_directories(DATA){source ls, #CHEMIN{IMG}/*/}{par basename}>
    [(#VAL{Y-m-d H:i:s}|date{#MTIME})] - [(#FILE)] <br>
</BOUCLE_directories>

Returns:

2018-02-28 17:33:25 - IMG/gif
2018-02-28 18:44:14 - IMG/jpg
2018-02-28 17:33:20 - IMG/png

A loop listing the 10 most recent files of IMG/jpg by the descending order of their dates:

<BOUCLE_files(DATA){source ls, #CHEMIN{IMG}/jpg/*.jpg}
    {!par mtime}
    {0,3}
>
    [(#VAL{Y-m-d H:i:s}|date{#MTIME})] - [(#FILE)] <br>
</BOUCLE_files>

Returns:

2018-02-28 18:45:39 - IMG/jpg/IMG011.jpg
2018-02-23 12:22:15 - IMG/jpg/IMG003.jpg
2018-02-23 12:21:47 - IMG/jpg/IMG004.jpg 

Listing files according to a given mask

The SPIP function preg_files allows the listing of files based on a regular expression and in a tree structure. [2]

Example: the plugin.xml files of the site.

Loop:

<BOUCLE_xml(DATA){source pregfiles, #EVAL{_DIR_RACINE}, plugin.xml$, 5}>
#VALEUR[ - (#VAL{Y-m-d H:i:s}|date{#VALEUR|filemtime})]
</BOUCLE_xml>

Return:

../plugins/a2a/plugin.xml - 2011-04-16 20:47:02
../extensions/simplog/plugin.xml - 2010-09-05 16:08:39
../extensions/vertebres/plugin.xml - 2011-03-19 16:49:51
../extensions/z-core/plugin.xml - 2010-09-05 16:08:47

Reading a CSV file

The file adresses.csv contains an address book in csv format. Displaying this file gives:

Fil,Philippe,fil@rezo.net,http://rezo.net/
Marcimat,Matthieu,marcimat@rezo.net,http://marcimat.magraine.net/

Loop:

<BOUCLE_csv(DATA){source csv, adresses.csv}
{par /3}
{"<br>"}>
#VALEUR{0} : #VALEUR{3}
</BOUCLE_csv>

Returns:

Marcimat: http://marcimat.magraine.net/
Fil: http://rezo.net/

Note: the sort criterion {par /3} allows the sorting on column n° 3, containing the web addresses. The initial / is mandatory in the case of a sort on a numeric key or starting with a number.

In the csv demo supplied with the iterator plug-in, there is a much comprehensive example which manages properly the csv file’s keys and even allows to sort on them or merge them.

A Google document (spreadsheet)

In Google Docs it is possible to "Share > Publish a document". Once this is done, you can retrieve the address of the document in CSV format, and display it as an HTML table.

Loop:

#SET{key,0AolUP3c6K9JodGwxRjJzb2hyTGZLU29qRVItRXk1VXc}
<B_csv>
<table border="1">
<BOUCLE_csv(DATA){source csv, https://spreadsheets.google.com/pub?key=#GET{key}&hl=en&dsds&output=csv}
>
<tr>
<td>#VALEUR{0}</td>
<td>#VALEUR{1}</td>
<td>#VALEUR{2}</td>
</tr>
</BOUCLE_csv>
</table>
</B_csv>

Returns:

Pays PIB Population
Rémitanie 12 1
Baldoghistan 23 2
Républiques Unies 43 3
Diloduristan 12 4
Zarlatie 9 5

Other extraction modes are possible, in particular in the form of a list or cells:

List:

Rémitanie  pib: 12, population: 1
Baldoghistan  pib: 23, population: 2
...

Cells:

A2: Rémitanie
B2: 12
C2: 1
A3: Baldoghistan
B3: 23
C3: 2
...

An HTML page

Read an HTML page, isolate a line with a certain marking and select an interesting value to display it properly? It’s possible! Here we will display the number of followers on a twitter account.

Loop:

#SET{followers,'data-nav="followers"'}
<BOUCLE_followers(DATA){source file, https://twitter.com/spip}{valeur == #GET{followers}}>
[<span title="[(#VALEUR|match{title="(.*)",1}|strtolower)]">(#VALEUR|match{title="(.*)",1}|replace{ abonnés}|trim|replace{ \d+$, k})</span>]
</BOUCLE_followers>

Returns:
1 k

A YAML format web service

The site http://per.sonn.es/ offers an API to read each profile in YAML format. We will therefore query the site on one of these profiles, and browse the result to display the list of friends of the person concerned.

Let’s start by analyzing my profile (at http://per.sonn.es/Fil.yaml):

name: Fil
sex: F
birthday: '1966-08-17'
job: Calorifugeur
friends:
  - 'Maude Guérin'
  - 'Zohra Robin'
  - 'Pierre-Yves Philippe'
  - 'Lauriane Bertin'
  - 'Jeannine Pichon'
  - 'Vanessa Michel'
  - 'Wendy Allard'
...
city: 'Mouxy (Rhone-Alpes)'

If you load this address with the format yaml, you get a slightly more complex data array than in the previous example. Indeed, some items are strings, others, such as the friends list, lists of strings.

(name => Fil, sex => F, friends => ( Maude Guérin, Zohra Robin, ...), ... ).

Let’s go through this data:

Loop:

<BOUCLE_yaml(DATA){source yaml, http://per.sonn.es/Fil.yaml}>
<dt>#CLE</dt>
<dd>[(#VALEUR|print)]</dd>
</BOUCLE_yaml>

Returns:

name
Fil
sex
F
birthday
1966-08-17
job
Calorifugeur
friends
Maude Guérin, Zohra Robin, Pierre-Yves Philippe, Lauriane Bertin, Jeannine Pichon, Vanessa Michel, Wendy Allard, Sylvie Michaud, Gwenaël Voisin, Paule Mary, Maia Ribeiro, Josianne François, Lucas Fernandes
...

Now we want to loop in on Fil’s friends. There are two possibilities for this. The first is to retrieve the data with our loop (DATA) in yaml format, then store the array of friends in a variable that we will browse with another loop (DATA), this time in table format:

Loop:

<BOUCLE_yaml2(DATA){source yaml, http://per.sonn.es/Fil.yaml}{cle=friends}>
  <BOUCLE_friends(DATA){source table, #VALEUR}{" - "}>
    #VALEUR
  </BOUCLE_friends>
</BOUCLE_yaml2>

Returns:

Maude Guérin - Zohra Robin - Pierre-Yves Philippe - Lauriane Bertin - Jeannine Pichon - Vanessa Michel - Wendy Allard - Sylvie Michaud - Gwenaël Voisin - Paule Mary - Maia Ribeiro

{datapath ...} criteria

In the above example, it is clear that the more complex the data table is, the more difficult it will be to code the loops that will have to be nested. This is where the criterion {datapath ... }, which allows you to tell the loop (DATA) the path (in the sense of Xpath) to the data array of interest [3].

With this tag our friends list is written in a single loop (and we take the opportunity to add a pagination):

Loop:

<BOUCLE_yaml3(DATA)
  {source yaml, http://per.sonn.es/Fil.yaml}
  {datapath friends}
  {" - "}{pagination 10}
>
  #VALEUR
</BOUCLE_yaml3>
#PAGINATION
</B_yaml3>

Returns:

Maude Guérin - Zohra Robin - Pierre-Yves Philippe - Lauriane Bertin - Jeannine Pichon - Vanessa Michel - Wendy Allard - Sylvie Michaud - Gwenaël Voisin - Paule Mary
0 | 10 | 20 | 30 | 40 | ...

An API in json

To view images of squirrels from Instagram:

<BOUCLE_instagram(DATA)
  {source json, https://www.instagram.com/explore/tags/squirrel/?__a=1}
  {datapath graphql/hashtag/edge_hashtag_to_media/edges}
>
  <BOUCLE_images(DATA){source table,#VALEUR}{!par taken_at_timestamp}>
    <a href="#DISPLAY_URL">[(#THUMBNAIL_SRC|balise_img)]</a>
  </BOUCLE_images>
</BOUCLE_instagram>

Returns:

XML file

The "random quotations" plugin on SPIP-Zone contains a quotation file in XML format (http://zone.spip.org/trac/spip-zone/browser/_plugins_/citation_aleatoire/trunk/citations/citations_fr.xml). The loop (DATA) allows you to use it directly:

Loop:

[(#REM) Récupérer le fichier au format txt de la zone - mieux vaut l'enregistrer localement sur le serveur]
[(#SET{c,http://zone.spip.org/trac/spip-zone/browser/_plugins_/citation_aleatoire/trunk/citations/citations_fr.xml?format=txt})]

<dl>
<BOUCLE_cite(DATA)
{source xml, #GET{c}}
{par hasard}{0,1}
>
[<dt>(#VALEUR{0/auteur/0})</dt>]
[<dd>(#VALEUR{0/texte/0})</dd>]
</BOUCLE_cite>
</dl>

Returns (randomly):

L'idéal quand on veut être admiré, c'est d'être mort.
(Michel Audiard)

Using a web service such as YQL — Yahoo Query Language)

YQL is a webservice that allows you to easily query many sites such as google, twitter, flickr, etc... With the loop (DATA), SPIP greatly facilitates its integration in the form of loops: Cf. http://zzz.rezo.net/Exemples-de-bou....

PHP iterators

SPIP is able to use standard PHP iterators. You should refer to their documentation, which is often very succinct, and check that the iterators in question are available on your system.

Note, for example, DirectoryIterator, which allows you to list the files from a directory:

Loop:

<pre>
<BOUCLE_ls(php:DirectoryIterator){args IMG/jpg/}
  {pagination 10}
  {valeur!==^\.}{valeur==\.jpg$}
>[(#VAL{Y-m-d H:i:s}|date{#GETMTIME})] / #VALEUR
</BOUCLE_ls>
</pre>
#PAGINATION
</B_ls>

Returns:

2008-02-01 23:27:23 / arton2135.jpg
2008-08-21 11:12:58 / DSC03420.jpg
2008-08-21 11:13:11 / DSC03421.jpg
2009-08-27 11:20:11 / hash-1.jpg
2009-08-27 11:20:04 / hash.jpg

SPIP should be told that it is a PHP iterator, by indicating (php:...) before the name of the iterator.

The {args xx,yy} criterion defines the arguments which will be passed to the iterator during its initialization. For DirectoryIterator, it is the path of the directory to list.

The methods of this iterator (cf. http://php.net/manual/fr/class.directoryiterator.php) are available as tags (here,
#GETMTIME).

Note: To list the contents of a directory, the format glob of the loop (DATA) is probably easier to use than this PHP iterator.
<BOUCLE_ls(DATA){source ls, IMG/jpg/*.jpg} {!par mtime}> ...

An iCalendar/ics format calendar

This feature requires the activation of the iCalendar plugin. It is then possible to iterate on the events of a calendar published in iCalendar/ics format. The article on SPIP-Contrib on the icalendar plugin gives a complete example.

 

Footnotes

[1See also: #SET and #GET

[2more versatile, but slower than glob.

[3To help you determine the datapath, you can view the result array by inserting the code [<pre>(#Value|print_r{1})</pre>] in your data loop/

Author jack, Loiseau2nuit Published : Updated : 23/07/23

Translations : English, français, Nederlands