You are here

API.txt in Voting API 8.3

Same filename and directory in other branches
  1. 6.2 API.txt
  2. 6 API.txt
  3. 7.3 API.txt
  4. 7.2 API.txt
What's this, now?
=================
VotingAPI provides a flexible, easy-to-use framework for rating, voting,
moderation, and consensus-gathering modules in Drupal. It allows module
developers to focus on their ideas (say, providing a 'rate this thread' widget
at the bottom of each forum post) without worrying about the grunt work of
storing votes, preventing ballot-stuffing, calculating the results, and so on.

VotingAPI handles three key things for module developers:

1) CRUD
Create/Retrieve/Update/Delete operations for voting data. The simplest modules
only need to call two functions -- votingapi_set_vote() and
votingapi_select_results() -- to use the API. Others can use finer-grained
functions for complete control.

2) Calculation
Every time a user casts a vote, VotingAPI calculates the results and caches
them for you. You can use the default calculations (like average, total, etc) or
implement your own custom tallying functions.

3) Display
VotingAPI integrates with the Views module, allowing you to slice and dice your
site's content based on user consensus. While some custom modules may need to
implement their own Views integration to provide customized displays, the vast
majority can use the built-in system without any additional work.

How Data Is Stored
==================
VotingAPI manages a raw 'pool' of vote data -- it doesn't keep track of any
content directly. Instead, it lets modules store each vote with a 'content type'
and 'content id', so that the same APIs can be used to rate nodes, comments,
users, aggregator items, or even other votes (in a Slashdot-esque
meta-moderation system). It can also be used by modules that need to store and
calculate custom data like polling results -- using a custom entity_type ensures
that other modules won't trample on your module's voting data.

For each discrete vote, the API stores the following information:

entity_type  -- This *usually* corresponds to a type of Drupal content, like
                'node' or 'comment' or 'user'.
entity_id    -- The key ID of the content being rated.
value        -- This is the actual value of the vote that was cast by the user.
value_type   -- This determines how vote results are totaled. VotingAPI
                supports three value types by default: 'percent' votes are
                averaged, 'points' votes are summed up, and 'option' votes get a
                count of votes cast for each specific option.  Modules can use
                other value_types, but must implement their own calculation
                functions to generate vote results -- more on that later.
tag          -- Modules can use different tags to store votes on specific
                aspects of a piece of content, like 'accuracy' and 'timeliness'
                of a news story. If you don't need to vote on multiple
                criteria, you should use the default value of 'vote'. If you use
                multiple tags, it is STRONGLY recommended that you provide an
                average or 'overall' value filed under the default 'vote' tag.
                This gives other modules that display voting data a single value
                to key on for sorting, etc.
uid          -- The user ID of the person who voted.
timestamp    -- The time the vote was cast.
vote_source  -- A unique identifier used to distinguish votes cast by anonymous
                users. By default, this is the IP address hash of the remote
                machine.

Whenever a vote is cast, VotingAPI gathers up all the votes for the
entity_type/entity_id combination, and creates a collection of cached 'result'
records. Each voting result recorded stores the following information:

entity_type  -- Just what you'd expect from the individual vote objects.
entity_id    -- Ditto.
value_type   -- Ditto.
tag          -- Ditto.
function     -- The aggregate function that's been calculated -- for example,
                'average', 'sum', and so on.
value        -- The value of the aggregate function.
timestamp    -- The time the results were calculated.


Upgrading from VotingAPI 1.x
============================
Version 2.0 of VotingAPI offers several notable changes. Modules MUST be
updated to work with VotingAPI 2.0, but changes for most modules should be
minimal. Among other things, version 2.0 offers automatic support for anonymous
votes -- something that required custom vote handling in version 1.x.

1) Functions accept objects / arrays of objects instead of long parameter lists.
VotingAPI 1.x used relatively complex parameter lists in its most
commonly used functions. In version 2.x, VotingAPI vote-casting functions
accept a single vote object or array of vote objects, while vote and result
retrieval functions accept a keyed array describing the filter criteria to be
used.

