Issue #1184944 by fago, xjm, klausi, aspilicious, bojanz, Tor Arne Thune: Make entities classed objects, introduce CRUD support.
parent
faddd57042
commit
694b6ac9c8
|
@ -0,0 +1,280 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity controller and class for comments.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the comment entity class.
|
||||
*/
|
||||
class Comment extends Entity {
|
||||
|
||||
/**
|
||||
* The comment ID.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $cid;
|
||||
|
||||
/**
|
||||
* The parent comment ID if this is a reply to a comment.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $pid;
|
||||
|
||||
/**
|
||||
* The comment language.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $language = LANGUAGE_NONE;
|
||||
|
||||
/**
|
||||
* The comment title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $subject;
|
||||
|
||||
|
||||
/**
|
||||
* The comment author ID.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $uid = 0;
|
||||
|
||||
/**
|
||||
* The comment author's name.
|
||||
*
|
||||
* For anonymous authors, this is the value as typed in the comment form.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* The comment author's e-mail address.
|
||||
*
|
||||
* For anonymous authors, this is the value as typed in the comment form.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $mail;
|
||||
|
||||
/**
|
||||
* The comment author's home page address.
|
||||
*
|
||||
* For anonymous authors, this is the value as typed in the comment form.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $homepage;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the controller class for comments.
|
||||
*
|
||||
* This extends the EntityDatabaseStorageController class, adding required
|
||||
* special handling for comment entities.
|
||||
*/
|
||||
class CommentStorageController extends EntityDatabaseStorageController {
|
||||
|
||||
/**
|
||||
* Overrides EntityDatabaseStorageController::buildQuery().
|
||||
*/
|
||||
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
|
||||
$query = parent::buildQuery($ids, $conditions, $revision_id);
|
||||
// Specify additional fields from the user and node tables.
|
||||
$query->innerJoin('node', 'n', 'base.nid = n.nid');
|
||||
$query->addField('n', 'type', 'node_type');
|
||||
$query->innerJoin('users', 'u', 'base.uid = u.uid');
|
||||
$query->addField('u', 'name', 'registered_name');
|
||||
$query->fields('u', array('uid', 'signature', 'signature_format', 'picture'));
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityDatabaseStorageController::attachLoad().
|
||||
*/
|
||||
protected function attachLoad(&$comments, $revision_id = FALSE) {
|
||||
// Set up standard comment properties.
|
||||
foreach ($comments as $key => $comment) {
|
||||
$comment->name = $comment->uid ? $comment->registered_name : $comment->name;
|
||||
$comment->new = node_mark($comment->nid, $comment->changed);
|
||||
$comment->node_type = 'comment_node_' . $comment->node_type;
|
||||
$comments[$key] = $comment;
|
||||
}
|
||||
parent::attachLoad($comments, $revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityDatabaseStorageController::preSave().
|
||||
*
|
||||
* @see comment_int_to_alphadecimal()
|
||||
* @see comment_increment_alphadecimal()
|
||||
*/
|
||||
protected function preSave(EntityInterface $comment) {
|
||||
global $user;
|
||||
|
||||
if (!isset($comment->status)) {
|
||||
$comment->status = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
|
||||
}
|
||||
// Make sure we have a proper bundle name.
|
||||
if (!isset($comment->node_type)) {
|
||||
$node = node_load($comment->nid);
|
||||
$comment->node_type = 'comment_node_' . $node->type;
|
||||
}
|
||||
if (!$comment->cid) {
|
||||
// Add the comment to database. This next section builds the thread field.
|
||||
// Also see the documentation for comment_view().
|
||||
if (!empty($comment->thread)) {
|
||||
// Allow calling code to set thread itself.
|
||||
$thread = $comment->thread;
|
||||
}
|
||||
elseif ($comment->pid == 0) {
|
||||
// This is a comment with no parent comment (depth 0): we start
|
||||
// by retrieving the maximum thread level.
|
||||
$max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid))->fetchField();
|
||||
// Strip the "/" from the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// Finally, build the thread field for this new comment.
|
||||
$thread = comment_increment_alphadecimal($max) . '/';
|
||||
}
|
||||
else {
|
||||
// This is a comment with a parent comment, so increase the part of
|
||||
// the thread value at the proper depth.
|
||||
|
||||
// Get the parent comment:
|
||||
$parent = comment_load($comment->pid);
|
||||
// Strip the "/" from the end of the parent thread.
|
||||
$parent->thread = (string) rtrim((string) $parent->thread, '/');
|
||||
// Get the max value in *this* thread.
|
||||
$max = db_query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
|
||||
':thread' => $parent->thread . '.%',
|
||||
':nid' => $comment->nid,
|
||||
))->fetchField();
|
||||
|
||||
if ($max == '') {
|
||||
// First child of this parent.
|
||||
$thread = $parent->thread . '.' . comment_int_to_alphadecimal(0) . '/';
|
||||
}
|
||||
else {
|
||||
// Strip the "/" at the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// Get the value at the correct depth.
|
||||
$parts = explode('.', $max);
|
||||
$parent_depth = count(explode('.', $parent->thread));
|
||||
$last = $parts[$parent_depth];
|
||||
// Finally, build the thread field for this new comment.
|
||||
$thread = $parent->thread . '.' . comment_increment_alphadecimal($last) . '/';
|
||||
}
|
||||
}
|
||||
if (empty($comment->created)) {
|
||||
$comment->created = REQUEST_TIME;
|
||||
}
|
||||
if (empty($comment->changed)) {
|
||||
$comment->changed = $comment->created;
|
||||
}
|
||||
// We test the value with '===' because we need to modify anonymous
|
||||
// users as well.
|
||||
if ($comment->uid === $user->uid && isset($user->name)) {
|
||||
$comment->name = $user->name;
|
||||
}
|
||||
// Add the values which aren't passed into the function.
|
||||
$comment->thread = $thread;
|
||||
$comment->hostname = ip_address();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityDatabaseStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $comment) {
|
||||
// Update the {node_comment_statistics} table prior to executing the hook.
|
||||
$this->updateNodeStatistics($comment->nid);
|
||||
if ($comment->status == COMMENT_PUBLISHED) {
|
||||
module_invoke_all('comment_publish', $comment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides EntityDatabaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($comments) {
|
||||
// Delete the comments' replies.
|
||||
$query = db_select('comment', 'c')
|
||||
->fields('c', array('cid'))
|
||||
->condition('pid', array(array_keys($comments)), 'IN');
|
||||
$child_cids = $query->execute()->fetchCol();
|
||||
comment_delete_multiple($child_cids);
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
$this->updateNodeStatistics($comment->nid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the comment statistics for a given node.
|
||||
*
|
||||
* The {node_comment_statistics} table has the following fields:
|
||||
* - last_comment_timestamp: The timestamp of the last comment for this node,
|
||||
* or the node created timestamp if no comments exist for the node.
|
||||
* - last_comment_name: The name of the anonymous poster for the last comment.
|
||||
* - last_comment_uid: The user ID of the poster for the last comment for
|
||||
* this node, or the node author's user ID if no comments exist for the
|
||||
* node.
|
||||
* - comment_count: The total number of approved/published comments on this
|
||||
* node.
|
||||
*
|
||||
* @param $nid
|
||||
* The node ID.
|
||||
*/
|
||||
protected function updateNodeStatistics($nid) {
|
||||
// Allow bulk updates and inserts to temporarily disable the
|
||||
// maintenance of the {node_comment_statistics} table.
|
||||
if (!variable_get('comment_maintain_node_statistics', TRUE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$count = db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND status = :status', array(
|
||||
':nid' => $nid,
|
||||
':status' => COMMENT_PUBLISHED,
|
||||
))->fetchField();
|
||||
|
||||
if ($count > 0) {
|
||||
// Comments exist.
|
||||
$last_reply = db_query_range('SELECT cid, name, changed, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
|
||||
':nid' => $nid,
|
||||
':status' => COMMENT_PUBLISHED,
|
||||
))->fetchObject();
|
||||
db_update('node_comment_statistics')
|
||||
->fields(array(
|
||||
'cid' => $last_reply->cid,
|
||||
'comment_count' => $count,
|
||||
'last_comment_timestamp' => $last_reply->changed,
|
||||
'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
|
||||
'last_comment_uid' => $last_reply->uid,
|
||||
))
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
// Comments do not exist.
|
||||
$node = db_query('SELECT uid, created FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
|
||||
db_update('node_comment_statistics')
|
||||
->fields(array(
|
||||
'cid' => 0,
|
||||
'comment_count' => 0,
|
||||
'last_comment_timestamp' => $node->created,
|
||||
'last_comment_name' => '',
|
||||
'last_comment_uid' => $node->uid,
|
||||
))
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ version = VERSION
|
|||
core = 8.x
|
||||
dependencies[] = text
|
||||
dependencies[] = entity
|
||||
files[] = comment.module
|
||||
files[] = comment.entity.inc
|
||||
files[] = comment.test
|
||||
configure = admin/content/comment
|
||||
stylesheets[all][] = comment.css
|
||||
|
|
|
@ -98,7 +98,8 @@ function comment_entity_info() {
|
|||
'base table' => 'comment',
|
||||
'uri callback' => 'comment_uri',
|
||||
'fieldable' => TRUE,
|
||||
'controller class' => 'CommentController',
|
||||
'controller class' => 'CommentStorageController',
|
||||
'entity class' => 'Comment',
|
||||
'entity keys' => array(
|
||||
'id' => 'cid',
|
||||
'bundle' => 'node_type',
|
||||
|
@ -738,8 +739,8 @@ function comment_node_page_additions($node) {
|
|||
|
||||
// Append comment form if needed.
|
||||
if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) {
|
||||
$build = drupal_get_form("comment_node_{$node->type}_form", (object) array('nid' => $node->nid));
|
||||
$additions['comment_form'] = $build;
|
||||
$comment = entity_create('comment', array('nid' => $node->nid));
|
||||
$additions['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment);
|
||||
}
|
||||
|
||||
if ($additions) {
|
||||
|
@ -1436,151 +1437,9 @@ function comment_access($op, $comment) {
|
|||
*
|
||||
* @param $comment
|
||||
* A comment object.
|
||||
*
|
||||
* @see comment_int_to_alphadecimal()
|
||||
*/
|
||||
function comment_save($comment) {
|
||||
global $user;
|
||||
|
||||
$transaction = db_transaction();
|
||||
try {
|
||||
$defaults = array(
|
||||
'mail' => '',
|
||||
'homepage' => '',
|
||||
'name' => '',
|
||||
'status' => user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED,
|
||||
);
|
||||
foreach ($defaults as $key => $default) {
|
||||
if (!isset($comment->$key)) {
|
||||
$comment->$key = $default;
|
||||
}
|
||||
}
|
||||
// Make sure we have a bundle name.
|
||||
if (!isset($comment->node_type)) {
|
||||
$node = node_load($comment->nid);
|
||||
$comment->node_type = 'comment_node_' . $node->type;
|
||||
}
|
||||
|
||||
// Load the stored entity, if any.
|
||||
if (!empty($comment->cid) && !isset($comment->original)) {
|
||||
$comment->original = entity_load_unchanged('comment', $comment->cid);
|
||||
}
|
||||
|
||||
field_attach_presave('comment', $comment);
|
||||
|
||||
// Allow modules to alter the comment before saving.
|
||||
module_invoke_all('comment_presave', $comment);
|
||||
module_invoke_all('entity_presave', $comment, 'comment');
|
||||
|
||||
if ($comment->cid) {
|
||||
|
||||
drupal_write_record('comment', $comment, 'cid');
|
||||
|
||||
// Ignore slave server temporarily to give time for the
|
||||
// saved comment to be propagated to the slave.
|
||||
db_ignore_slave();
|
||||
|
||||
// Update the {node_comment_statistics} table prior to executing hooks.
|
||||
_comment_update_node_statistics($comment->nid);
|
||||
|
||||
field_attach_update('comment', $comment);
|
||||
// Allow modules to respond to the updating of a comment.
|
||||
module_invoke_all('comment_update', $comment);
|
||||
module_invoke_all('entity_update', $comment, 'comment');
|
||||
}
|
||||
else {
|
||||
// Add the comment to database. This next section builds the thread field.
|
||||
// Also see the documentation for comment_view().
|
||||
if (!empty($comment->thread)) {
|
||||
// Allow calling code to set thread itself.
|
||||
$thread = $comment->thread;
|
||||
}
|
||||
elseif ($comment->pid == 0) {
|
||||
// This is a comment with no parent comment (depth 0): we start
|
||||
// by retrieving the maximum thread level.
|
||||
$max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid))->fetchField();
|
||||
// Strip the "/" from the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// Finally, build the thread field for this new comment.
|
||||
$thread = comment_increment_alphadecimal($max) . '/';
|
||||
}
|
||||
else {
|
||||
// This is a comment with a parent comment, so increase the part of the
|
||||
// thread value at the proper depth.
|
||||
|
||||
// Get the parent comment:
|
||||
$parent = comment_load($comment->pid);
|
||||
// Strip the "/" from the end of the parent thread.
|
||||
$parent->thread = (string) rtrim((string) $parent->thread, '/');
|
||||
// Get the max value in *this* thread.
|
||||
$max = db_query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
|
||||
':thread' => $parent->thread . '.%',
|
||||
':nid' => $comment->nid,
|
||||
))->fetchField();
|
||||
|
||||
if ($max == '') {
|
||||
// First child of this parent.
|
||||
$thread = $parent->thread . '.' . comment_int_to_alphadecimal(0) . '/';
|
||||
}
|
||||
else {
|
||||
// Strip the "/" at the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// Get the value at the correct depth.
|
||||
$parts = explode('.', $max);
|
||||
$parent_depth = count(explode('.', $parent->thread));
|
||||
$last = $parts[$parent_depth];
|
||||
// Finally, build the thread field for this new comment.
|
||||
$thread = $parent->thread . '.' . comment_increment_alphadecimal($last) . '/';
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($comment->created)) {
|
||||
$comment->created = REQUEST_TIME;
|
||||
}
|
||||
|
||||
if (empty($comment->changed)) {
|
||||
$comment->changed = $comment->created;
|
||||
}
|
||||
|
||||
if ($comment->uid === $user->uid && isset($user->name)) { // '===' Need to modify anonymous users as well.
|
||||
$comment->name = $user->name;
|
||||
}
|
||||
|
||||
// Ensure the parent id (pid) has a value set.
|
||||
if (empty($comment->pid)) {
|
||||
$comment->pid = 0;
|
||||
}
|
||||
|
||||
// Add the values which aren't passed into the function.
|
||||
$comment->thread = $thread;
|
||||
$comment->hostname = ip_address();
|
||||
|
||||
drupal_write_record('comment', $comment);
|
||||
|
||||
// Ignore slave server temporarily to give time for the
|
||||
// created comment to be propagated to the slave.
|
||||
db_ignore_slave();
|
||||
|
||||
// Update the {node_comment_statistics} table prior to executing hooks.
|
||||
_comment_update_node_statistics($comment->nid);
|
||||
|
||||
field_attach_insert('comment', $comment);
|
||||
|
||||
// Tell the other modules a new comment has been submitted.
|
||||
module_invoke_all('comment_insert', $comment);
|
||||
module_invoke_all('entity_insert', $comment, 'comment');
|
||||
}
|
||||
if ($comment->status == COMMENT_PUBLISHED) {
|
||||
module_invoke_all('comment_publish', $comment);
|
||||
}
|
||||
unset($comment->original);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$transaction->rollback('comment');
|
||||
watchdog_exception('comment', $e);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$comment->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1600,31 +1459,7 @@ function comment_delete($cid) {
|
|||
* The comment to delete.
|
||||
*/
|
||||
function comment_delete_multiple($cids) {
|
||||
$comments = comment_load_multiple($cids);
|
||||
if ($comments) {
|
||||
$transaction = db_transaction();
|
||||
try {
|
||||
// Delete the comments.
|
||||
db_delete('comment')
|
||||
->condition('cid', array_keys($comments), 'IN')
|
||||
->execute();
|
||||
foreach ($comments as $comment) {
|
||||
field_attach_delete('comment', $comment);
|
||||
module_invoke_all('comment_delete', $comment);
|
||||
module_invoke_all('entity_delete', $comment, 'comment');
|
||||
|
||||
// Delete the comment's replies.
|
||||
$child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $comment->cid))->fetchCol();
|
||||
comment_delete_multiple($child_cids);
|
||||
_comment_update_node_statistics($comment->nid);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$transaction->rollback();
|
||||
watchdog_exception('comment', $e);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
entity_delete_multiple('comment', $cids);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1671,37 +1506,6 @@ function comment_load($cid, $reset = FALSE) {
|
|||
return $comment ? $comment[$cid] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller class for comments.
|
||||
*
|
||||
* This extends the DrupalDefaultEntityController class, adding required
|
||||
* special handling for comment objects.
|
||||
*/
|
||||
class CommentController extends DrupalDefaultEntityController {
|
||||
|
||||
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
|
||||
$query = parent::buildQuery($ids, $conditions, $revision_id);
|
||||
// Specify additional fields from the user and node tables.
|
||||
$query->innerJoin('node', 'n', 'base.nid = n.nid');
|
||||
$query->addField('n', 'type', 'node_type');
|
||||
$query->innerJoin('users', 'u', 'base.uid = u.uid');
|
||||
$query->addField('u', 'name', 'registered_name');
|
||||
$query->fields('u', array('uid', 'signature', 'signature_format', 'picture'));
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function attachLoad(&$comments, $revision_id = FALSE) {
|
||||
// Setup standard comment properties.
|
||||
foreach ($comments as $key => $comment) {
|
||||
$comment->name = $comment->uid ? $comment->registered_name : $comment->name;
|
||||
$comment->new = node_mark($comment->nid, $comment->changed);
|
||||
$comment->node_type = 'comment_node_' . $comment->node_type;
|
||||
$comments[$key] = $comment;
|
||||
}
|
||||
parent::attachLoad($comments, $revision_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of new comments for current user and specified node.
|
||||
*
|
||||
|
@ -1831,22 +1635,6 @@ function comment_form($form, &$form_state, $comment) {
|
|||
// use during form building and processing. During a rebuild, use what is in
|
||||
// the form state.
|
||||
if (!isset($form_state['comment'])) {
|
||||
$defaults = array(
|
||||
'name' => '',
|
||||
'mail' => '',
|
||||
'homepage' => '',
|
||||
'subject' => '',
|
||||
'comment' => '',
|
||||
'cid' => NULL,
|
||||
'pid' => NULL,
|
||||
'language' => LANGUAGE_NONE,
|
||||
'uid' => 0,
|
||||
);
|
||||
foreach ($defaults as $key => $value) {
|
||||
if (!isset($comment->$key)) {
|
||||
$comment->$key = $value;
|
||||
}
|
||||
}
|
||||
$form_state['comment'] = $comment;
|
||||
}
|
||||
else {
|
||||
|
@ -2143,12 +1931,6 @@ function comment_form_validate($form, &$form_state) {
|
|||
* Prepare a comment for submission.
|
||||
*/
|
||||
function comment_submit($comment) {
|
||||
// @todo Legacy support. Remove in Drupal 8.
|
||||
if (is_array($comment)) {
|
||||
$comment += array('subject' => '');
|
||||
$comment = (object) $comment;
|
||||
}
|
||||
|
||||
if (empty($comment->date)) {
|
||||
$comment->date = 'now';
|
||||
}
|
||||
|
@ -2398,61 +2180,6 @@ function _comment_per_page() {
|
|||
return drupal_map_assoc(array(10, 30, 50, 70, 90, 150, 200, 250, 300));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the comment statistics for a given node. This should be called any
|
||||
* time a comment is added, deleted, or updated.
|
||||
*
|
||||
* The following fields are contained in the node_comment_statistics table.
|
||||
* - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
|
||||
* - last_comment_name: the name of the anonymous poster for the last comment
|
||||
* - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
|
||||
* - comment_count: the total number of approved/published comments on this node.
|
||||
*/
|
||||
function _comment_update_node_statistics($nid) {
|
||||
// Allow bulk updates and inserts to temporarily disable the
|
||||
// maintenance of the {node_comment_statistics} table.
|
||||
if (!variable_get('comment_maintain_node_statistics', TRUE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$count = db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND status = :status', array(
|
||||
':nid' => $nid,
|
||||
':status' => COMMENT_PUBLISHED,
|
||||
))->fetchField();
|
||||
|
||||
if ($count > 0) {
|
||||
// Comments exist.
|
||||
$last_reply = db_query_range('SELECT cid, name, changed, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
|
||||
':nid' => $nid,
|
||||
':status' => COMMENT_PUBLISHED,
|
||||
))->fetchObject();
|
||||
db_update('node_comment_statistics')
|
||||
->fields(array(
|
||||
'cid' => $last_reply->cid,
|
||||
'comment_count' => $count,
|
||||
'last_comment_timestamp' => $last_reply->changed,
|
||||
'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
|
||||
'last_comment_uid' => $last_reply->uid,
|
||||
))
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
// Comments do not exist.
|
||||
$node = db_query('SELECT uid, created FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
|
||||
db_update('node_comment_statistics')
|
||||
->fields(array(
|
||||
'cid' => 0,
|
||||
'comment_count' => 0,
|
||||
'last_comment_timestamp' => $node->created,
|
||||
'last_comment_name' => '',
|
||||
'last_comment_uid' => $node->uid,
|
||||
))
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sorting code.
|
||||
*
|
||||
|
|
|
@ -35,7 +35,8 @@ function comment_reply($node, $pid = NULL) {
|
|||
// The user is previewing a comment prior to submitting it.
|
||||
if ($op == t('Preview')) {
|
||||
if (user_access('post comments')) {
|
||||
$build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", (object) array('pid' => $pid, 'nid' => $node->nid));
|
||||
$comment = entity_create('comment', array('nid' => $node->nid, 'pid' => $pid));
|
||||
$build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('You are not authorized to post comments.'), 'error');
|
||||
|
@ -86,8 +87,8 @@ function comment_reply($node, $pid = NULL) {
|
|||
drupal_goto("node/$node->nid");
|
||||
}
|
||||
elseif (user_access('post comments')) {
|
||||
$edit = array('nid' => $node->nid, 'pid' => $pid);
|
||||
$build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", (object) $edit);
|
||||
$comment = entity_create('comment', array('nid' => $node->nid, 'pid' => $pid));
|
||||
$build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('You are not authorized to post comments.'), 'error');
|
||||
|
|
|
@ -87,7 +87,7 @@ class CommentHelperCase extends DrupalWebTestCase {
|
|||
}
|
||||
|
||||
if (isset($match[1])) {
|
||||
return (object) array('id' => $match[1], 'subject' => $subject, 'comment' => $comment);
|
||||
return entity_create('comment', array('id' => $match[1], 'subject' => $subject, 'comment' => $comment));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ class CommentHelperCase extends DrupalWebTestCase {
|
|||
|
||||
// Create a new comment. This helper function may be run with different
|
||||
// comment settings so use comment_save() to avoid complex setup.
|
||||
$comment = (object) array(
|
||||
$comment = entity_create('comment', array(
|
||||
'cid' => NULL,
|
||||
'nid' => $this->node->nid,
|
||||
'node_type' => $this->node->type,
|
||||
|
@ -280,7 +280,7 @@ class CommentHelperCase extends DrupalWebTestCase {
|
|||
'hostname' => ip_address(),
|
||||
'language' => LANGUAGE_NONE,
|
||||
'comment_body' => array(LANGUAGE_NONE => array($this->randomName())),
|
||||
);
|
||||
));
|
||||
comment_save($comment);
|
||||
$this->drupalLogout();
|
||||
|
||||
|
@ -661,7 +661,7 @@ class CommentInterfaceTest extends CommentHelperCase {
|
|||
if ($info['comment count']) {
|
||||
// Create a comment via CRUD API functionality, since
|
||||
// $this->postComment() relies on actual user permissions.
|
||||
$comment = (object) array(
|
||||
$comment = entity_create('comment', array(
|
||||
'cid' => NULL,
|
||||
'nid' => $this->node->nid,
|
||||
'node_type' => $this->node->type,
|
||||
|
@ -672,7 +672,7 @@ class CommentInterfaceTest extends CommentHelperCase {
|
|||
'hostname' => ip_address(),
|
||||
'language' => LANGUAGE_NONE,
|
||||
'comment_body' => array(LANGUAGE_NONE => array($this->randomName())),
|
||||
);
|
||||
));
|
||||
comment_save($comment);
|
||||
$this->comment = $comment;
|
||||
|
||||
|
@ -1463,7 +1463,7 @@ class CommentApprovalTest extends CommentHelperCase {
|
|||
// Get unapproved comment id.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$anonymous_comment4 = $this->getUnapprovedComment($subject);
|
||||
$anonymous_comment4 = (object) array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body);
|
||||
$anonymous_comment4 = entity_create('comment', array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body));
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->assertFalse($this->commentExists($anonymous_comment4), t('Anonymous comment was not published.'));
|
||||
|
@ -1527,7 +1527,7 @@ class CommentApprovalTest extends CommentHelperCase {
|
|||
// Get unapproved comment id.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$anonymous_comment4 = $this->getUnapprovedComment($subject);
|
||||
$anonymous_comment4 = (object) array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body);
|
||||
$anonymous_comment4 = entity_create('comment', array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body));
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->assertFalse($this->commentExists($anonymous_comment4), t('Anonymous comment was not published.'));
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides an interface and a base class for entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines a common interface for all entity objects.
|
||||
*/
|
||||
interface EntityInterface {
|
||||
|
||||
/**
|
||||
* Constructs a new entity object.
|
||||
*
|
||||
* @param $values
|
||||
* An array of values to set, keyed by property name. If the entity type
|
||||
* has bundles, the bundle key has to be specified.
|
||||
* @param $entity_type
|
||||
* The type of the entity to create.
|
||||
*/
|
||||
public function __construct(array $values, $entity_type);
|
||||
|
||||
/**
|
||||
* Returns the entity identifier (the entity's machine name or numeric ID).
|
||||
*
|
||||
* @return
|
||||
* The identifier of the entity, or NULL if the entity does not yet have
|
||||
* an identifier.
|
||||
*/
|
||||
public function id();
|
||||
|
||||
/**
|
||||
* Returns whether the entity is new.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the entity is new, or FALSE if the entity has already been saved.
|
||||
*/
|
||||
public function isNew();
|
||||
|
||||
/**
|
||||
* Returns the type of the entity.
|
||||
*
|
||||
* @return
|
||||
* The type of the entity.
|
||||
*/
|
||||
public function entityType();
|
||||
|
||||
/**
|
||||
* Returns the bundle of the entity.
|
||||
*
|
||||
* @return
|
||||
* The bundle of the entity. Defaults to the entity type if the entity type
|
||||
* does not make use of different bundles.
|
||||
*/
|
||||
public function bundle();
|
||||
|
||||
/**
|
||||
* Returns the label of the entity.
|
||||
*
|
||||
* @return
|
||||
* The label of the entity, or NULL if there is no label defined.
|
||||
*/
|
||||
public function label();
|
||||
|
||||
/**
|
||||
* Returns the URI elements of the entity.
|
||||
*
|
||||
* @return
|
||||
* An array containing the 'path' and 'options' keys used to build the URI
|
||||
* of the entity, and matching the signature of url(). NULL if the entity
|
||||
* has no URI of its own.
|
||||
*/
|
||||
public function uri();
|
||||
|
||||
/**
|
||||
* Saves an entity permanently.
|
||||
*
|
||||
* @return
|
||||
* Either SAVED_NEW or SAVED_UPDATED, depending on the operation performed.
|
||||
*
|
||||
* @throws EntityStorageException
|
||||
* In case of failures an exception is thrown.
|
||||
*/
|
||||
public function save();
|
||||
|
||||
/**
|
||||
* Deletes an entity permanently.
|
||||
*
|
||||
* @throws EntityStorageException
|
||||
* In case of failures an exception is thrown.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Creates a duplicate of the entity.
|
||||
*
|
||||
* @return EntityInterface
|
||||
* A clone of the current entity with all identifiers unset, so saving
|
||||
* it inserts a new entity into the storage system.
|
||||
*/
|
||||
public function createDuplicate();
|
||||
|
||||
/**
|
||||
* Returns the info of the type of the entity.
|
||||
*
|
||||
* @see entity_get_info()
|
||||
*/
|
||||
public function entityInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a base entity class.
|
||||
*
|
||||
* Default implementation of EntityInterface.
|
||||
*
|
||||
* This class can be used as-is by simple entity types. Entity types requiring
|
||||
* special handling can extend the class.
|
||||
*/
|
||||
class Entity implements EntityInterface {
|
||||
|
||||
/**
|
||||
* The entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* Information about the entity's type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entityInfo;
|
||||
|
||||
/**
|
||||
* The entity ID key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $idKey;
|
||||
|
||||
/**
|
||||
* The entity bundle key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundleKey;
|
||||
|
||||
/**
|
||||
* Constructs a new entity object.
|
||||
*/
|
||||
public function __construct(array $values = array(), $entity_type) {
|
||||
$this->entityType = $entity_type;
|
||||
$this->setUp();
|
||||
// Set initial values.
|
||||
foreach ($values as $key => $value) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the object instance on construction or unserialization.
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->entityInfo = entity_get_info($this->entityType);
|
||||
$this->idKey = $this->entityInfo['entity keys']['id'];
|
||||
$this->bundleKey = isset($this->entityInfo['entity keys']['bundle']) ? $this->entityInfo['entity keys']['bundle'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::id().
|
||||
*/
|
||||
public function id() {
|
||||
return isset($this->{$this->idKey}) ? $this->{$this->idKey} : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::isNew().
|
||||
*/
|
||||
public function isNew() {
|
||||
// We support creating entities with pre-defined IDs to ease migrations.
|
||||
// For that the "is_new" property may be set to TRUE.
|
||||
return !empty($this->is_new) || empty($this->{$this->idKey});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::entityType().
|
||||
*/
|
||||
public function entityType() {
|
||||
return $this->entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::bundle().
|
||||
*/
|
||||
public function bundle() {
|
||||
return isset($this->bundleKey) ? $this->{$this->bundleKey} : $this->entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::label().
|
||||
*
|
||||
* @see entity_label()
|
||||
*/
|
||||
public function label() {
|
||||
$label = FALSE;
|
||||
if (isset($this->entityInfo['label callback']) && function_exists($this->entityInfo['label callback'])) {
|
||||
$label = $this->entityInfo['label callback']($this->entityType, $this);
|
||||
}
|
||||
elseif (!empty($this->entityInfo['entity keys']['label']) && isset($this->{$this->entityInfo['entity keys']['label']})) {
|
||||
$label = $this->{$this->entityInfo['entity keys']['label']};
|
||||
}
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::uri().
|
||||
*
|
||||
* @see entity_uri()
|
||||
*/
|
||||
public function uri() {
|
||||
$bundle = $this->bundle();
|
||||
// A bundle-specific callback takes precedence over the generic one for the
|
||||
// entity type.
|
||||
if (isset($this->entityInfo['bundles'][$bundle]['uri callback'])) {
|
||||
$uri_callback = $this->entityInfo['bundles'][$bundle]['uri callback'];
|
||||
}
|
||||
elseif (isset($this->entityInfo['uri callback'])) {
|
||||
$uri_callback = $this->entityInfo['uri callback'];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Invoke the callback to get the URI. If there is no callback, return NULL.
|
||||
if (isset($uri_callback) && function_exists($uri_callback)) {
|
||||
$uri = $uri_callback($this);
|
||||
// Pass the entity data to url() so that alter functions do not need to
|
||||
// look up this entity again.
|
||||
$uri['options']['entity_type'] = $this->entityType;
|
||||
$uri['options']['entity'] = $this;
|
||||
return $uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::save().
|
||||
*/
|
||||
public function save() {
|
||||
return entity_get_controller($this->entityType)->save($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::delete().
|
||||
*/
|
||||
public function delete() {
|
||||
if (!$this->isNew()) {
|
||||
entity_get_controller($this->entityType)->delete(array($this->id()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::createDuplicate().
|
||||
*/
|
||||
public function createDuplicate() {
|
||||
$duplicate = clone $this;
|
||||
$duplicate->{$this->idKey} = NULL;
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityInterface::entityInfo().
|
||||
*/
|
||||
public function entityInfo() {
|
||||
return $this->entityInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes only what is necessary.
|
||||
*
|
||||
* See @link http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.sleep PHP Magic Methods @endlink.
|
||||
*/
|
||||
public function __sleep() {
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['entityInfo'], $vars['idKey'], $vars['bundleKey']);
|
||||
// Also key the returned array with the variable names so the method may
|
||||
// be easily overridden and customized.
|
||||
return drupal_map_assoc(array_keys($vars));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes setUp() on unserialization.
|
||||
*
|
||||
* See @link http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.sleep PHP Magic Methods @endlink
|
||||
*/
|
||||
public function __wakeup() {
|
||||
$this->setUp();
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Interface for entity controller classes.
|
||||
* Defines a common interface for entity controller classes.
|
||||
*
|
||||
* All entity controller classes specified via the 'controller class' key
|
||||
* returned by hook_entity_info() or hook_entity_info_alter() have to implement
|
||||
|
@ -19,7 +19,7 @@
|
|||
interface DrupalEntityControllerInterface {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Constructs a new DrupalEntityControllerInterface object.
|
||||
*
|
||||
* @param $entityType
|
||||
* The entity type for which the instance is created.
|
||||
|
@ -50,6 +50,8 @@ interface DrupalEntityControllerInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Defines a base entity controller class.
|
||||
*
|
||||
* Default implementation of DrupalEntityControllerInterface.
|
||||
*
|
||||
* This class can be used as-is by most simple entity types. Entity types
|
||||
|
@ -122,7 +124,9 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
|
|||
protected $cache;
|
||||
|
||||
/**
|
||||
* Constructor: sets basic variables.
|
||||
* Implements DrupalEntityControllerInterface::__construct().
|
||||
*
|
||||
* Sets basic variables.
|
||||
*/
|
||||
public function __construct($entityType) {
|
||||
$this->entityType = $entityType;
|
||||
|
@ -194,11 +198,16 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
|
|||
// is set to FALSE (so we load all entities), if there are any ids left to
|
||||
// load, if loading a revision, or if $conditions was passed without $ids.
|
||||
if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
|
||||
// Build the query.
|
||||
$query = $this->buildQuery($ids, $conditions, $revision_id);
|
||||
$queried_entities = $query
|
||||
->execute()
|
||||
->fetchAllAssoc($this->idKey);
|
||||
// Build and execute the query.
|
||||
$query_result = $this->buildQuery($ids, $conditions, $revision_id)->execute();
|
||||
|
||||
if (!empty($this->entityInfo['entity class'])) {
|
||||
// We provide the necessary arguments for PDO to create objects of the
|
||||
// specified entity class.
|
||||
// @see EntityInterface::__construct()
|
||||
$query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['entity class'], array(array(), $this->entityType));
|
||||
}
|
||||
$queried_entities = $query_result->fetchAllAssoc($this->idKey);
|
||||
}
|
||||
|
||||
// Pass all entities loaded from the database through $this->attachLoad(),
|
||||
|
@ -388,3 +397,169 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
|
|||
$this->entityCache += $entities;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a common interface for entity storage controllers.
|
||||
*/
|
||||
interface EntityStorageControllerInterface extends DrupalEntityControllerInterface {
|
||||
|
||||
/**
|
||||
* Deletes permanently saved entities.
|
||||
*
|
||||
* @param $ids
|
||||
* An array of entity IDs.
|
||||
*
|
||||
* @throws EntityStorageException
|
||||
* In case of failures, an exception is thrown.
|
||||
*/
|
||||
public function delete($ids);
|
||||
|
||||
/**
|
||||
* Saves the entity permanently.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity to save.
|
||||
*
|
||||
* @return
|
||||
* SAVED_NEW or SAVED_UPDATED is returned depending on the operation
|
||||
* performed.
|
||||
*
|
||||
* @throws EntityStorageException
|
||||
* In case of failures, an exception is thrown.
|
||||
*/
|
||||
public function save(EntityInterface $entity);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an exception thrown when storage operations fail.
|
||||
*/
|
||||
class EntityStorageException extends Exception { }
|
||||
|
||||
/**
|
||||
* Implements the entity storage controller interface for the database.
|
||||
*/
|
||||
class EntityDatabaseStorageController extends DrupalDefaultEntityController implements EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Implements EntityStorageControllerInterface::delete().
|
||||
*/
|
||||
public function delete($ids) {
|
||||
$entities = $ids ? $this->load($ids) : FALSE;
|
||||
if (!$entities) {
|
||||
// If no IDs or invalid IDs were passed, do nothing.
|
||||
return;
|
||||
}
|
||||
$transaction = db_transaction();
|
||||
|
||||
try {
|
||||
$this->preDelete($entities);
|
||||
$ids = array_keys($entities);
|
||||
|
||||
db_delete($this->entityInfo['base table'])
|
||||
->condition($this->idKey, $ids, 'IN')
|
||||
->execute();
|
||||
// Reset the cache as soon as the changes have been applied.
|
||||
$this->resetCache($ids);
|
||||
|
||||
$this->postDelete($entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
// Ignore slave server temporarily.
|
||||
db_ignore_slave();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$transaction->rollback();
|
||||
watchdog_exception($this->entityType, $e);
|
||||
throw new EntityStorageException($e->getMessage, $e->getCode, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements EntityStorageControllerInterface::save().
|
||||
*/
|
||||
public function save(EntityInterface $entity) {
|
||||
$transaction = db_transaction();
|
||||
try {
|
||||
// Load the stored entity, if any.
|
||||
if (!$entity->isNew() && !isset($entity->original)) {
|
||||
$entity->original = entity_load_unchanged($this->entityType, $entity->id());
|
||||
}
|
||||
|
||||
$this->preSave($entity);
|
||||
$this->invokeHook('presave', $entity);
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
$return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
|
||||
$this->resetCache(array($entity->{$this->idKey}));
|
||||
$this->postSave($entity);
|
||||
$this->invokeHook('update', $entity);
|
||||
}
|
||||
else {
|
||||
$return = drupal_write_record($this->entityInfo['base table'], $entity);
|
||||
$this->postSave($entity);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
|
||||
// Ignore slave server temporarily.
|
||||
db_ignore_slave();
|
||||
unset($entity->is_new);
|
||||
unset($entity->original);
|
||||
|
||||
return $return;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$transaction->rollback();
|
||||
watchdog_exception($this->entityType, $e);
|
||||
throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on an entity before the presave hook is invoked.
|
||||
*
|
||||
* Used before the entity is saved and before invoking the presave hook.
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) { }
|
||||
|
||||
/**
|
||||
* Acts on a saved entity before the insert or update hook is invoked.
|
||||
*
|
||||
* Used after the entity is saved, but before invoking the insert or update
|
||||
* hook.
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity) { }
|
||||
|
||||
/**
|
||||
* Acts on entities before they are deleted.
|
||||
*
|
||||
* Used before the entities are deleted and before invoking the delete hook.
|
||||
*/
|
||||
protected function preDelete($entities) { }
|
||||
|
||||
/**
|
||||
* Acts on deleted entities before the delete hook is invoked.
|
||||
*
|
||||
* Used after the entities are deleted but before invoking the delete hook.
|
||||
*/
|
||||
protected function postDelete($entities) { }
|
||||
|
||||
/**
|
||||
* Invokes a hook on behalf of the entity.
|
||||
*
|
||||
* @param $op
|
||||
* One of 'presave', 'insert', 'update', or 'delete'.
|
||||
* @param $entity
|
||||
* The entity object.
|
||||
*/
|
||||
protected function invokeHook($hook, EntityInterface $entity) {
|
||||
if (!empty($this->entityInfo['fieldable']) && function_exists($function = 'field_attach_' . $hook)) {
|
||||
$function($this->entityType, $entity);
|
||||
}
|
||||
// Invoke the hook.
|
||||
module_invoke_all($this->entityType . '_' . $hook, $entity);
|
||||
// Invoke the respective entity-level hook.
|
||||
module_invoke_all('entity_' . $hook, $entity, $this->entityType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ package = Core
|
|||
version = VERSION
|
||||
core = 8.x
|
||||
required = TRUE
|
||||
files[] = entity.class.inc
|
||||
files[] = entity.query.inc
|
||||
files[] = entity.controller.inc
|
||||
files[] = tests/entity_crud_hook_test.test
|
||||
files[] = tests/entity_query.test
|
||||
files[] = tests/entity.test
|
||||
|
|
|
@ -173,19 +173,20 @@ function entity_extract_ids($entity_type, $entity) {
|
|||
* 2: bundle name of the entity, or NULL if $entity_type has no bundles
|
||||
*
|
||||
* @return
|
||||
* An entity structure, initialized with the ids provided.
|
||||
* An entity object, initialized with the IDs provided.
|
||||
*/
|
||||
function entity_create_stub_entity($entity_type, $ids) {
|
||||
$entity = new stdClass();
|
||||
$values = array();
|
||||
$info = entity_get_info($entity_type);
|
||||
$entity->{$info['entity keys']['id']} = $ids[0];
|
||||
$values[$info['entity keys']['id']] = $ids[0];
|
||||
if (!empty($info['entity keys']['revision']) && isset($ids[1])) {
|
||||
$entity->{$info['entity keys']['revision']} = $ids[1];
|
||||
$values[$info['entity keys']['revision']] = $ids[1];
|
||||
}
|
||||
if (!empty($info['entity keys']['bundle']) && isset($ids[2])) {
|
||||
$entity->{$info['entity keys']['bundle']} = $ids[2];
|
||||
$values[$info['entity keys']['bundle']] = $ids[2];
|
||||
}
|
||||
return $entity;
|
||||
// @todo Once all entities are converted, just rely on entity_create().
|
||||
return isset($info['entity class']) ? entity_create($entity_type, $values) : (object) $values;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,6 +256,36 @@ function entity_load_unchanged($entity_type, $id) {
|
|||
return reset($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple entities permanently.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of the entity.
|
||||
* @param $ids
|
||||
* An array of entity IDs of the entities to delete.
|
||||
*/
|
||||
function entity_delete_multiple($entity_type, $ids) {
|
||||
entity_get_controller($entity_type)->delete($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new entity object, without saving it to the database.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of the entity.
|
||||
* @param $values
|
||||
* An array of values to set, keyed by property name. If the entity type has
|
||||
* bundles the bundle key has to be specified.
|
||||
*
|
||||
* @return EntityInterface
|
||||
* A new entity object.
|
||||
*/
|
||||
function entity_create($entity_type, array $values) {
|
||||
$info = entity_get_info($entity_type) + array('entity class' => 'Entity');
|
||||
$class = $info['entity class'];
|
||||
return new $class($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity controller class for an entity type.
|
||||
*/
|
||||
|
@ -321,6 +352,9 @@ function entity_prepare_view($entity_type, $entities) {
|
|||
* An array containing the 'path' and 'options' keys used to build the uri of
|
||||
* the entity, and matching the signature of url(). NULL if the entity has no
|
||||
* uri of its own.
|
||||
*
|
||||
* @todo
|
||||
* Remove once all entity types are implementing the EntityInterface.
|
||||
*/
|
||||
function entity_uri($entity_type, $entity) {
|
||||
$info = entity_get_info($entity_type);
|
||||
|
@ -362,6 +396,9 @@ function entity_uri($entity_type, $entity) {
|
|||
*
|
||||
* @return
|
||||
* The entity label, or FALSE if not found.
|
||||
*
|
||||
* @todo
|
||||
* Remove once all entity types are implementing the EntityInterface.
|
||||
*/
|
||||
function entity_label($entity_type, $entity) {
|
||||
$label = FALSE;
|
||||
|
@ -439,4 +476,3 @@ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_st
|
|||
* Exception thrown when a malformed entity is passed.
|
||||
*/
|
||||
class EntityMalformedException extends Exception { }
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity CRUD API tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests the basic Entity API.
|
||||
*/
|
||||
class EntityAPITestCase extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity CRUD',
|
||||
'description' => 'Tests basic CRUD functionality.',
|
||||
'group' => 'Entity API',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('entity', 'entity_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic CRUD functionality of the Entity API.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$user1 = $this->drupalCreateUser();
|
||||
|
||||
// Create some test entities.
|
||||
$entity = entity_create('entity_test', array('name' => 'test', 'uid' => $user1->uid));
|
||||
$entity->save();
|
||||
$entity = entity_create('entity_test', array('name' => 'test2', 'uid' => $user1->uid));
|
||||
$entity->save();
|
||||
$entity = entity_create('entity_test', array('name' => 'test', 'uid' => NULL));
|
||||
$entity->save();
|
||||
|
||||
$entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test')));
|
||||
|
||||
$this->assertEqual($entities[0]->name, 'test', 'Created and loaded entity.');
|
||||
$this->assertEqual($entities[1]->name, 'test', 'Created and loaded entity.');
|
||||
|
||||
// Test loading a single entity.
|
||||
$loaded_entity = entity_test_load($entity->id);
|
||||
$this->assertEqual($loaded_entity->id, $entity->id, 'Loaded a single entity by id.');
|
||||
|
||||
// Test deleting an entity.
|
||||
$entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2')));
|
||||
$entities[0]->delete();
|
||||
$entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2')));
|
||||
$this->assertEqual($entities, array(), 'Entity deleted.');
|
||||
|
||||
// Test updating an entity.
|
||||
$entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test')));
|
||||
$entities[0]->name = 'test3';
|
||||
$entities[0]->save();
|
||||
$entity = entity_test_load($entities[0]->id);
|
||||
$this->assertEqual($entity->name, 'test3', 'Entity updated.');
|
||||
|
||||
// Try deleting multiple test entities by deleting all.
|
||||
$ids = array_keys(entity_test_load_multiple(FALSE));
|
||||
entity_test_delete_multiple($ids);
|
||||
|
||||
$all = entity_test_load_multiple(FALSE);
|
||||
$this->assertTrue(empty($all), 'Deleted all entities.');
|
||||
}
|
||||
}
|
|
@ -66,7 +66,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
|
|||
node_save($node);
|
||||
$nid = $node->nid;
|
||||
|
||||
$comment = (object) array(
|
||||
$comment = entity_create('comment', array(
|
||||
'cid' => NULL,
|
||||
'pid' => 0,
|
||||
'nid' => $nid,
|
||||
|
@ -76,7 +76,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase {
|
|||
'changed' => REQUEST_TIME,
|
||||
'status' => 1,
|
||||
'language' => LANGUAGE_NONE,
|
||||
);
|
||||
));
|
||||
$_SESSION['entity_crud_hook_test'] = array();
|
||||
comment_save($comment);
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
name = Entity CRUD test module
|
||||
description = Provides entity types based upon the CRUD API.
|
||||
package = Testing
|
||||
version = VERSION
|
||||
core = 8.x
|
||||
dependencies[] = entity
|
||||
hidden = TRUE
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the entity_test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function entity_test_install() {
|
||||
// Auto-create a field for testing.
|
||||
$field = array(
|
||||
'field_name' => 'field_test_text',
|
||||
'type' => 'text',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
field_create_field($field);
|
||||
|
||||
$instance = array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_test_text',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => 'Test text-field',
|
||||
'widget' => array(
|
||||
'type' => 'text_textfield',
|
||||
'weight' => 0,
|
||||
),
|
||||
);
|
||||
field_create_instance($instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function entity_test_schema() {
|
||||
$schema['entity_test'] = array(
|
||||
'description' => 'Stores entity_test items.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary Key: Unique entity-test item ID.',
|
||||
),
|
||||
'name' => array(
|
||||
'description' => 'The name of the test entity.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'uid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
'default' => NULL,
|
||||
'description' => "The {users}.uid of the associated user.",
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'uid' => array('uid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'uid' => array('users' => 'uid'),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
);
|
||||
return $schema;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test module for the entity API providing an entity type for testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info().
|
||||
*/
|
||||
function entity_test_entity_info() {
|
||||
$return = array(
|
||||
'entity_test' => array(
|
||||
'label' => t('Test entity'),
|
||||
'entity class' => 'Entity',
|
||||
'controller class' => 'EntityDatabaseStorageController',
|
||||
'base table' => 'entity_test',
|
||||
'fieldable' => TRUE,
|
||||
'entity keys' => array(
|
||||
'id' => 'id',
|
||||
),
|
||||
),
|
||||
);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a test entity.
|
||||
*
|
||||
* @param $id
|
||||
* A test entity ID.
|
||||
* @param $reset
|
||||
* A boolean indicating that the internal cache should be reset.
|
||||
*
|
||||
* @return Entity
|
||||
* The loaded entity object, or FALSE if the entity cannot be loaded.
|
||||
*/
|
||||
function entity_test_load($id, $reset = FALSE) {
|
||||
$result = entity_load('entity_test', array($id), array(), $reset);
|
||||
return reset($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple test entities based on certain conditions.
|
||||
*
|
||||
* @param $ids
|
||||
* An array of entity IDs.
|
||||
* @param $conditions
|
||||
* An array of conditions to match against the {entity} table.
|
||||
* @param $reset
|
||||
* A boolean indicating that the internal cache should be reset.
|
||||
*
|
||||
* @return
|
||||
* An array of test entity objects, indexed by ID.
|
||||
*/
|
||||
function entity_test_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
|
||||
return entity_load('entity_test', $ids, $conditions, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple test entities.
|
||||
*
|
||||
* @param $ids
|
||||
* An array of test entity IDs.
|
||||
*/
|
||||
function entity_test_delete_multiple(array $ids) {
|
||||
entity_get_controller('entity_test')->delete($ids);
|
||||
}
|
|
@ -732,7 +732,7 @@ class UserCancelTestCase extends DrupalWebTestCase {
|
|||
$this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$this->assertText(t('Your comment has been posted.'));
|
||||
$comments = comment_load_multiple(array(), array('subject' => $edit['subject']));
|
||||
$comments = comment_load_multiple(FALSE, array('subject' => $edit['subject']));
|
||||
$comment = reset($comments);
|
||||
$this->assertTrue($comment->cid, t('Comment found.'));
|
||||
|
||||
|
|
Loading…
Reference in New Issue