From e8f29ae8ccf863627d00c9429b887c8a9254cf00 Mon Sep 17 00:00:00 2001
From: gloas <gloas@afi-sa.fr>
Date: Thu, 18 Nov 2021 16:24:31 +0100
Subject: [PATCH] hotline MT #142649 fix cache key generation in
 AjaxPaginatedListHelper

---
 VERSIONS_HOTLINE/142649                       |   1 +
 .../Class/User/CardsOperationDecorator.php    |   4 +-
 .../Library/AjaxPaginatedListHelper.php       |   9 +-
 .../Library/View/Wrapper/Abstract.php         |  18 +-
 .../View/Wrapper/AbstractCardOperation.php    | 159 ++++++++++++++++++
 .../Library/View/Wrapper/ActivitySession.php  |   4 +-
 .../Wrapper/ActivitySessionInscription.php    |   2 +-
 .../Intonation/Library/View/Wrapper/Hold.php  | 158 +----------------
 .../Intonation/Library/View/Wrapper/Loan.php  | 118 +------------
 .../Library/View/Wrapper/MenuEntry.php        |  10 +-
 .../Library/View/Wrapper/PNBHold.php          |  19 +--
 .../Library/View/Wrapper/PNBLoan.php          |  19 +--
 .../Library/View/Wrapper/ReviewsByRecord.php  |  10 +-
 .../Library/View/Wrapper/Selection.php        |   2 +-
 .../Library/View/Wrapper/Suggestion.php       |   5 +-
 .../Intonation/Library/View/Wrapper/Work.php  |   4 +-
 .../Templates/TemplatesAbonneTest.php         |   8 +-
 .../TemplatesAjaxPaginatedListTest.php        |  67 +++++++-
 .../Templates/TemplatesWebsitesTest.php       |   2 +-
 19 files changed, 283 insertions(+), 336 deletions(-)
 create mode 100644 VERSIONS_HOTLINE/142649
 create mode 100644 library/templates/Intonation/Library/View/Wrapper/AbstractCardOperation.php