2) hook_votingapi_update() Removed.
This function allowed modules to intervene whenever a user changed their vote.
The processing overhead that it imposed on most operations, however, was severe.
No modules in contrib implemented the hook, and it has been eliminated.
hook_votingapi_insert() and hook_votingapi_delete() are still available.

3) Retrieval functions consolidated.
In VotingAPI 1.x, votes for a content object were retrieved using a dizzying
array of functions, including the ugly buy often-used internal function,
_votingapi_get_raw_votes(). In version 2.x, the following functions are
provided:

* votingapi_select_votes();
* votingapi_select_results();
* votingapi_select_single_vote_value()
* votingapi_select_single_result_value();

4) Custom result calculations must do their own SQL.
In version 1.x, VotingAPI loaded all votes for a given content object in order
to calculate the average vote using PHP. Modules calculating their own results
(median, etc.) were handed the stack of vote objects and given an opportunity to
do more using hook_votingapi_calculate(). This was fine for simple cases, but
consumed monstrous amounts of RAM whenever a single piece of content accumulated
large numbers of votes.

In version 2.x, VotingAPI modules may implement hook_votingapi_results_alter()
instead. They receive the same information about the content object, and the
stack of results to modify, but are responsible for using their own SQL to
generate their results. Fortunately, most modules implementing custom results
required complex calculations more efficiently done in SQL anyways.

5) Views integration hooks have changed.
VotingAPI now supports any valid base table when exposing its data to Views.
Modules that cast votes on non-node content can implement
hook_votingapi_views_entity_types() to let VotingAPI know what base tables
should get the relationships.

Because Views' internal data structures have changed, and the VotingAPI
integration now supports additional base tables, the data handed off to
hook_votingapi_views_formatters() has changed. See the reference at the bottom
of this document for an example implementation.

In the future, modules that need to offer highly customized ways of presenting
VotingAPI data in a view are advised to use hook_views_data_alter() to simply
add a new custom field to the VotingAPI table definition. That will give full
control over display and filtering, but will take advantage of the flexible,
base-table-agnostic join handlers VotingAPI provides.


An example custom calculation
=============================
The following function adds a standard_deviation result to the calculated result
data. Note that in previous versions of VotingAPI, this function received
in-memory copies of each and every cast vote to avoid the need for custom SQL.
This turned out to be very, very, very inefficient -- the slowdown of possibly
running multiple aggregate queries is far outweighed by the memory savings of
each module handling its own queries. After all, MySQL calculates standard
deviation far faster than you can in PHP.

function hook_votingapi_results_alter(&$vote_results, $content_type, $content_id) {
  // We're using a MySQLism (STDDEV isn't ANSI SQL), but it's OK because this is
  // an example. And no one would ever base real code on sample code. Ever. Never.

  $sql = "SELECT v.tag, STDDEV(v.value) as standard_deviation ";
  $sql .= "FROM {votingapi_vote} v ";
  $sql .= "WHERE v.content_type = '%s' AND v.content_id = %d AND v.value_type = 'percent' ";
  $sql .= "GROUP BY v.tag";

  $aggregates = \Drupal::database()->query($sql, $content_type, $content_id);

  // VotingAPI wants the data in the following format:
  // $vote_results[$tag][$value_type][$aggregate_function] = $value;

  foreach ($aggregates as $aggregate) {
    $aggregate = (array) $aggregate;
    $vote_results[$aggregate['tag']]['percent']['standard_deviation'] = $aggregate['standard_deviation'];
  }
}

An example of advanced Views integration
========================================
VotingAPI provides Views Relationship connections for votes cast on nodes and
comments. If your module casts other types of votes that should be made
available via Views, it needs to implement hook_votingapi_relationships().


