{doublons} is used to restrict the display of results that are already included in other loops using that criteria.

The {doublons} (duplicates) criteria, once you’ve discovered it, has the potential to rapidly become an indispensable component in your template files [1]. Even if it’s a strange beast to implement, it enables some very interesting record selections, but which would not normally leap out at the casual reader of the SPIP.NET documentation, which merely says:

The {doublons} or {unique} criteria [2] :
are used to eliminate the display of results which are already displayed in other loops that use this criteria. These two criteria are strictly identical to each other.

Identical? Well, not exactly! With the progression of different versions of SPIP, doublons has come to benefit from several supplementary possibilities:
-  we can nominate our duplicates (doublons), for which there can be several in the same piece of code,
-  it also enables some SPIP tricks with anti-duplicates!

Basic usage: do not re-include elements that are already handled on the current page

An example is offered to us in the default home page template in the ’dist’ collection [3]

<BOUCLE_recent_articles(ARTICLES) {par date}{inverse} {0,2} {doublons}>
some elements to display, like #TITRE
<BOUCLE_other_articles(ARTICLES) {par date}{inverse} {doublons} >
some elements to display, like #TITRE

This entails listing the articles of the site in inverse chronological order AND reserving the two most recently published articles for some special treatment. As you can see, the "_recent_articles" and "_other_articles" loops are constructed in exactly the same manner. In theory, they would therefore normally return the same list of articles.

It’s thanks to the work of the "doublons" criteria that the 2 most recent articles, already "processed" in the first loop, will not be found in the list displayed by the "_other_articles" loop.

Another common usage: excluding certain elements

Our grand classic: excluding depending on a keyword

We often see the spip-users list see requests about this type of problem:

"I can’t seem to exclude some elements depending on their keywords.
I tried:

<BOUCLE_rubriques(RUBRIQUES) {racine} {titre_mot!=invisible}{par num titre, titre}>

but it doesn’t work properly"

And for good reason!

What the user wants here, is to select all the sections that do NOT have the "invisible" keyword attached. However, what the database understands by {titre_mot != invisible} is that it should select all sections that DO HAVE a keyword AND ALSO that keyword is different from "invisible".

And that changes everything. Since the results will contain, for example, a section which has been assigned the keyword "thingy", which is different from "invisible" (ok!), but which has also been linked to the "invisible" keyword (arg !), and what’s more, there will also be the section that does not have ANY keywords assigned (basically quite the opposite of what the user really wanted !). [4]

The solution: sequence an empty loop which selects sections according to the matching keyword, with a secondary loop that returns results based on using our {doublons} criterion.

Continuing with our example above, this would give us:

<BOUCLE_exclude(RUBRIQUES) {racine} {titre_mot=invisible}{doublons}>

This loop selects all sections linked with the "invisible" keyword , but which actually displays nothing at all.

<BOUCLE_sections(RUBRIQUES) {racine}{par num titre, titre} {doublons}>
{normal processing goes in here}

This second loop, using the doublons criterion, will select its members as all the other sections and then apply the normal processing provided by the user.

Naming duplicates (doublons) to use them several times in the same file

Objective: on the home page, manage the display of links to articles and to press releases. The presentation of the two most recently published articles and two most recent press releases are to be different from the mass of all the others.

For our example, we see here the same schema already seen with the DIST loops. This simply entails making some very similar loops live side-by-side without any conflicts. Naming the duplicates will avoid the selection criteria from one loop from interfering with those of the other.

<BOUCLE_communiques_recents(ARTICLES) {!par date}{id_mot=1} {0,2} {doublons com}>
some elements to display, like #TITRE
<BOUCLE_autres_communiques(ARTICLES) {!par date}{id_mot=1} {doublons com} >
some elements to display, like #TITRE

<BOUCLE_articles_recents(ARTICLES) {!par date} {0,2} {doublons art}>
some elements to display, like #TITRE
<BOUCLE_autres_articles(ARTICLES) {!par date} {doublons art} >
some elements to display, like #TITRE

Generally speaking, naming your duplicates is an overall good practice to avoid any current concertina type problems, but also things that may occur later in the future (templates which might be later altered, with the author overlooking the use of the doublons). It is also a component which helps automatically document your SPIP code.

For advanced usage, you could try using SPIP tags for this purpose too. For example: {doublons #TITRE} or even {doublons #_myownloop:TITRE}, perhaps even {doublons (#_myownloop:TITRE|supprimer_numero)}.

Advanced usage: anti-duplicates, or how to stack up a pile of data to be processed

The anti-duplicates mechanism

In this instance, "doublons" will make it possible to accumulate the results of several loops that use different criteria and "!doublons" to apply the intended processes to this collection of items.

The usage schema is as follows:

We select a first set of articles (but display nothing)...

<BOUCLE0(ARTICLES){id_mot=2}{doublons A}></BOUCLE0>

...then a second set of articles (and still display nothing)....

<BOUCLE1(ARTICLES){id_auteur=1}{doublons A}></BOUCLE1>

... we collect according to our wishes and display the whole lot using the anti-doublons criterion.

<BOUCLE2(ARTICLES){par date}{!doublons A}>#TITRE<br></BOUCLE2>

An example of anti-doublons

Objective: build a loop which retrieves the articles of all sections except for sections 2 and 3, AND, in respect of the articles in section 4, only those from the last 60 days.

The solution: we need a first loop that will find us all the articles but excluding those in sections 2, 3, and 4...

<BOUCLE0(ARTICLES) {id_rubrique !IN 2,3,4}{doublons tri1}></BOUCLE0>

....to which we supplement a second loop of the other articles we don’t really want...
<BOUCLE1(ARTICLES) {id_rubrique=4}{age<60}{doublons tri1}></BOUCLE1>

...and then in the third loop we can use anti-doublons to display the articles selected by eliminating the ones selected already according to our various loop criteria.

<BOUCLE2(ARTICLES){par date}{!doublons tri1}>#TITRE<br></BOUCLE2>


[2Attention, do not confuse the "unique" criteria and the "unique" filter, which are applied in these applications for Usage of the unique filter and Using the unique filter.

[3This is the SPIP’s default template located in the /DIST directory. Reminder: you should not alter these files, but compile the files for your own templates in a separate /squelettes folder that should be created at the same hierarchy level as /DIST, i.e. in the site’s root directory.

[4This note was copied from this discussion thread (Thanks, Cédric!)

Original article: SPIP-Contrib.net.
Published on spip.net by Teddy

Author Mark Published : Updated : 13/07/23

Translations : català, English, Español, français, Nederlands