diff --git a/VERSIONS_HOTLINE/142649 b/VERSIONS_HOTLINE/142649
new file mode 100644
index 00000000000..f41092da6b5
--- /dev/null
+++ b/VERSIONS_HOTLINE/142649
@@ -0,0 +1 @@
+ - ticket #142649 : Magasin de thèmes : Correction du rendu de liste à interactions qui pouvait renvoyer une autre liste après la pagination.
\ No newline at end of file
diff --git a/library/Class/User/CardsOperationDecorator.php b/library/Class/User/CardsOperationDecorator.php
index 60a69701112..14237bcf772 100644
--- a/library/Class/User/CardsOperationDecorator.php
+++ b/library/Class/User/CardsOperationDecorator.php
@@ -27,8 +27,8 @@ class Class_User_CardsOperationDecorator {
 
   public static function decorateAll($operations, $card) {
     return $operations->collect(
-                                function($loan) use ($card) {
-                                  return new Class_User_CardsOperationDecorator($loan, $card);
+                                function($operation) use ($card) {
+                                  return new Class_User_CardsOperationDecorator($operation, $card);
                                 });
   }
 
diff --git a/library/templates/Intonation/Library/AjaxPaginatedListHelper.php b/library/templates/Intonation/Library/AjaxPaginatedListHelper.php
index 462951de562..63b4148e01f 100644
--- a/library/templates/Intonation/Library/AjaxPaginatedListHelper.php
+++ b/library/templates/Intonation/Library/AjaxPaginatedListHelper.php
@@ -139,10 +139,11 @@ class Intonation_Library_AjaxPaginatedListHelper {
 
     return $this->_collection_ids =
       $this->getCollection()
-           ->select(function($wrapper)
-                    {
-                      return $wrapper->getId();
-                    });
+           ->injectInto([], function($ids, $wrapper)
+                        {
+                          $ids [] = get_class($wrapper) . ':' . $wrapper->getId();
+                          return $ids;
+                        });
   }
 
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/Abstract.php b/library/templates/Intonation/Library/View/Wrapper/Abstract.php
index e071b22cbe6..c31280b7e58 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Abstract.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Abstract.php
@@ -38,7 +38,7 @@ abstract class Intonation_Library_View_Wrapper_Abstract {
 
   public function __sleep() {
     if ($this->_model) {
-      $this->_model_id = $this->getModelId();
+      $this->_model_id = $this->getId();
       $this->_model_classname = $this->getModelClassname();
     }
 
@@ -59,7 +59,16 @@ abstract class Intonation_Library_View_Wrapper_Abstract {
 
 
   public function getId() {
-    return $this->_model_id;
+    return $this->_model_id
+      ? $this->_model_id
+      : $this->_model_id = $this->_getModelId();
+  }
+
+
+  protected function _getModelId() {
+    return $this->_model
+      ? $this->_model->getId()
+      : '';
   }
 
 
@@ -95,11 +104,6 @@ abstract class Intonation_Library_View_Wrapper_Abstract {
   }
 
 
-  public function getModelId() {
-    return $this->_model->getId();
-  }
-
-
   public function getAnchor() {
     return '';
   }
diff --git a/library/templates/Intonation/Library/View/Wrapper/AbstractCardOperation.php b/library/templates/Intonation/Library/View/Wrapper/AbstractCardOperation.php
new file mode 100644
index 00000000000..eddd7fe15c0
--- /dev/null
+++ b/library/templates/Intonation/Library/View/Wrapper/AbstractCardOperation.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * Copyright (c) 2012-2018, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+abstract class Intonation_Library_View_Wrapper_AbstractCardOperation extends Intonation_Library_View_Wrapper_Abstract {
+
+  protected
+    $_operation,
+    $_record_cache,
+    $_ready,
+    $_library_label,
+    $_end_availability;
+
+
+  public function __sleep() {
+    $this->_operation = $this->_model->getOperation();
+    $sleep_params = parent::__sleep();
+    $sleep_params [] = '_operation';
+    return $sleep_params;
+  }
+
+
+  public function getSecondaryTitle() {
+    return ($record = $this->_getRecord())
+      ? $record->getSecondaryTitle()
+      : $this->_model->getAuteur();
+  }
+
+
+  public function getPicture() {
+    return ($record = $this->_getRecord())
+      ? $record->getPicture()
+      : '';
+  }
+
+
+  public function getPictureAction() {
+    return '';
+  }
+
+
+  public function getDescriptionTitle() {
+    return '';
+  }
+
+
+  public function getMainLink() {
+    return ($record = $this->_getRecord())
+      ? $record->getMainLink()
+      : null;
+  }
+
+
+  public function getSecondaryLink() {
+    return ($record = $this->_getRecord())
+      ? $record->getSecondaryLink()
+      : null;
+  }
+
+
+  public function getSecondaryIco() {
+    return ($record = $this->_getRecord())
+      ? $record->getSecondaryIco()
+      : null;
+  }
+
+
+  public function getDocType() {
+    return ($record = $this->_getRecord())
+      ? $record->getDocType()
+      : null;
+  }
+
+
+  public function getDocTypeLabel() {
+    return ($record = $this->_getRecord())
+      ? $record->getDocTypeLabel()
+      : '';
+  }
+
+
+  public function getEmbedMedia() {
+    return '';
+  }
+
+
+  public function getHtmlPicture() {
+    return '';
+  }
+
+
+  public function getOsmData() {
+    return null;
+  }
+
+
+  protected function _getRecord() {
+    if ($this->_record_cache)
+      return $this->_record_cache;
+
+    if (!$db_record = $this->_model->getNoticeOPAC())
+      return $this->_record_cache = null;
+
+    return $this->_record_cache = Class_Template::current()->newWrapper($db_record, $this->_view);
+  }
+
+
+  protected function _getOperationId() {
+    return $this->_model->getOperationId();
+  }
+
+
+  protected function _getModelId() {
+    $id = ($user =  $this->_model->getUser())
+      ? $user->getId()
+      : 0;
+
+    $this->_operation = $this->_model->getOperation();
+    return $this->_model_id = serialize([$this->_operation, $id]);
+  }
+
+
+  protected function _getModelIdAsJson() {
+    $id = ($user =  $this->_model->getUser())
+      ? $user->getId()
+      : 0;
+
+    return $this->_model_id = json_encode([$this->_model->getOperationId(), $id]);
+  }
+
+
+  public function getModelClassname() {
+    return get_class($this);
+  }
+
+
+  public static function find($id) {
+    $id = unserialize($id);
+    return new Class_User_CardsOperationDecorator($id[0], Class_Users::find($id[1]));
+  }
+}
\ No newline at end of file
diff --git a/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php b/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php
index d933caa5810..b174572930a 100644
--- a/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php
+++ b/library/templates/Intonation/Library/View/Wrapper/ActivitySession.php
@@ -37,7 +37,7 @@ class Intonation_Library_View_Wrapper_ActivitySession
     return (new Intonation_Library_Link)
         ->setUrl($this->_view->url(['controller' => 'abonne',
                                     'action' => 'detail-session',
-                                    'id' => $this->getModelId()],
+                                    'id' => $this->getId()],
                                    null, true))
         ->setText($this->_('En savoir plus'))
         ->setImage($this->getIco('read-document', 'library'))
@@ -70,7 +70,7 @@ class Intonation_Library_View_Wrapper_ActivitySession
     $actions = [(new Intonation_Library_Link)
                 ->setUrl($this->_view->url(['controller' => 'abonne',
                                             'action' => 'inscrire-session',
-                                            'id' => $this->getModelId()],
+                                            'id' => $this->getId()],
                                            null, true))
                 ->setImage($this->getIco('add', 'utils'))
                 ->setText($this->_('S\'inscrire'))
diff --git a/library/templates/Intonation/Library/View/Wrapper/ActivitySessionInscription.php b/library/templates/Intonation/Library/View/Wrapper/ActivitySessionInscription.php
index 974656b29d7..851e48f1803 100644
--- a/library/templates/Intonation/Library/View/Wrapper/ActivitySessionInscription.php
+++ b/library/templates/Intonation/Library/View/Wrapper/ActivitySessionInscription.php
@@ -42,7 +42,7 @@ class Intonation_Library_View_Wrapper_ActivitySessionInscription
       $links [] = (new Intonation_Library_Link)
         ->setUrl($this->_view->url(['controller' => 'abonne',
                                     'action' => 'edit-session',
-                                    'id' => $this->getModelId()],
+                                    'id' => $this->getId()],
                                    null, true))
         ->setText($this->_('Modifier'))
         ->setImage($this->getIco('edit', 'utils'))
diff --git a/library/templates/Intonation/Library/View/Wrapper/Hold.php b/library/templates/Intonation/Library/View/Wrapper/Hold.php
index 34755307566..32124baef2e 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Hold.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Hold.php
@@ -20,22 +20,7 @@
  */
 
 
-class Intonation_Library_View_Wrapper_Hold extends Intonation_Library_View_Wrapper_Abstract {
-
-  protected
-    $_operation,
-    $_wrapper_cache,
-    $_ready,
-    $_library_label,
-    $_end_availability;
-
-
-  public function __sleep() {
-    $this->_operation = $this->_model->getOperation();
-    $sleep_params = parent::__sleep();
-    $sleep_params [] = '_operation';
-    return $sleep_params;
-  }
+class Intonation_Library_View_Wrapper_Hold extends Intonation_Library_View_Wrapper_AbstractCardOperation {
 
 
   public function getMainTitle() {
@@ -48,55 +33,26 @@ class Intonation_Library_View_Wrapper_Hold extends Intonation_Library_View_Wrapp
     $hold_by = $this->_div(['class' => 'hold_by'],
                            $hold_by);
 
-    if ( ! $this->_model->getNoticeOPAC())
+    if ( ! $record = $this->_getRecord())
       return $hold_by .
         $this->_div(['class' => 'document_title'],
                     $this->_model->getTitre());
 
-    $wrapper = $this->_getWrapper();
-
     return $hold_by
       . $this->_div(['class' => 'document_title'],
-                    $wrapper->getMainTitle());
-  }
-
-
-  public function getSecondaryTitle() {
-    if (!$this->_model->getNoticeOPAC())
-      return $this->_model->getAuteur();
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getSecondaryTitle();
-  }
-
-
-  public function getPicture() {
-    if (!$this->_model->getNoticeOPAC())
-      return '';
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getPicture();
-  }
-
-
-  public function getPictureAction() {
-    return '';
+                    $record->getMainTitle());
   }
 
 
   public function getDescription() {
     $html = [$this->getBadges()];
 
-    if (!$this->_model->getNoticeOPAC())
+    if ( ! $record = $this->_getRecord())
       return implode($html);
 
-    $wrapper = $this->_getWrapper();
-
     $html = $this->_addParagraphe($html);
 
-    $html [] = $wrapper->getDescription();
+    $html [] = $record->getDescription();
 
     return implode($html);
   }
@@ -117,72 +73,15 @@ class Intonation_Library_View_Wrapper_Hold extends Intonation_Library_View_Wrapp
   public function getFullDescription() {
     $html = [$this->getBadges()];
 
-    if (!$this->_model->getNoticeOPAC())
+    if ( ! $record = $this->_getRecord())
       return implode($html);
 
-    $wrapper = $this->_getWrapper();
-
-    $html [] = $wrapper->getFullDescription();
+    $html [] = $record->getFullDescription();
 
     return implode($html);
   }
 
 
-  public function getDescriptionTitle() {
-    return '';
-  }
-
-
-  public function getMainLink() {
-    if (!$this->_model->getNoticeOPAC())
-      return null;
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getMainLink();
-  }
-
-
-  public function getSecondaryLink() {
-    if (!$this->_model->getNoticeOPAC())
-      return null;
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getSecondaryLink();
-  }
-
-
-  public function getSecondaryIco() {
-    if (!$this->_model->getNoticeOPAC())
-      return null;
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getSecondaryIco();
-  }
-
-
-  public function getDocType() {
-    if (!$this->_model->getNoticeOPAC())
-      return null;
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getDocType();
-  }
-
-
-  public function getDocTypeLabel() {
-    if (!$this->_model->getNoticeOPAC())
-      return null;
-
-    $wrapper = $this->_getWrapper();
-
-    return $wrapper->getDocTypeLabel();
-  }
-
-
   public function getBadges() {
     $rang = $this->_model->getRang();
     $badges = [];
@@ -242,47 +141,4 @@ class Intonation_Library_View_Wrapper_Hold extends Intonation_Library_View_Wrapp
             ->setText($this->_('Supprimer'))
             ->setTitle($this->_('Supprimer la réservation %s', $this->_model->getTitre()))];
   }
-
-
-  public function getEmbedMedia() {
-    return '';
-  }
-
-
-  public function getHtmlPicture() {
-    return '';
-  }
-
-
-  public function getOsmData() {
-    return null;
-  }
-
-
-  protected function _getWrapper() {
-    if ( $this->_wrapper_cache)
-      return $this->_wrapper_cache;
-
-    return $this->_wrapper_cache =
-      Class_Template::current()->newWrapper($this->_model->getNoticeOPAC(), $this->_view);
-  }
-
-
-  public function getModelId() {
-    $id = ($user =  $this->_model->getUser())
-      ? $user->getId()
-      : 0;
-
-    return [$this->_operation, $id];
-  }
-
-
-  public function getModelClassname() {
-    return get_class($this);
-  }
-
-
-  public static function find($id) {
-    return new Class_User_CardsOperationDecorator($id[0], Class_Users::find($id[1]));
-  }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/Library/View/Wrapper/Loan.php b/library/templates/Intonation/Library/View/Wrapper/Loan.php
index c0fbd6282a7..7c21ea195a5 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Loan.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Loan.php
@@ -20,19 +20,7 @@
  */
 
 
-class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapper_Abstract {
-
-  protected
-    $_record,
-    $_operation;
-
-
-  public function __sleep() {
-    $this->_operation = $this->_model->getOperation();
-    $sleep_params = parent::__sleep();
-    $sleep_params [] = '_operation';
-    return $sleep_params;
-  }
+class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapper_AbstractCardOperation {
 
 
   public function getMainTitle() {
@@ -53,25 +41,6 @@ class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapp
   }
 
 
-  public function getSecondaryTitle() {
-    return ($record = $this->_getRecord())
-      ? $record->getSecondaryTitle()
-      : $this->_model->getAuteur();
-  }
-
-
-  public function getPicture() {
-    return ($record = $this->_getRecord())
-      ? $record->getPicture()
-      : '';
-  }
-
-
-  public function getPictureAction() {
-    return '';
-  }
-
-
   public function getDescription() {
     return ($record = $this->_getRecord())
       ? $this->getBadges() . $record->getDescription()
@@ -86,46 +55,6 @@ class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapp
   }
 
 
-  public function getDescriptionTitle() {
-    return '';
-  }
-
-
-  public function getMainLink() {
-    return ($record = $this->_getRecord())
-      ? $record->getMainLink()
-      : null;
-  }
-
-
-  public function getSecondaryLink() {
-    return ($record = $this->_getRecord())
-      ? $record->getSecondaryLink()
-      : null;
-  }
-
-
-  public function getSecondaryIco() {
-    return ($record = $this->_getRecord())
-      ? $record->getSecondaryIco()
-      : '';
-  }
-
-
-  public function getDocType() {
-    return ($record = $this->_getRecord())
-      ? $record->getDocType()
-      : '';
-  }
-
-
-  public function getDocTypeLabel() {
-    return ($record = $this->_getRecord())
-      ? $record->getDocTypeLabel()
-      : '';
-  }
-
-
   public function getBadges() {
     $issue_date = $this->_model->getIssueDate();
     $return_date = $this->_model->getDateRetour();
@@ -220,49 +149,4 @@ class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapp
 
     return $actions;
   }
-
-
-  public function getEmbedMedia() {
-    return '';
-  }
-
-
-  public function getHtmlPicture() {
-    return '';
-  }
-
-
-  protected function _getRecord() {
-    if ($this->_record)
-      return $this->_record;
-
-    if (!$db_record = $this->_model->getNoticeOPAC())
-      return $this->_record = null;
-
-    return $this->_record = Class_Template::current()->newWrapper($db_record, $this->_view);
-  }
-
-
-  public function getOsmData() {
-    return null;
-  }
-
-
-  public function getModelId() {
-    $id = ($user =  $this->_model->getUser())
-      ? $user->getId()
-      : 0;
-
-    return [$this->_operation, $id];
-  }
-
-
-  public function getModelClassname() {
-    return get_class($this);
-  }
-
-
-  public static function find($id) {
-    return new Class_User_CardsOperationDecorator($id[0], Class_Users::find($id[1]));
-  }
 }
diff --git a/library/templates/Intonation/Library/View/Wrapper/MenuEntry.php b/library/templates/Intonation/Library/View/Wrapper/MenuEntry.php
index 50432761b90..ead6369accc 100644
--- a/library/templates/Intonation/Library/View/Wrapper/MenuEntry.php
+++ b/library/templates/Intonation/Library/View/Wrapper/MenuEntry.php
@@ -128,10 +128,11 @@ class Intonation_Library_View_Wrapper_MenuEntry extends Intonation_Library_View_
   }
 
 
-  public function getModelId() {
-    return [$this->_model->getId(),
-            $this->_model->getParent(),
-            $this->_model->getProfileId()];
+  protected function _getModelId() {
+    return $this->_model_id =
+      json_encode([$this->_model->getId(),
+                   $this->_model->getParent(),
+                   $this->_model->getProfileId()]);
   }
 
 
@@ -141,6 +142,7 @@ class Intonation_Library_View_Wrapper_MenuEntry extends Intonation_Library_View_
 
 
   public static function find($id) {
+    $id = json_decode($id);
     return (new Class_Systeme_Widget_Menu)
       ->setId($id[0])
       ->setParent($id[1])
diff --git a/library/templates/Intonation/Library/View/Wrapper/PNBHold.php b/library/templates/Intonation/Library/View/Wrapper/PNBHold.php
index 8bf27a32bd1..3c30bd81cc6 100644
--- a/library/templates/Intonation/Library/View/Wrapper/PNBHold.php
+++ b/library/templates/Intonation/Library/View/Wrapper/PNBHold.php
@@ -63,7 +63,7 @@ class Intonation_Library_View_Wrapper_PNBHold extends Intonation_Library_View_Wr
     return [(new Intonation_Library_Link)
             ->setUrl($this->_view->url(['controller' => 'abonne',
                                         'action' => 'delete-pnb-hold',
-                                        'id' => $this->_getModelId()]))
+                                        'id' => $this->_getOperationId()]))
             ->setImage($this->getIco('delete', 'utils'))
             ->setText($this->_('Supprimer'))
             ->setTitle($this->_('Supprimer la réservation du document %s', $this->_model->getTitre()))
@@ -73,23 +73,14 @@ class Intonation_Library_View_Wrapper_PNBHold extends Intonation_Library_View_Wr
   }
 
 
-  private function _getModelId() {
-    return $this->_model->getOperationId();
-  }
-
-
-  public function getModelId() {
-    $id = ($user =  $this->_model->getUser())
-      ? $user->getId()
-      : 0;
-
-    return [$this->_model->getOperationId(), $id];
+  protected function _getModelId() {
+    return $this->_getModelIdAsJson();
   }
 
 
   public static function find($id) {
-    return new Class_User_CardsOperationDecorator(Class_Hold_Pnb::findFirstBy(['id' => $id[0],
-                                                                               'user_id' => $id[1]]),
+    $id = json_decode($id);
+    return new Class_User_CardsOperationDecorator(Class_Hold_Pnb::find($id[0]),
                                                   Class_Users::find($id[1]));
   }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/Library/View/Wrapper/PNBLoan.php b/library/templates/Intonation/Library/View/Wrapper/PNBLoan.php
index 2e61e8b5583..3440a09153f 100644
--- a/library/templates/Intonation/Library/View/Wrapper/PNBLoan.php
+++ b/library/templates/Intonation/Library/View/Wrapper/PNBLoan.php
@@ -30,13 +30,6 @@ class Intonation_Library_View_Wrapper_PNBLoan extends Intonation_Library_View_Wr
   }
 
 
-  public function getSecondaryTitle() {
-    return ($record = $this->_getRecord())
-      ? $record->getSecondaryTitle()
-      : '';
-  }
-
-
   public function getBadges() {
     $issue_date = $this->_model->getIssueDate();
     $return_date = $this->_model->getDateRetourISO8601();
@@ -119,19 +112,15 @@ class Intonation_Library_View_Wrapper_PNBLoan extends Intonation_Library_View_Wr
   }
 
 
-  public function getModelId() {
-    $id = ($user =  $this->_model->getUser())
-      ? $user->getId()
-      : 0;
-
-    return [$this->_model->getOperationId(), $id];
+  protected function _getModelId() {
+    return $this->_getModelIdAsJson();
   }
 
 
 
   public static function find($id) {
-    return new Class_User_CardsOperationDecorator(Class_Loan_Pnb::findFirstBy(['id' => $id[0],
-                                                                               'user_id' => $id[1]]),
+    $id = json_decode($id);
+    return new Class_User_CardsOperationDecorator(Class_Loan_Pnb::find($id[0]),
                                                   Class_Users::find($id[1]));
   }
 }
diff --git a/library/templates/Intonation/Library/View/Wrapper/ReviewsByRecord.php b/library/templates/Intonation/Library/View/Wrapper/ReviewsByRecord.php
index 3c086dba837..7f31e47b68c 100644
--- a/library/templates/Intonation/Library/View/Wrapper/ReviewsByRecord.php
+++ b/library/templates/Intonation/Library/View/Wrapper/ReviewsByRecord.php
@@ -154,16 +154,16 @@ class Intonation_Library_View_Wrapper_ReviewsByRecord extends Intonation_Library
   }
 
 
-  public function getModelId() {
+  protected function _getModelId() {
     $ids = [];
 
     if ($this->_record)
-      $ids = [$this->_record->getModelId()];
+      $ids = [$this->_record->getId()];
 
     foreach ( $this->_reviews as $review)
-      $ids [][] = $review->getModelId();
+      $ids [][] = $review->getId();
 
-    return $ids;
+    return $this->_model_id = json_encode($ids);
   }
 
 
@@ -173,6 +173,8 @@ class Intonation_Library_View_Wrapper_ReviewsByRecord extends Intonation_Library
 
 
   public static function find($ids) {
+    $ids = json_decode($ids);
+
     $record_data = array_slice($ids, 0, 1, true);
     $record = Class_Notice::find(array_shift($record_data));
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/Selection.php b/library/templates/Intonation/Library/View/Wrapper/Selection.php
index a35b3a041bc..cf56b0c13e3 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Selection.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Selection.php
@@ -101,7 +101,7 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
     return (new Intonation_Library_Link)
       ->setUrl($this->_view->url(['controller' => 'recherche',
                                   'action' => 'simple',
-                                  'id_panier' => $this->getModelId()],
+                                  'id_panier' => $this->getId()],
                                  null, true))
       ->setImage($this->getIco('search_more', 'library'))
       ->setText($this->_('Voir'))
diff --git a/library/templates/Intonation/Library/View/Wrapper/Suggestion.php b/library/templates/Intonation/Library/View/Wrapper/Suggestion.php
index a9b7c57c16a..80183a0b71c 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Suggestion.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Suggestion.php
@@ -201,16 +201,17 @@ class Intonation_Library_View_Wrapper_Suggestion extends Intonation_Library_View
   }
 
 
-  public function getModelId() {
+  protected function _getModelId() {
     $id = ($user =  $this->_model->getUser())
       ? $user->getId()
       : 0;
 
-    return [$this->_model->getOperationId(), $id];
+    return $this->_model_id = json_encode([$this->_model->getOperationId(), $id]);
   }
 
 
   public static function find($id) {
+    $id = json_decode($id);
     return new Class_User_CardsOperationDecorator(Class_SuggestionAchat::findFirstBy(['id' => $id[0],
                                                                                       'user_id' => $id[1]]),
                                                   Class_Users::find($id[1]));
diff --git a/library/templates/Intonation/Library/View/Wrapper/Work.php b/library/templates/Intonation/Library/View/Wrapper/Work.php
index 487e53d83de..ee0b5d73b93 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Work.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Work.php
@@ -173,8 +173,8 @@ class Intonation_Library_View_Wrapper_Work extends Intonation_Library_View_Wrapp
   }
 
 
-  public function getModelId() {
-    return ($model = $this->_model->getRecord())
+  protected function _getModelId() {
+    return $this->_model_id = ($model = $this->_model->getRecord())
       ? $model->getClefOeuvre()
       : '';
   }
diff --git a/tests/scenarios/Templates/TemplatesAbonneTest.php b/tests/scenarios/Templates/TemplatesAbonneTest.php
index d28a715c678..e6d12284434 100644
--- a/tests/scenarios/Templates/TemplatesAbonneTest.php
+++ b/tests/scenarios/Templates/TemplatesAbonneTest.php
@@ -309,7 +309,7 @@ class TemplatesDispatchAbonneLoansPaginatedTest extends TemplatesIntonationAccou
     $id = $helper->getId();
 
     $this->dispatch('/opac/index/ajax-paginated-list/id/' . $id . '/page/2/render/ajax/id_profil/72');
-    $this->assertContains('input_99914b932bd37a50b983c5e7c90ae93b', $this->_response->getBody());
+    $this->assertContains('input_ba399369f4582ca10ca7b1651defce91', $this->_response->getBody());
   }
 
 
@@ -365,7 +365,7 @@ class TemplatesDispatchAbonneLoansPaginatedTest extends TemplatesIntonationAccou
     $id = $helper->getId();
 
     $this->dispatch('/opac/index/ajax-paginated-list/id/' . $id . '/page/2/render/ajax/id_profil/72/size/1/search/roi');
-    $this->assertContains('input_99914b932bd37a50b983c5e7c90ae93b', $this->_response->getBody());
+    $this->assertContains('input_db436b32a1026df5398c4c8e32b1c51b', $this->_response->getBody());
     $this->assertContains('avec sa couronne', $this->_response->getBody());
   }
 
@@ -396,8 +396,8 @@ class TemplatesDispatchAbonneLoansPaginatedTest extends TemplatesIntonationAccou
 
     $this->dispatch('/opac/index/ajax-paginated-list/id/' . $id . '/page/1/id_profil/72/search/house/size/10');
     $this->assertXPathContentContains('//div//a', 'Dr House');
-    $this->assertXPathContentContains('//div//a[contains(@href, "/bib-numerique/return-pnb-loan/id/99914b932bd37a50b983c5e7c90ae93b/page/1/id_profil/72/search/house/size/10/loan_id/666_3")]', 'Retour anticip', $this->_response->getBody());
-    $this->assertXPathContentContains('//div//a[contains(@href, "/bib-numerique/extends-pnb-loan/id/99914b932bd37a50b983c5e7c90ae93b/page/1/id_profil/72/search/house/size/10/loan_id/666_3")]', 'Prolonger');
+    $this->assertXPathContentContains('//div//a[contains(@href, "/bib-numerique/return-pnb-loan/id/ec2c17cefacacad7f25f875ae87956c1/page/1/id_profil/72/search/house/size/10/loan_id/666_3")]', 'Retour anticip', $this->_response->getBody());
+    $this->assertXPathContentContains('//div//a[contains(@href, "/bib-numerique/extends-pnb-loan/id/ec2c17cefacacad7f25f875ae87956c1/page/1/id_profil/72/search/house/size/10/loan_id/666_3")]', 'Prolonger');
   }
 
 
diff --git a/tests/scenarios/Templates/TemplatesAjaxPaginatedListTest.php b/tests/scenarios/Templates/TemplatesAjaxPaginatedListTest.php
index 55b794a69d3..0a5327e72b3 100644
--- a/tests/scenarios/Templates/TemplatesAjaxPaginatedListTest.php
+++ b/tests/scenarios/Templates/TemplatesAjaxPaginatedListTest.php
@@ -109,7 +109,7 @@ abstract class TemplatesAjaxPaginatedListTestCase extends AbstractControllerTest
     $data = $this->_ajax_paginated_list_helper->getDataToCache();
     $serialized = serialize($data);
     $size = mb_strlen($serialized);
-    $this->assertTrue('1024' > $size);
+    $this->assertTrue(1024 > $size);
   }
 
 
@@ -263,7 +263,7 @@ class TemplatesAjaxPaginatedListWithHoldTest extends TemplatesAjaxPaginatedListT
     $data = $this->_ajax_paginated_list_helper->getDataToCache();
     $serialized = serialize($data);
     $size = mb_strlen($serialized);
-    $this->assertTrue('1524' > $size);
+    $this->assertTrue(3072 > $size);
   }
 }
 