function mymodule_votingapi_relationships() {
  $relationships[] = [
    // 'description' is used to construct the field description in the Views UI.
    'description' => t('nodes'),
    // 'entity_type' contain the value that your module stores in the voting
    // api 'entity_type' column. 'node', 'comment', etc.
    'entity_type' => 'node',
    // 'base_table' contain the name of the Views base table that stores the
    // data your votes apply to.
    'base_table' => 'node',
    // 'entity_id_column' contains the name of the views field that represents
    // your base_table's primary key. This column will be joined against the
    // voting api 'entity_id' column.
    'entity_id_column' => 'nid',
    // VotingAPI constructs pseudo-tables so that multiple relationships can
    // point to the same base table (normal and translation-based votes nodes
    // for example. These two columns allow you to override the names of the
    // pseudo-tables. You probably don't need to change this part unless you're
    // nedjo.
    'pseudo_vote' => 'votingapi_vote',
    'pseudo_cache' => 'votingapi_cache',
  ];
  return $relationships;
}

An example of a custom Views value formatter
============================================
VotingAPI's Views integration can present vote results as simple numbers, but
most users will want to see something a bit snazzier. By implementing
hook_votingapi_views_formatters(), you can expose a custom formatter for any
given VotingAPI view field. For the View field that's passed in, your hook
should return an array of key => value pairs, where the key is a the name of a
callback function that will format the values from the database.

function mymodule_votingapi_views_formatters($field) {
  if ($field->field == 'value') {
    return ['mymodule_funky_formatter' => t('MyModule value formatter')];
  }
  if ($field->field == 'tag') {
    return ['mymodule_funky_tags' => t('MyModule tag formatter')];
  }
}

File

