Thanks again Luke.
That explanation on sessions and cookies is good enough for me. Your help has inspired me to come up with this solution storing recently viewed items in a session variable which I am very happy with. I ended up using CList for its add() and remove() methods.
ERecentlyViewedBehaviour.php
/**
* ERecentlyViewedBehavior is a behavior for managing recently viewed model items.
*/
class ERecentlyViewedBehavior extends CBehavior
{
/**
* @param integer the limit to the number of items in the recently viewed list.
*/
var $limit = 0; // 0 = no limit.
/**
* Adds an item id to a 'recently viewed items' session object.
* @var string the modelClass of the item to store
* @param integer the id of the item to store
*/
protected function setRecentlyViewed($modelClass, $id)
{
// Create the session index
$index = $modelClass.'_recently_viewed';
// Check if the session index exists
if (!isset(Yii::app()->session[$index]))
{
$recentlyViewed = new CList();
}
else
{
$recentlyViewed = Yii::app()->session[$index];
// Remove the id if it is already in the list
if ($recentlyViewed->contains($id))
{
$recentlyViewed->remove($id);
}
// If a limit is set, and the list is at (or over) the limit, remove oldest item(s)
if ($this->limit > 0 && $recentlyViewed->count() >= $this->limit)
{
$count = $recentlyViewed->count() - $this->limit;
for ($i = 0; $i <= $count; $i++)
{
$recentlyViewed->removeAt(0);
}
}
}
// Add the current item id to the end of the array
$recentlyViewed->add($id);
// Update the session
Yii::app()->session[$index] = $recentlyViewed;
}
/**
* Retrieves model records from a 'recently viewed items' session object.
* @var string the modelClass of the items to retrieve
*/
protected function getRecentlyViewed($modelClass)
{
// Create the session index
$index = $modelClass.'_recently_viewed';
$models = array();
// Check if the session index exists
if (isset(Yii::app()->session[$index]))
{
$recentlyViewed = Yii::app()->session[$index];
// Check if a limit is set, and if the list is at (or over) the limit
if ($this->limit > 0 && $recentlyViewed->count() >= $this->limit)
{
$count = $recentlyViewed->count() - $this->limit;
// Remove the oldest item(s) (always an index of 0 after each removal)
for ($i = 0; $i < $count; $i++)
{
$recentlyViewed->removeAt(0);
}
}
// Convert the CList object stored in the session to an array
$recentlyViewed = $recentlyViewed->toArray();
// Reverse the array so the most recently added item is first
$recentlyViewed = array_reverse($recentlyViewed);
// Create a comma separated list for the db order property
$recentlyViewedCommaSeparated = implode(',', $recentlyViewed);
// Find all of the models with the array of ids recently viewed
// and order the results in the same order as the array
$criteria = new CDbCriteria;
$criteria->order = "FIELD(id, $recentlyViewedCommaSeparated)"; // MySQL function
$models = CActiveRecord::model($modelClass)->findAllByPk($recentlyViewed, $criteria);
}
return $models;
}
}
In your controller, attach the behavior…
/**
* Controller behaviors
*/
public function behaviors()
{
return array(
'recentlyViewed'=>array(
'class'=>'ext.behaviors.ERecentlyViewedBehavior', // Location of the behavior class
'limit'=>5, // Limit the number of recently viewed items stored. 0 = no limit.
),
);
}
Then, in your controller action(s) where you want to set or get the Recently Viewed list…
To add an item to the Recently Viewed list - use the $this->setRecentlyViewed($modelClass, $id) method, where $modelClass is a string of the model class name (i.e. ‘Post’, or get the model class name with get_class($model)), and $id is the id of the viewed model item that you want added to the list;
To retrieve the model records from the Recently Viewed list - use the $this->getRecentlyViewed($modelClass, $id) method, where $modelClass is a string of the model class name (i.e. ‘Post’, or get the model class name get_class($model)), to return an object with all of the model records in the list;
public function actionView($id)
{
$model = $this->loadModel($id);
$modelClass = get_class($model);
// Add this item to the recently viewed list
$this->setRecentlyViewed($modelClass, $id);
// Retrieve the recently viewed list
$recentlyViewedPosts = $this->getRecentlyViewed($modelClass);
$this->render('/post/view', array(
'model'=>$model,
'recentlyViewedPosts'=>$recentlyViewedPosts,
));
}
Finally in your View file, you can loop through the model records like any other object…
<?php
foreach ($recentlyViewedPosts as $recentlyViewedPost)
{
echo 'ID: '.$recentlyViewedPost->id.'<br />';
echo 'Title: '.$recentlyViewedPost->title.'<br />';
}
?>
I hope this is helpful to someone. Isn’t Yii just the best?! 