@@ -331,7 +331,7 @@ class TemplatesAjaxPaginatedListWithLoanTest extends TemplatesAjaxPaginatedListT
     $data = $this->_ajax_paginated_list_helper->getDataToCache();
     $serialized = serialize($data);
     $size = mb_strlen($serialized);
-    $this->assertTrue('1524' > $size);
+    $this->assertTrue(2048 > $size);
   }
 }
 
@@ -605,7 +605,7 @@ class TemplatesAjaxPaginatedListWithRssItemTest extends TemplatesAjaxPaginatedLi
     $data = $this->_ajax_paginated_list_helper->getDataToCache();
     $serialized = serialize($data);
     $size = mb_strlen($serialized);
-    $this->assertTrue('1524' > $size);
+    $this->assertTrue(1524 > $size);
   }
 }
 
@@ -906,6 +906,63 @@ class TemplatesAjaxPaginatedListWithLoanAndSearchTest extends TemplatesAjaxPagin
     $data = $this->_ajax_paginated_list_helper->getDataToCache();
     $serialized = serialize($data);
     $size = mb_strlen($serialized);
-    $this->assertTrue('1524' > $size);
+    $this->assertTrue(2048 > $size);
+  }
+}
+
+
+
+
+class TemplatesAjaxPaginatedListWithSameCollectionIdsTest extends AbstractControllerTestCase {
+
+  protected $_storm_default_to_volatile = true;
+
+
+  /** @test */
+  public function withDifferentClassesCacheKeyShouldBeDifferent() {
+    $records = [$this->fixture(Class_Notice::class, ['id' => 1]),
+                $this->fixture(Class_Notice::class, ['id' => 2])];
+
+    $items = [$this->fixture(Class_Exemplaire::class, ['id' => 1]),
+                $this->fixture(Class_Exemplaire::class, ['id' => 2])];
+
+    $key_1 = (new Intonation_Library_AjaxPaginatedListHelper)
+      ->setCollection(new Storm_Collection($records))
+      ->getId();
+
+    $key_2 = (new Intonation_Library_AjaxPaginatedListHelper)
+      ->setCollection(new Storm_Collection($items))
+      ->getId();
+
+    $this->assertNotEquals($key_1, $key_2);
+  }
+
+
+
+  /** @test */
+  public function withDifferentIdCacheKeyShouldBeDifferent() {
+    $user = $this->fixture(Class_Users::class,
+                           ['id' => 1,
+                            'login'=> 'log',
+                            'password' => 'pass']);
+
+    $item_1 = new Class_WebService_SIGB_Exemplaire(1);
+    $loan_1 = new Class_WebService_SIGB_Emprunt(1, $item_1);
+
+    $item_2 = new Class_WebService_SIGB_Exemplaire(2);
+    $loan_2 = new Class_WebService_SIGB_Emprunt(2, $item_2);
+
+    $card_operation_decorator_1 = new Class_User_CardsOperationDecorator($loan_1, $user);
+    $card_operation_decorator_2 = new Class_User_CardsOperationDecorator($loan_2, $user);
+
+    $key_1 = (new Intonation_Library_AjaxPaginatedListHelper)
+      ->setCollection(new Storm_Collection([new Intonation_Library_View_Wrapper_Loan($card_operation_decorator_1)]))
+      ->getId();
+
+    $key_2 = (new Intonation_Library_AjaxPaginatedListHelper)
+      ->setCollection(new Storm_Collection([new Intonation_Library_View_Wrapper_Loan($card_operation_decorator_2)]))
+      ->getId();
+
+    $this->assertNotEquals($key_1, $key_2);
   }
 }
\ No newline at end of file
diff --git a/tests/scenarios/Templates/TemplatesWebsitesTest.php b/tests/scenarios/Templates/TemplatesWebsitesTest.php
index 8c606bb41b6..cdd7e36e41b 100644
--- a/tests/scenarios/Templates/TemplatesWebsitesTest.php
+++ b/tests/scenarios/Templates/TemplatesWebsitesTest.php
@@ -431,6 +431,6 @@ class TemplatesWebsitesDispatchPaginatedTest extends TemplatesWebsitesFixturesTe
     $id = $helper->getId();
 
     $this->dispatch('/opac/index/ajax-paginated-list/id/' . $id . '/page/1/render/ajax/id_profil/19');
-    $this->assertXPath('//form//input[@name="input_99914b932bd37a50b983c5e7c90ae93b"]');
+    $this->assertXPath('//form//input[@name="input_3bbeeef790e61514feb083341497ce60"]');
   }
 }
\ No newline at end of file
-- 
GitLab