API.txt
View source
  1. What's this, now?
  2. =================
  3. VotingAPI provides a flexible, easy-to-use framework for rating, voting,
  4. moderation, and consensus-gathering modules in Drupal. It allows module
  5. developers to focus on their ideas (say, providing a 'rate this thread' widget
  6. at the bottom of each forum post) without worrying about the grunt work of
  7. storing votes, preventing ballot-stuffing, calculating the results, and so on.
  8. VotingAPI handles three key things for module developers:
  9. 1) CRUD
  10. Create/Retrieve/Update/Delete operations for voting data. The simplest modules
  11. only need to call two functions -- votingapi_set_vote() and
  12. votingapi_select_results() -- to use the API. Others can use finer-grained
  13. functions for complete control.
  14. 2) Calculation
  15. Every time a user casts a vote, VotingAPI calculates the results and caches
  16. them for you. You can use the default calculations (like average, total, etc) or
  17. implement your own custom tallying functions.
  18. 3) Display
  19. VotingAPI integrates with the Views module, allowing you to slice and dice your
  20. site's content based on user consensus. While some custom modules may need to
  21. implement their own Views integration to provide customized displays, the vast
  22. majority can use the built-in system without any additional work.
  23. How Data Is Stored
  24. ==================
  25. VotingAPI manages a raw 'pool' of vote data -- it doesn't keep track of any
  26. content directly. Instead, it lets modules store each vote with a 'content type'
  27. and 'content id', so that the same APIs can be used to rate nodes, comments,
  28. users, aggregator items, or even other votes (in a Slashdot-esque
  29. meta-moderation system). It can also be used by modules that need to store and
  30. calculate custom data like polling results -- using a custom entity_type ensures
  31. that other modules won't trample on your module's voting data.
  32. For each discrete vote, the API stores the following information:
  33. entity_type -- This *usually* corresponds to a type of Drupal content, like
  34. 'node' or 'comment' or 'user'.
  35. entity_id -- The key ID of the content being rated.
  36. value -- This is the actual value of the vote that was cast by the user.
  37. value_type -- This determines how vote results are totaled. VotingAPI
  38. supports three value types by default: 'percent' votes are
  39. averaged, 'points' votes are summed up, and 'option' votes get a
  40. count of votes cast for each specific option. Modules can use
  41. other value_types, but must implement their own calculation
  42. functions to generate vote results -- more on that later.
  43. tag -- Modules can use different tags to store votes on specific
  44. aspects of a piece of content, like 'accuracy' and 'timeliness'
  45. of a news story. If you don't need to vote on multiple
  46. criteria, you should use the default value of 'vote'. If you use
  47. multiple tags, it is STRONGLY recommended that you provide an
  48. average or 'overall' value filed under the default 'vote' tag.
  49. This gives other modules that display voting data a single value
  50. to key on for sorting, etc.
  51. uid -- The user ID of the person who voted.
  52. timestamp -- The time the vote was cast.
  53. vote_source -- A unique identifier used to distinguish votes cast by anonymous
  54. users. By default, this is the IP address hash of the remote
  55. machine.
  56. Whenever a vote is cast, VotingAPI gathers up all the votes for the
  57. entity_type/entity_id combination, and creates a collection of cached 'result'
  58. records. Each voting result recorded stores the following information:
  59. entity_type -- Just what you'd expect from the individual vote objects.
  60. entity_id -- Ditto.
  61. value_type -- Ditto.
  62. tag -- Ditto.
  63. function -- The aggregate function that's been calculated -- for example,
  64. 'average', 'sum', and so on.
  65. value -- The value of the aggregate function.
  66. timestamp -- The time the results were calculated.
  67. Upgrading from VotingAPI 1.x
  68. ============================
  69. Version 2.0 of VotingAPI offers several notable changes. Modules MUST be
  70. updated to work with VotingAPI 2.0, but changes for most modules should be
  71. minimal. Among other things, version 2.0 offers automatic support for anonymous
  72. votes -- something that required custom vote handling in version 1.x.
  73. 1) Functions accept objects / arrays of objects instead of long parameter lists.
  74. VotingAPI 1.x used relatively complex parameter lists in its most
  75. commonly used functions. In version 2.x, VotingAPI vote-casting functions
  76. accept a single vote object or array of vote objects, while vote and result
  77. retrieval functions accept a keyed array describing the filter criteria to be
  78. used.
  79. 2) hook_votingapi_update() Removed.
  80. This function allowed modules to intervene whenever a user changed their vote.
  81. The processing overhead that it imposed on most operations, however, was severe.
  82. No modules in contrib implemented the hook, and it has been eliminated.
  83. hook_votingapi_insert() and hook_votingapi_delete() are still available.
  84. 3) Retrieval functions consolidated.
  85. In VotingAPI 1.x, votes for a content object were retrieved using a dizzying
  86. array of functions, including the ugly buy often-used internal function,
  87. _votingapi_get_raw_votes(). In version 2.x, the following functions are
  88. provided:
  89. * votingapi_select_votes();
  90. * votingapi_select_results();
  91. * votingapi_select_single_vote_value()
  92. * votingapi_select_single_result_value();
  93. 4) Custom result calculations must do their own SQL.
  94. In version 1.x, VotingAPI loaded all votes for a given content object in order
  95. to calculate the average vote using PHP. Modules calculating their own results
  96. (median, etc.) were handed the stack of vote objects and given an opportunity to
  97. do more using hook_votingapi_calculate(). This was fine for simple cases, but
  98. consumed monstrous amounts of RAM whenever a single piece of content accumulated
  99. large numbers of votes.
  100. In version 2.x, VotingAPI modules may implement hook_votingapi_results_alter()
  101. instead. They receive the same information about the content object, and the
  102. stack of results to modify, but are responsible for using their own SQL to
  103. generate their results. Fortunately, most modules implementing custom results
  104. required complex calculations more efficiently done in SQL anyways.
  105. 5) Views integration hooks have changed.
  106. VotingAPI now supports any valid base table when exposing its data to Views.
  107. Modules that cast votes on non-node content can implement
  108. hook_votingapi_views_entity_types() to let VotingAPI know what base tables
  109. should get the relationships.
  110. Because Views' internal data structures have changed, and the VotingAPI
  111. integration now supports additional base tables, the data handed off to
  112. hook_votingapi_views_formatters() has changed. See the reference at the bottom
  113. of this document for an example implementation.
  114. In the future, modules that need to offer highly customized ways of presenting
  115. VotingAPI data in a view are advised to use hook_views_data_alter() to simply
  116. add a new custom field to the VotingAPI table definition. That will give full
  117. control over display and filtering, but will take advantage of the flexible,
  118. base-table-agnostic join handlers VotingAPI provides.
  119. An example custom calculation
  120. =============================
  121. The following function adds a standard_deviation result to the calculated result
  122. data. Note that in previous versions of VotingAPI, this function received
  123. in-memory copies of each and every cast vote to avoid the need for custom SQL.
  124. This turned out to be very, very, very inefficient -- the slowdown of possibly
  125. running multiple aggregate queries is far outweighed by the memory savings of
  126. each module handling its own queries. After all, MySQL calculates standard
  127. deviation far faster than you can in PHP.
  128. function hook_votingapi_results_alter(&$vote_results, $content_type, $content_id) {
  129. // We're using a MySQLism (STDDEV isn't ANSI SQL), but it's OK because this is
  130. // an example. And no one would ever base real code on sample code. Ever. Never.
  131. $sql = "SELECT v.tag, STDDEV(v.value) as standard_deviation ";
  132. $sql .= "FROM {votingapi_vote} v ";
  133. $sql .= "WHERE v.content_type = '%s' AND v.content_id = %d AND v.value_type = 'percent' ";
  134. $sql .= "GROUP BY v.tag";
  135. $aggregates = \Drupal::database()->query($sql, $content_type, $content_id);
  136. // VotingAPI wants the data in the following format:
  137. // $vote_results[$tag][$value_type][$aggregate_function] = $value;
  138. foreach ($aggregates as $aggregate) {
  139. $aggregate = (array) $aggregate;
  140. $vote_results[$aggregate['tag']]['percent']['standard_deviation'] = $aggregate['standard_deviation'];
  141. }
  142. }
  143. An example of advanced Views integration
  144. ========================================
  145. VotingAPI provides Views Relationship connections for votes cast on nodes and
  146. comments. If your module casts other types of votes that should be made
  147. available via Views, it needs to implement hook_votingapi_relationships().
  148. function mymodule_votingapi_relationships() {
  149. $relationships[] = [
  150. // 'description' is used to construct the field description in the Views UI.
  151. 'description' => t('nodes'),
  152. // 'entity_type' contain the value that your module stores in the voting
  153. // api 'entity_type' column. 'node', 'comment', etc.
  154. 'entity_type' => 'node',
  155. // 'base_table' contain the name of the Views base table that stores the
  156. // data your votes apply to.
  157. 'base_table' => 'node',
  158. // 'entity_id_column' contains the name of the views field that represents
  159. // your base_table's primary key. This column will be joined against the
  160. // voting api 'entity_id' column.
  161. 'entity_id_column' => 'nid',
  162. // VotingAPI constructs pseudo-tables so that multiple relationships can
  163. // point to the same base table (normal and translation-based votes nodes
  164. // for example. These two columns allow you to override the names of the
  165. // pseudo-tables. You probably don't need to change this part unless you're
  166. // nedjo.
  167. 'pseudo_vote' => 'votingapi_vote',
  168. 'pseudo_cache' => 'votingapi_cache',
  169. ];
  170. return $relationships;
  171. }
  172. An example of a custom Views value formatter
  173. ============================================
  174. VotingAPI's Views integration can present vote results as simple numbers, but
  175. most users will want to see something a bit snazzier. By implementing
  176. hook_votingapi_views_formatters(), you can expose a custom formatter for any
  177. given VotingAPI view field. For the View field that's passed in, your hook
  178. should return an array of key => value pairs, where the key is a the name of a
  179. callback function that will format the values from the database.
  180. function mymodule_votingapi_views_formatters($field) {
  181. if ($field->field == 'value') {
  182. return ['mymodule_funky_formatter' => t('MyModule value formatter')];
  183. }
  184. if ($field->field == 'tag') {
  185. return ['mymodule_funky_tags' => t('MyModule tag formatter')];
  186. }
  187. }