create_instance.rst 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. Attach Fields to Content Types
  2. ==============================
  3. In summary, creation of a new field requires creation of three classes that inherit from the ``TripalField``, ``TripalFieldWidget`` and ``TripalFieldFormatter`` base classes. If the fields are created correctly and placed in the ``includes/TripalFields`` directory of your module then Tripal will automatically find them. However, the field is not yet attached to any content type. They must be attached. Fields can be attached programmatically or via the online Drupal interface by a site admin.
  4. The hook_bundle_fields_info() function
  5. -------------------------------------
  6. The three TripalField classes simply define how the field will function, but Drupal does not yet know about the field. The ``hook_bundle_fields_info`` function tells Drupal about your field. It must be implemented in a custom Drupal module, and provides an array that tells Drupal about the fields and the classes to use for the field. Suppose we were creating a field named ``obi__genus`` which displays the Genus for a species and we have a custom module named ``tripal_org2``. The hook function would be named ``tripal_org2_bundle_fields_info()``:
  7. .. code-block:: php
  8. :linenos:
  9. function tripal_org2_bundle_fields_info($entity_type, $bundle) {
  10. $info = [];
  11. // Make sure this bundle is an organism (OBI:0100026) then we'll attach our
  12. // field to display the genus.
  13. $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
  14. $term_accession = $term->vocab->vocabulary . '__' . $term->accession;
  15. if ($term_accession == 'OBI:0100026') {
  16. $field_name = 'obi__genus';
  17. $field_type = 'obi__genus';
  18. $info[$field_name] = [
  19. 'field_name' => $field_name,
  20. 'type' => $field_type,
  21. 'cardinality' => 1,
  22. 'locked' => FALSE,
  23. 'storage' => [
  24. 'type' => 'field_chado_storage',
  25. ],
  26. 'settings' => [],
  27. ];
  28. }
  29. return $info
  30. }
  31. This function receives as its second argument the ``$bundle`` object. This is the bundle that Drupal is requesting new fields for. For this example we only want to attach the field if the content type is the organism content type. The format of the returned ``$info`` array should have the field name as the key and an array that follows the instructions provided by Drupal's `field_create_field() <https://api.drupal.org/api/drupal/modules%21field%21field.crud.inc/function/field_create_field/7.x>`_ function.
  32. The settings indicate the field name, the field type, the cardinality (how many values are allowed), any default settings and the storage type. Because we expect our data to come from Chado we set the ``field_chado_storage`` as the type. The ``locked`` setting is set to FALSE indicating that Drupal will allow the field to be deleted if the site developer desires.
  33. When the site administrator navigates to **Administer > Structure > Tripal Content Types**, clicks on a content type, and then the **manage fields** tab, a link appears at the top titled **Check for new fields**. When that link is clicked, this hook function is called.
  34. Programmatically Attaching Fields
  35. ---------------------------------
  36. You probably want to programmatically attach fields to content types if your have existing data that you know should be made available. For example, an organism always has a genus and only one genus. If we have a field that displays the genus for an organism then we will want it automatically attached on installation of our module. We can do this programmatically using two hook functions: ``hook_bundle_fields_info()`` and ``hook_bundle_instances_info()``. Both functions are required to attach a field to a content type.
  37. The hook_bundle_instances_info() function.
  38. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  39. The previous hook tells Drupal that our field exists and is allowed to be connected to the organism bundle. Next we need to create an actual instance of this field for the bundle. We do this with the ``hook_bundle_instances_info()`` function. The format is the same as the previous hook but the info array is different. For example:
  40. .. code-block:: php
  41. :linenos:
  42. function tripal_org2_bundle_instances_info($entity_type, $bundle) {
  43. $info = []
  44. // Make sure this bundle is an organism (OBI:0100026) then we'll attach our
  45. // field to display the genus.
  46. $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
  47. $term_accession = $term->vocab->vocabulary . '__' . $term->accession;
  48. if ($term_accession == 'OBI:0100026') {
  49. $field_name = 'obi__genus';
  50. $is_required = FALSE;
  51. $info[$field_name] = [
  52. 'field_name' => $field_name,
  53. 'entity_type' => $entity_type,
  54. 'bundle' => $bundle->name,
  55. 'label' => 'Genus',
  56. 'description' => 'The genus for the organism',
  57. 'required' => TRUE,
  58. 'settings' => [
  59. 'auto_attach' => TRUE,
  60. 'chado_table' => 'organism',
  61. 'chado_column' => 'genus',
  62. 'base_table' => 'organism',
  63. 'term_accession' => '0000005',
  64. 'term_vocabulary' => 'TAXRANK',
  65. 'term_name' => 'Genus',
  66. ],
  67. 'widget' => [
  68. 'type' => 'obi__genus_widget',
  69. 'settings' => [
  70. 'display_label' => 1,
  71. ),
  72. ],
  73. 'display' => [
  74. 'default' => [
  75. 'label' => 'inline',
  76. 'type' => 'obi__genus_formatter',
  77. 'settings' => [],
  78. ],
  79. ],
  80. ];
  81. }
  82. return $info;
  83. }
  84. The format of the returned ``$info`` array should have the field name as the key and an array that follows the instructions provided by Drupal's `field_create_instance() <https://api.drupal.org/api/drupal/modules%21field%21field.crud.inc/function/field_create_instance/7.x>`_ function.
  85. Unique to this info array are the settings related to Chado. Because we expect our data to be loaded from Chado we must specify these settings:
  86. - ``base_table``: the name of the base table to which the record will be associated. In our case the ``organism`` table of Chado is the base table.
  87. - ``chado_table``: the name of the actual table form which the value of the field will be loaded or saved to. In our case the ``organism`` table is also the ``chado_table``.
  88. - ``chado_column``: the name of the column in the ``chado_table`` where the data is loaded from. if the ``base_table`` and ``chado_table`` are the same then this is the name of the column. In our case the ``genus`` columns. If the base and chado tables are different then it is the name o the primary key column in the ``chado_table``
  89. - ``auto_attach``: set this to TRUE if you want the field to automatically be added to an entity when it is generated for viewing. Set it to FALSE to allow the field to be added via AJAX. For fields that require time to load setting to FALSE is preferred.
  90. .. note::
  91. A base table is one that contains the primary records to which ancillary data (e.g. properties, cross references, CV terms, publications, contacts, etc) are associated via linker tables. For example some base tables include: ``feature``, ``organism``, ``stock``, ``library``, etc.). The ``base_table`` and ``chado_table`` will always be the same when you are mapping a field to data in a column in a base table. If your field maps data to a "linker" table where ancillary data is stored then the ``chado_table`` will be the linker table.
  92. Notice as well that the ``display`` and ``widget`` sections list the name of our TripalEntityWidget and TripalEntityFormatter classes respectively. This tells drupal to use our widget and formatter classes by default.
  93. When the site administrator navigates to **Administer > Structure > Tripal Content Types**, clicks on a content type, and then the **manage fields** tab, a link appears at the top titled **Check for new fields**. When that link is clicked, this hook function is called.
  94. .. note::
  95. Both hook functions must be properly constructed for the field to be automatically attached to the content type.
  96. Allowing Manual Attachment of Fields
  97. ------------------------------------
  98. Not all fields are created equal. Some fields can be added by the site developer to a bundle and some cannot. When the ``TripalField`` class is implemented for a class the ``$no_ui`` parameter is set to indicate if a field can be added via the web interface or not. See the :doc:`manual_field_creation` page for more details. But in short the following setting does not allow a field to be added using the web interface
  99. .. code-block:: php
  100. public static $no_ui = TRUE;
  101. The following setting will allow the field to be added:
  102. .. code-block:: php
  103. public static $no_ui = FALSE;
  104. Next, we must let Drupal know that our field exists. We do this by adding an entry to the ``$info`` array in the ``hook_bundle_fields_info()`` function described above. This lets Drupal know about our field. However, because we are not programmatically creating an instance of the field on a content type, but allowing the user to create them we do not need to implement the ``hook_bundle_instances_info()`` function. Instead, we must implement ``hook_bundle_create_user_field()``. This function is called when the user attempts to add our new field to a bundle. One field that comes with Tripal is the ``chado_linker__prop`` field. Most Chado base tables have an associated property table (e.g. ``organismprop``, ``featureprop``, ``stockprop``, etc). By default, the ``tripal_chado`` module automatically adds this field to all bundles that have existing properties. It adds a new instance for every property type. However, new properties can be added to bundle, and the site admin may want to add those properties via the user interface rather. Therefore, this field has the ``$no_ui`` set to TRUE and uses the ``hook_bundle_create_user_field()`` to create the new field instance for the user.
  105. The following code is a snippet from the ``tripal_chado_bundle_create_user_field`` function of the ``tripal_chado`` module. Note that it uses the ``field_create_field`` function and the ``field_create_instance`` functions directly. The arrays passed to these functions are identical to the ``$info`` arrays of both the ``hook_bundle_fields_info`` and ``hook_bundle_instances_info`` functions described above.
  106. .. code-block:: php
  107. :linenos:
  108. function tripal_chado_bundle_create_user_field($new_field, $bundle) {
  109. // Get the table this bundle is mapped to.
  110. $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
  111. $vocab = $term->vocab;
  112. $params = array(
  113. 'vocabulary' => $vocab->vocabulary,
  114. 'accession' => $term->accession,
  115. );
  116. $chado_table = $bundle->data_table;
  117. $chado_type_table = $bundle->type_linker_table;
  118. $chado_type_column = $bundle->type_column;
  119. $chado_type_id = $bundle->type_id;
  120. $chado_type_value = $bundle->type_value;
  121. // We allow site admins to add new chado_linker__prop fields to an entity.
  122. // This function will allow us to properly add them. But at this point we
  123. // don't know the controlled vocabulary term. We'll have to use the
  124. // defaults and let the user set it using the interface.
  125. if ($new_field['type'] == 'chado_linker__prop') {
  126. $table_name = $chado_table . 'prop';
  127. if (chado_table_exists($table_name)) {
  128. $schema = chado_get_schema($table_name);
  129. $pkey = $schema['primary key'][0];
  130. $field_name = $new_field['field_name'];
  131. $field_type = 'chado_linker__prop';
  132. // First add the field.
  133. field_create_field(array(
  134. 'field_name' => $field_name,
  135. 'type' => $field_type,
  136. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  137. 'locked' => FALSE,
  138. 'storage' => array(
  139. 'type' => 'field_chado_storage',
  140. ),
  141. ));
  142. // Now add the instance
  143. field_create_instance(array(
  144. 'field_name' => $field_name,
  145. 'entity_type' => 'TripalEntity',
  146. 'bundle' => $bundle->name,
  147. 'label' => $new_field['label'],
  148. 'description' => '',
  149. 'required' => FALSE,
  150. 'settings' => array(
  151. 'auto_attach' => TRUE,
  152. 'base_table' => $chado_table,
  153. 'chado_table' => $table_name,
  154. 'chado_column' => $pkey,
  155. 'term_vocabulary' => '',
  156. 'term_accession' => '',
  157. 'term_name' => ''
  158. ),
  159. 'widget' => array(
  160. 'type' => 'chado_linker__prop_widget',
  161. 'settings' => array(
  162. 'display_label' => 1,
  163. ),
  164. ),
  165. 'display' => array(
  166. 'default' => array(
  167. 'label' => 'inline',
  168. 'type' => 'chado_linker__prop_formatter',
  169. 'settings' => array(),
  170. ),
  171. ),
  172. ));
  173. }
  174. else {
  175. drupal_set_message('Cannot add a property field to this entity. Chado does not support properties for this data type.', 'error');
  176. }
  177. }
  178. }
  179. .. note::
  180. It is possible to have a field that is both programmatically attached to some content types but is also allowed to be attached to another content type by the site admin using the web interface. To do this, programmatically add the field to the content types using the ``hook_bundle_instances_info`` function and also implement the ``hook_bundle_create_user_field`` function to support manual adding.