The syntax of SPIP’s loops

The basic syntax

The simplified syntax for a loop is as follows:

* HTML code + SPIP tags

We saw previously, in the overview of loops and tags, that the HTML code + SPIP tags included in the example above are repeated as many times as the loop can extract records from the database (in other words, once, several times, or not at all).

The important line in this example is:


-  The word BOUCLE (French for "loop") is an instruction given to SPIP to define a new loop and cannot be written any other way: all SPIP loops must start with a BOUCLE command.

-  The n element is the loop’s identifier. It can be either a name or a number and is freely chosen by the webmaster for every loop created (warning: we will take care to name our loops only with alphanumeric characters that do not have diacritical accents and the "underscore"; i.e. characters from the class defined by [a-zA-Z0-9_]. We will see later how it is possible to use several loops in the same template. This is, after all, the key object of the exercise, and because of this it is essential for each loop to have a unique identifier.

If you decide to number your loops, the syntax for loop 5 would be:

<BOUCLE5...> ... </BOUCLE5>

If you decide to give a name to your loops, which is usually more convenient and makes your code easier to read, then the name must start with the underscore symbol “_”. For example:

<BOUCLE_subsections...> ... </BOUCLE_subsections>

-  The (TYPE) element is essential for defining what kind of items are to be displayed. The syntax is important: the TYPE must be entered between parentheses, with no spaces, in capital letters, and must be one of the types specified by SPIP: ARTICLES, RUBRIQUES, AUTEURS, BREVES, etc. (they are all covered in this documentation).[Note: not strictly true any more - you can create your own extended tables, and you may also refer to database table names explicitly. Refer to the documentation on extending SPIP and the plugins already constructed to help you do so.]

So, continuing with the previous example, we now have:


-  The criteria specified in {criterion1}{criterion2}... are used to define selection criteria for the records to be retrieved from the database (for example, showing the sub-sections in the current section, or other sections at the same hierarchical level, or ...). Criteria are also used to further filter the selection of items and define how they will be sorted. For example, articles can be sorted by their publication dates, or their titles, etc., and then only the first three articles displayed, or only half of the articles, and so on. With a combination of criteria, you can easily make loops which reflect complicated queries.

Each criteria is coded between opening and closing braces, or they may be entered together within a single pair of braces but with spaces between them. Example:

<BOUCLE_same_author(ARTICLES){id_auteur}{par date}{inverse}{0,5}>

For each type of loop a detailed description of the various criteria which can be used, together with the syntax for using them, is given in this documentation. Some criteria work for every type of loop, others are specific to loops on only certain kinds of objects.

Full Syntax

The syntax shown above can be completed by conditional elements or [as of SPIP 4.0] by unconditional elements but associated with the returns of the loop.

Conditional elements: the loop previously introduced displays successively the elements contained within the loop. SPIP also allows you to indicate what is displayed before and after the loop if it contains one or more results, and what is displayed if there are no elements.

This gives:

* Optionnal code unconditional before
* * Optional code before
* Code + SPIP tags
* Optional code after
* * Alternative code
* Optionnal code unconditional after

The optional “before” code (preceded by <Bn>) is displayed only if the loop returns at least one result; it appears before the loop’s results.

Attention :
Although it is displayed before the main body of the loop, the optional "before" code is computed after the execution of the loop (allowing, for example, to display the tag #TOTAL_BOUCLE before the loop’s particular returns are displayed).

The optional “after” code (ended by </Bn>) is shown only if the loop returns at least one result; it appears after the loop’s results.

The alternative code (ended by <//Bn>) is displayed instead of the loop (and therefore also instead of the optional codes before and after) if the loop does not display anything either because the database did not provide any answers, either because the code using these answers in the loop does not display anything.

For example, the code:

 This section has the following elements:
  This section does not have any articles.

will display the following results, depending on the circumstances:

-  if there is only one article:

This section has the following elements:
   <li>Title of the article</li>
</ul>one article

-  if there are several articles:

This section has the following elements:
   <li>Title of article 1</li>
   <li>Title of article 2</li>
   <li>Title of the last article</li>

-  if there are no articles:

This section does not have any articles.

Unconditional elements but associated with the loop

SPIP 4.0 introduces the possibility of specifying unconditional parts before and after each loop, within which tags relating to the execution of the loop can be interrogated, whether or not there is a result.

This is done by using <BB> and </BB>, as in the following example:

<div class="style_myloop">
    <h1>there are [(#TOTAL_BOUCLE) ]articles</h1>
    <p>Maybe you made a mistake?...</p>

In particular, the following tags can be used <#PAGINATION, #TRI, #TOTAL_BOUCLE and #GRAND_TOTAL, to build for example the headers of a table or a list of results.

Conditional tables

From SPIP 4.0 onwards, it is possible to write a loop on a table that may or may not exist. This is useful, for example, to create a part that is only displayed if
the plugin that creates this table has been installed and activated. To avoid an error if the table does not exist, we add a ? after its name.

For example, the plugin organiseur (which was one of the plug-in-dist) generates an RSS feed that provides information about personal forums... only if the FORUMS table exists:

<BOUCLE_RSS2(FORUMS ?){!par date_heure}{!doublons F}{tout}{statut=perso} >

Shortcut syntax

It is possible in certain cases to use a shorthand method for coding the loops.

As such, if you only want to retrieve the total number of records returned in the query (with the #TOTAL_BOUCLE tag), you can write:

` in place and instead of:`

This shorthand form can also be useful whenever you’re only looking to fill up a "doublons" (duplicates) table:<BOUCLE_a(ARTICLES) {criteria ...} {doublons}/>

Anonymous loops

Since SPIP 4.0, it has also been possible to create an anonymous loop, i.e. one without a name, when the loop is not nested. A large number of simple loops, complete or partial, are concerned.

The syntax is the same except that there is no number or name n of the loop.

Example :

  <div style="border:5px solid red;padding:20px;">
    <h1>[(#TOTAL_BOUCLE) ]articles</h1>
      <p>Maybe you made a mistake?...</p>

There can obviously be several anonymous loops in a page, and the same anonymous loop can be present several times in the page.

Internally, and to reference this loop in the debug page (obtained with var_mode=debug) the compiler generates a unique identifier for each anonymous loop (line number of the source file + hash).

Criteria for a cascading environment

Each loop selects items from the database according to criteria. Some of these criteria correspond to the environment in which the loop is located.

For example, if you want to have a loop “Display the articles of this section”, you need to know which section is targeted. This is what is meant by the environment.

-  The environment given by the URL

When you visit a page on a SPIP site, its address usually contains a variable.
For instance:

This variable (id_rubrique) defines an initial environment. Thus, the loop “Display the articles of this section” will be interpreted as: “Display the articles of section 15”.

And if you call the very same template, with the following URL:
the loop will become “Display the articles of section 7”.

-  The environment provided by other loops

When loops are nested within each other, the inner loop inherits the environment of the one surrounding it. This creates a cascading environment.

Let’s have a look at the following pseudo-structure:

<BOUCLE_articles: (shows the articles from this section)>
  Title of the article
  <BOUCLE_authors: (shows the authors of this article)>
    Name of the author

You can see that:
-  the first loop (BOUCLE_articles) displays the articles of the section, where the latter is defined by the environment given in the URL (id_rubrique=15 for example);
-  this loop returns one or several articles;
-  each time the BOUCLE_articles loop executes, it provides a different environment: that of the current article (id_article=199 for example);
-  the inner loop, (BOUCLE_authors), will be triggered each time that the BOUCLE_articles performs one loop. As each time the environment provided by the outer loop is different, so the meaning of BOUCLE_authors, which, on its own, would be: “show the authors of this article”, will become successively: “show the authors of the first article”, “show the authors of the second article”, and so on.

You can see that by nesting loops within each other it is possible to build a very useful structure, with inner loops inheriting environmental variable values from their parent outer loops. The environment of the outermost loop is inherited from one or more parameters in the URL which calls the page.

Nested loops and successive loops

Just as you can nest loops, each loop being placed within the context of another loop, you can also write loops one after the other; successive loops don’t have any influence on each other.

For instance, the template for a section usually consists of the following elements:

  <ul>Title of the section
    <li>Title of the article</li>
    <li>Title of the sub-section</li>
  <ul>There is no section at this address.</ul>

The first loop (BOUCLE_section) depends on the variable passed by the URL of the page (id_rubrique=15 for example).

The other loops (BOUCLE_articles and
BOUCLE_subsections) are placed within the first loop. If there is no section 15, the first loop will not return any results (the alternative code “There is no section...” will be shown), and the two loops inside it will be totally ignored. But if there is a section 15, then the two inner loops will be analysed.

The two inner loops are written one after the other. So even though they both depend on the outer loop, they are independent of each other. Thus, if there are no articles in section 15 (BOUCLE_articles), the list of sub-sections in section 15 (BOUCLE_subsections) will still be displayed and vice-versa.


Two tags make it possible to count the number of results in a loop:

-  #TOTAL_BOUCLE gives the total number of results returned by the loop. It may be used anywhere within the loop syntax: in the main body of the loop, within the optional before/after parts, or even in the alternative, “no results”, part.
For example, in order to display the number of documents associated with an article:

  <BOUCLE_doc(DOCUMENTS) {id_article}></BOUCLE_doc>
  [There are (#TOTAL_BOUCLE) document(s).]

Note: If the main body of the loop does not contain any text (as is the case with <BOUCLE_doc> above, which only counts the number of results), then #TOTAL_BOUCLE needs to be placed in the alternative part following the main loop (between </BOUCLE_doc> and <//B_doc>).

-  #COMPTEUR_BOUCLE increases each time the loop loops, and gives the number of the current loop. It can be used to number the results:

<BOUCLE_art(ARTICLES) {par date} {inverse} {0,10}>

Author mathieu, bealach, Paolo Published : Updated : 05/05/23

Translations : عربي, català, Deutsch, English, Español, français, italiano, Nederlands, русский, українська