As I first reported an issue/feature request about general entity history, I realized that I actually would need it right now. So I proceeded to make a proof of concept in order to update my issue. But instead of updating the issue I figured that my post better belongs here in a forum where maybe more people could take part of it and I could get some more feedback.
Please see the background of this topic before continuing: #1029708: History table for any entity
First of all I took the system history table as a starting point and split the 'timestamp' column into 'first_read' and 'last_read' in order to determine if the entity has been seen since last update.
$schema['oddbit_entity_history'] = array(
'description' => 'A record of which {users} have read which entities.',
'fields' => array(
'entity_type' => array(
'description' => 'The viewed entity type.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => ''
),
'eid' => array(
'description' => 'The id of the entity.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'uid' => array(
'description' => 'The {users}.uid that read the entity id.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'first_read' => array(
'description' => 'The Unix timestamp at which the first read occurred.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'last_read' => array(
'description' => 'The Unix timestamp at which last read occured.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('entity_type', 'uid', 'eid'),
'indexes' => array(
'idx_oddbit_entity_history_id' => array('eid'),
'idx_oddbit_entity_history_last_read' => array('last_read'),
),
'foreign keys' => array(
'fk_entity_history_user' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
);
After that I added some of functions to get things done in an internal library module for misc tools.
function oddbit_entity_mark_read($entity_type, $entity, $account = NULL) {
global $user;
if (!isset($account) || !is_object($account)) {
$account = $user;
}
list($eid, , ) = entity_extract_ids($entity_type, $entity);
$last_read = oddbit_entity_get_last_read($entity_type, $entity, $account);
$current_timestamp = REQUEST_TIME;
$fields = array(
'last_read' => $current_timestamp,
);
// No previous record of reading this entity
if(!$last_read) {
$fields += array(
'entity_type' => $entity_type,
'eid' => $eid,
'uid' => $account->uid,
'first_read' => $current_timestamp,
);
// Record that the user read this entity
db_insert('oddbit_entity_history')
->fields($fields)
->execute();
}
else {
// Update the latest read for this entity
db_update('oddbit_entity_history')
->fields($fields)
->condition('entity_type', $entity_type)
->condition('uid', $account->uid)
->condition('eid', $eid)
->execute();
}
}
function oddbit_entity_get_last_read($entity_type, $entity, $account = NULL) {
return _oddbit_entity_get_history_timestamp($entity_type, $entity, $account, 'last_read');
}
function oddbit_entity_get_first_read($entity_type, $entity, $account = NULL) {
return _oddbit_entity_get_history_timestamp($entity_type, $entity, $account, 'first_read');
}
function _oddbit_entity_get_history_timestamp($entity_type, $entity, $account, $ts_type) {
global $user;
if (!isset($account) || !is_object($account)) {
$account = $user;
}
list($eid, , ) = entity_extract_ids($entity_type, $entity);
$timestamps = db_select('oddbit_entity_history', 'history')
->fields('history', array($ts_type))
->condition('entity_type', $entity_type)
->condition('eid', $eid)
->condition('uid', $account->uid)
->range(0, 1)
->execute()
->fetchCol();
if (is_array($timestamps) && count($timestamps)) {
return $timestamps[0];
}
return 0;
}
Finally I implemented the hook_node_view() for the module in order to be able skip the system history table completely in my own custom made modules.
function oddbit_node_view($node, $view_mode, $langcode) {
// Use our own improved history table instead of the regular
oddbit_entity_mark_read('node', $node);
}
Just copy/paste it all into your own module of choice and find/replace 'oddbit' with whatever your module is named.
... and it seems to work fine at first glance.