From 44305bd090c0295b653c5cf0b242c58efd47f757 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Tue, 7 May 2024 09:29:33 -0500 Subject: [PATCH 01/15] work in progress --- docs/dev_guide/module_dev/fields.rst | 168 +++++++++++++++++---------- 1 file changed, 105 insertions(+), 63 deletions(-) diff --git a/docs/dev_guide/module_dev/fields.rst b/docs/dev_guide/module_dev/fields.rst index 0d0d0e1..fab885c 100644 --- a/docs/dev_guide/module_dev/fields.rst +++ b/docs/dev_guide/module_dev/fields.rst @@ -260,17 +260,17 @@ field is used to provide the genus. Tripal provides some ready-to-use field classes for single-values. These are: -- **ChadoIntegerTypeItem**: for integer data. -- **ChadoStringTypeItem**: for string data with a max length. -- **ChadoTextTypeItem**: for string data with unlimited length. -- **ChadoRealTypeItem**: for real (floating point) numberic data. -- **ChadoBoolTypeItem**: for boolean data. -- **ChadoDateTimeTypeItem**: for data/time data. +- **ChadoIntegerTypeDefault**: for integer data. +- **ChadoStringTypeDefault**: for string data with a max length. +- **ChadoTextTypeDefault**: for string data with unlimited length. +- **ChadoRealTypeDefault**: for real (floating point) numeric data. +- **ChadoBoolTypeDefault**: for boolean data. +- **ChadoDateTimeTypeDefault**: for data/time data. .. warning:: - The alpha v1 version of Tripal v4 does not yet implement these fields: - `ChadoRealTypeItem`, `ChadoBoolTypeItem`, `ChadoDateTimeTypeItem` + The alpha v2 version of Tripal v4 does not yet implement these fields: + `ChadoRealTypeDefault`, `ChadoDateTimeTypeDefault` If you need to add a single-value field for your custom module then you do not need to write your own field! You can use one of these existing field types. @@ -287,7 +287,7 @@ a germplasm page must provide a field that allows the user to specify an organis for saving. It should also format the organism name for display. In practice, the `stock` table stores the numeric `organism_id` when saving -a germplasm. We could use a single-value `ChadoIntegerTypeItem` to allow the +a germplasm. We could use a single-value `ChadoIntegerTypeDefault` to allow the user to provide the numeric ID for the organism. But, this is not practical. Users should not be required to use a look-up table of numeric organism IDs. @@ -385,6 +385,12 @@ class example: $settings = $field_definition->getSetting('storage_plugin_settings'); $base_table = $settings['base_table']; + // If we don't have a base table then we're not ready to specify the + // properties for this field. + if (!$base_table) { + return; + } + // Determine the primary key of the base table. $chado = \Drupal::service('tripal_chado.database'); $schema = $chado->schema(); @@ -393,11 +399,10 @@ class example: // Return the array of property types. return [ - new ChadoIntStoragePropertyType($entity_type_id, self::$id,'record_id', [ + new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'record_id', [ 'action' => 'store_id', 'drupal_store' => TRUE, - 'chado_table' => $base_table, - 'chado_column' => $base_pkey_col + 'path' => $base_table . '.' . $base_pkey_col, ]), ]; } @@ -557,9 +562,9 @@ in the ``storage_plugin_settings`` array. But you are free to add any additional settings you would like to help manage your field, especially if those settings help the field define how it will interact with Chado. -An example where a storage settings is needed is in the ``ChadoStringTypeItem`` field -that gets used for any single-value string mapped to a Chado table column. Here -we must set the maximum length of the string. Here is the corresonding ``defaultStorageSettings`` +An example where a storage setting is needed is in the ``ChadoStringTypeDefault`` field +that gets used for any single-value string mapped to a Chado table column of type ``character varying``. +Here we must set the maximum length of the string. Here is the corresonding ``defaultStorageSettings`` function from this field: .. code:: php @@ -578,7 +583,7 @@ If a field needs input from the user to provide values for settings, then the `storageSettingsForm()` function can be implemented. Add the form elements needed for the user to provide values. -For example, the `ChadoStringTypeItem` field wants to allow the site admin to +For example, the `ChadoStringTypeDefault` field wants to allow the site admin to set the maximum string length. .. code:: php @@ -613,7 +618,7 @@ The site admin will be able to change the storage settings if they: .. note:: Site admins can change storage settings for a field only before it is used. - Once the field is used to store data on a live entity, storage settings are + Once the field is used to store data on a live entity, storage settings become fixed. The fieldSettingsForm() Function @@ -629,7 +634,7 @@ ensure that values provided to fields are appropriate. You can read more about defining validation contraints for fields `here `_. -For following code example, is from the `ChadoStringTypeItem` field. It wants +For following code example, is from the `ChadoStringTypeDefault` field. It wants to ensure that that max length of the string is not exceeded. .. code:: php @@ -672,6 +677,11 @@ In the code block below you can see the steps where the field settings are retrieved, and then used to create an array containing a single property. More about properties is described in the next section. +Note that we return from this function early if we do not have a base table +defined. This will happen during manual addition of a field through the GUI. +One of the first steps during this process is to select the base table, so +before that is selected, this function simply returns. + .. code-block:: php public static function tripalTypes($field_definition) { @@ -681,6 +691,12 @@ More about properties is described in the next section. $settings = $field_definition->getSetting('storage_plugin_settings'); $base_table = $settings['base_table']; + // If we don't have a base table then we're not ready to specify the + // properties for this field. + if (!$base_table) { + return; + } + // Determine the primary key of the base table. $chado = \Drupal::service('tripal_chado.database'); $schema = $chado->schema(); @@ -713,14 +729,16 @@ properties. These are named after PostgreSQL column types: - **ChadoDateTimeStoragePropertyType**: a date/time property. - **ChadoIntStoragePropertyType**: an integer property. - **ChadoRealStoragePropertyType**: a floating point property. -- **ChadoTextStoragePropertyType**: an unlimited string property. +- **ChadoTextStoragePropertyType**: a string property with unlimited length. - **ChadoVarCharStoragePropertyType**: a string property with a maximum length. -All of these classes can be instantiated with four arguments: +All of these classes can be instantiated with the following arguments: - The entity type ID: the unique ID for the entity type. - The field ID: the unique ID of the field this property belongs to. - The property "key": a unique key for this property. +- The property controlled vocabulary term: namespace and accession. +- (For ChadoVarCharStoragePropertyType only): The maximum length of the string. - The property settings: an array of settings for this property. See the :ref:`Property Settings` section below for more information on how to specify the property settings array. @@ -732,32 +750,41 @@ The :ref:`Property Types` section above indicated that each property type class has a fourth argument that provides settings for the property. These settings are critical for describing how the property is managed by the ``ChadoStorage`` backend. The settings are an associative array of key-value pairs that specify an -"action" to perform for each property and corresponding helper information. The -following actions can be used: +"action" to perform for each property and corresponding helper information. + +Several of the actions use a **path** specification, which is a sequence of join +actions to reach the desired column in the desired table. For example if the base +table for the record is `feature` and we want to retrieve the organism species, +then the path would be: `feature.organism_id>organism.organism_id;species`. +Separate multiple joins with a semicolon. For example to get the infraspecific +type name of an organism: `feature.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id;name`. +If we want a column in the base table, then the path might be as simple as: `feature.feature_id`. + +The following actions can be used: - **store_id**: indicates that the value of this property will hold the record ID (or primary key ID) of the record in the base table of Chado. Common - base tables include: analysis, feature, stock, pub, organism. This action - uses the following key/value pairs: + base tables include: analysis, feature, stock, pub, organism. There will only + be a single store_id action in any given field. This action uses the following + key/value pairs: - - **chado_table**: (required) the name of the table that this property will - get stored in. This will always be the base table name (e.g. feature). - - **chado_column**: (required) the name of the column in the table where This - property value will get stored. This will always be the primary key of the - base table (e.g., feature_id). + - **path**: (required) this path specifies the primary key of the base table, + and is composed of the base table name, a period, and the primary key of + the base table (e.g., feature_id). + - **drupal_store**: (required) this setting should always be TRUE for this action. - **store_link**: indicates that the value of this property will hold the - value of a foreign key ID to the base table. A property with this action + value of a foreign key ID to the base table. A property with this action is required for fields that provide ancillary information about a record but that information is not stored in a column of the base table, but instead - in a linked table. Examples for such a situation would be + in a linked table. Examples for such a situation would be values from property table: e.g., analysisprop, featureprop, stockprop, etc. This action uses the following key/value pairs: - - **chado_table**: (required) the name of the linked table (e.g. analysisprop) - - **chado_column**: (required) the name of the foreign key column that - links to the base table (e.g. analysis_id) - - **drupal_store**: (requited) this setting should always be TRUE for this action. + - **path**: (required) this path specifies the primary key of the linking table, + and is composed of a join to the linking table's foreign key, for example + `$base_table . '.' . $base_pkey_col . '>' . $linker_table . '.' . $linker_fkey_col`. + - **drupal_store**: (required) this setting should always be TRUE for this action. This forces Tripal to store this value in the Drupal field tables. Without this, Tripal cannot link the fields in Drupal with a base record. @@ -772,7 +799,7 @@ following actions can be used: - **chado_table**: (required) the name of the linked table (e.g. analysisprop) - **chado_column**: (required) the name of the primary key column that links to the base table (e.g. analysisprop_id) - - **drupal_store**: (requited) this setting should always be TRUE for this action. + - **drupal_store**: (required) this setting should always be TRUE for this action. This forces Tripal to store this value in the Drupal field tables. Without this, Tripal cannot link the fields in Drupal with a base record. @@ -808,7 +835,10 @@ following actions can be used: `ChadoStorage` backend will generate, you can rename the `chado_column` with a different name. -- **replace**: indicates that the value of this property is a tokenized string +- **read_value**: this is almost the same as join, but we will not be modifying the + value if we edit a content type, we just look up the existing value. + +- **replace**: indicates that the value of this property is a tokenized string and should be replaced with values from other properties. - **template**: (required) a string containing the value of the field. The @@ -819,21 +849,35 @@ following actions can be used: following template string: "[genus] [species] [iftype] [ifname]". -- **function**: indicates that the value of this property will be set by a - callback function. +- **function**: indicates that the value of this property will be set by a + callback function. You will need to pass the namespace for the + callback function, and the function name. Chado storage will generate a + context array that the function can access to calculate its value. + The callback function then returns a single value which becomes the value + for this property. - - *Currently not implemented in Alpha release v1* As an example, let's look at the ``tripalTypes()`` function of the field that -allows an end-user to add an organism to content. This code is found -in the ``tripal_chado\src\Plugin\Field\FieldType\obi__organism.php`` file of -Tripal: +allows an end-user to add an organism to content. This is a simplified version of the +code that is found in the +``tripal_chado\src\Plugin\Field\FieldType\ChadoOrganismTypeDefault.php`` +file of Tripal: .. code:: php public static function tripalTypes($field_definition) { $entity_type_id = $field_definition->getTargetEntityTypeId(); + // Get the settings for this field. + $settings = $field_definition->getSetting('storage_plugin_settings'); + $base_table = $settings['base_table']; + + // If we don't have a base table then we're not ready to specify the + // properties for this field. + if (!$base_table) { + return; + } + // Get the length of the database fields so we don't go over the size limit. $chado = \Drupal::service('tripal_chado.database'); $schema = $chado->schema(); @@ -846,8 +890,6 @@ Tripal: $label_len = $genus_len + $species_len + $iftype_len + $ifname_len; // Get the base table columns needed for this field. - $settings = $field_definition->getSetting('storage_plugin_settings'); - $base_table = $settings['base_table']; $base_schema_def = $schema->getTableDef($base_table, ['format' => 'Drupal']); $base_pkey_col = $base_schema_def['primary key']; $base_fk_col = array_keys($base_schema_def['foreign keys']['organism']['columns'])[0]; @@ -857,38 +899,38 @@ Tripal: new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'record_id', [ 'action' => 'store_id', 'drupal_store' => TRUE, - 'chado_table' => $base_table, - 'chado_column' => $base_pkey_col + 'path' => $base_table . '.' . $base_pkey_col, ]), new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'organism_id', [ 'action' => 'store', - 'chado_table' => $base_table, - 'chado_column' => $base_fk_col, + 'drupal_store' => TRUE, + 'path' => $base_table . '.' . $base_fk_col, ]), new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'label', $label_len, [ 'action' => 'replace', + 'drupal_store' => FALSE, 'template' => "[genus] [species] [infraspecific_type] [infraspecific_name]", ]), new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'genus', $genus_len, [ - 'action' => 'join', - 'path' => $base_table . '.organism_id>organism.organism_id', - 'chado_column' => 'genus' + 'action' => 'read_value', + 'drupal_store' => FALSE, + 'path' => $base_table . '.organism_id>organism.organism_id;genus', ]), new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'species', $species_len, [ - 'action' => 'join', - 'path' => $base_table . '.organism_id>organism.organism_id', - 'chado_column' => 'species' + 'action' => 'read_value', + 'drupal_store' => FALSE, + 'path' => $base_table . '.organism_id>organism.organism_id;species', ]), new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'infraspecific_name', $ifname_len, [ - 'action' => 'join', - 'path' => $base_table . '.organism_id>organism.organism_id', - 'chado_column' => 'infraspecific_name', + 'action' => 'read_value', + 'drupal_store' => FALSE, + 'path' => $base_table . '.organism_id>organism.organism_id;infraspecific_name', ]), - new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'infraspecific_type', [ - 'action' => 'join', - 'path' => $base_table . '.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id', - 'chado_column' => 'name', - 'as' => 'infraspecific_type_name' + new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'infraspecific_type', [ + 'action' => 'read_value', + 'drupal_store' => FALSE, + 'path' => $base_table . '.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id;name', + 'as' => 'infraspecific_type' ]) ]; } From dd0e9c6fd5598222be9fa6cf041b3027e2ba50c2 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sat, 11 May 2024 10:12:49 -0500 Subject: [PATCH 02/15] 20240510 --- docs/dev_guide/module_dev/fields.rst | 41 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/docs/dev_guide/module_dev/fields.rst b/docs/dev_guide/module_dev/fields.rst index fab885c..070a55e 100644 --- a/docs/dev_guide/module_dev/fields.rst +++ b/docs/dev_guide/module_dev/fields.rst @@ -8,7 +8,7 @@ fields that are bundled with them. For example, when adding a basic page (a default Drupal content type), the end-user is provided with form elements (or widgets) that allow the user to set the title and the body text for the page. The "Body" is a field. When a basic page is -viewed, the body is rendered on the page using formatters and +viewed, the body is rendered on the page using formatters, and Drupal stores the values for the body in the database. Every field, therefore, provides three types of functionality: instructions for storage, widgets for allowing input, and formatters for rendering. @@ -60,11 +60,13 @@ Custom module developers who wish to add new fields to Tripal whose data are stored in Chado should implement the following three classes for every new field: - **ChadoFieldItemBase**: extends the Tripal class `TripalFieldItemBase` - which extends the Drupal class `FiedlItemBase`. The `TripalFieldItemBase` - must be used for all fields attached to Tripal content types and the + which extends the Drupal class `FieldItemBase`. The `TripalFieldItemBase` + must be used for all fields attached to Tripal content types, and the `ChadoFieldItemBase` adds Chado-specific support. -- **TripalWidgetBase**: a class that extends the Drupal class `WidgetBase`. -- **TripalFormatterBase**: a class that extends the Drupal class `FormatterBase`. +- **ChadoWidgetBase**: extends the Tripal class `TripalWidgetBase` + which extends the Drupal class `WidgetBase`. +- **ChadoFormatterBase**: extends the Tripal class `TripalFormatterBase` + which extends the Drupal class `FormatterBase`. How to Write a New Field for Chado @@ -73,8 +75,8 @@ How to Write a New Field for Chado Directory Setup ^^^^^^^^^^^^^^^^ Drupal manages fields using its `Plugin API `_. -this means that as long as new field classes are placed in the correct directory -and have the correct "annotations" in the class comments then Drupal will find them +This means that as long as new field classes are placed in the correct directory +and have the correct "annotations" in the class comments, then Drupal will find them and make the field available. All new fields must be placed in the custom extension module inside of the `src/Plugin/Field` directory. There are three subdirectories, one each for the three elements of a field: @@ -104,7 +106,10 @@ Note that the file name must match the class name. Naming convention ^^^^^^^^^^^^^^^^^ -The filename for your new field should adhere to the following schema. Please note the casing used. In addition, for fields that will be included in Tripal Core, note the 'Default' designation, any fields added by extension modules should **not** use 'Default': +The filename for your new field should adhere to the following schema. Please +note the casing used. In addition, for fields that will be included in Tripal +Core, note the 'Default' designation, any fields added by extension modules +should **not** use 'Default': .. table:: Tripal Core modules: @@ -225,14 +230,14 @@ that allows a field to interact with a Chado database. The `ChadoStorage` backend extends the `SqlContentEntityStorage` and will create a table in the Drupal schema for every Tripal field that is -added to a content type. The table columns will have the same default columns. -It will also have a set of additional columns for every property the field wants -to manage. +added to a content type. The table columns will include the same default +columns as a Drupal field, and will also include a set of additional columns +for every property the field wants to manage. -The `ChadoStorage` backend is different from the `SqlContentEntityStorage` +The `ChadoStorage` backend is different from `SqlContentEntityStorage` in that it will not store the values of the properties in the table. This is because those values need to be stored in Chado--we do not want to duplicate -the data in the Drupal schema and the Chado schema. The `ChadoStorage` +the data in the Drupal schema and the Chado schema. The `ChadoStorage` backend is also different in that it requires a set of property settings that help it control how properties of a field are stored, edited and loaded from Chado. Instructions for working with properties and storing data in Chado are @@ -287,7 +292,7 @@ a germplasm page must provide a field that allows the user to specify an organis for saving. It should also format the organism name for display. In practice, the `stock` table stores the numeric `organism_id` when saving -a germplasm. We could use a single-value `ChadoIntegerTypeDefault` to allow the +a germplasm record. We could use a single-value `ChadoIntegerTypeDefault` to allow the user to provide the numeric ID for the organism. But, this is not practical. Users should not be required to use a look-up table of numeric organism IDs. @@ -296,9 +301,9 @@ Instead what we need is: - A field that will store and load a numeric organism ID value that the user will never see. - A field that has access to the genus, species, infraspecific type, - infraspecific name, etc., of the organism. + infraspecific name, etc. of the organism. - A widget (form element) that allows the user to select an existing organism. -- A formatter that prints the full scientific name of the organism. +- A formatter that displays the full scientific name of the organism. Class Setup @@ -451,7 +456,7 @@ classes you could import if needed. Annotation Section ```````````````````` -The annotation section in the class file is the in-line comments for the class. +The annotation section in the class file is the set of in-line comments for the class. Note the @FieldType stanza in the comments. Drupal uses these annotations to recognize the new field. It provides information such as the field ID, label and description. It also indicates the default widget @@ -473,7 +478,7 @@ and formatter class. This annotation is required. .. warning:: - If the annotation section is not present, has misspellings or is not + If the annotation section is not present, has misspellings, or is not complete, the field will not be recognized by Drupal. From dc64d76cf3825b4e11c4d7e828889d870960f7ef Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sat, 11 May 2024 12:46:52 -0500 Subject: [PATCH 03/15] 20240511 --- docs/dev_guide/module_dev/fields.rst | 154 +++++++++++++++++---------- 1 file changed, 98 insertions(+), 56 deletions(-) diff --git a/docs/dev_guide/module_dev/fields.rst b/docs/dev_guide/module_dev/fields.rst index 070a55e..5ac60a3 100644 --- a/docs/dev_guide/module_dev/fields.rst +++ b/docs/dev_guide/module_dev/fields.rst @@ -538,7 +538,7 @@ As an example, the Tripal organism field sets the term ID space and accession: Not all fields will need the `termIdSpace` and `termAccession` hardcoded like in the example above. A field can be re-used for different terms and those -can be set with the field is added automatically. See the +can be set when the field is added automatically. See the :ref:`Automate Adding a Field to a Content Type` section. The defaultStorageSettings() Function @@ -561,7 +561,7 @@ between field settings and field storage settings. In the example above the first line calls ``parent::defaultStorageSettings()``. this will retrieve the default settings for all Chado fields. This includes a setting named ``base_table`` in the ``storage_plugin_settings`` array. -The ``ChadoStorage`` backend requires a ``base_table`` setting to tell it what table +The ``ChadoStorage`` backend always requires a ``base_table`` setting to tell it what table of Chado this field works with. Tripal will pass to the storage backend any settings in the ``storage_plugin_settings`` array. But you are free to add any additional settings you would like to help manage your field, especially if those settings @@ -639,7 +639,7 @@ ensure that values provided to fields are appropriate. You can read more about defining validation contraints for fields `here `_. -For following code example, is from the `ChadoStringTypeDefault` field. It wants +The following code example is from the `ChadoStringTypeDefault` field. It wants to ensure that that max length of the string is not exceeded. .. code:: php @@ -678,14 +678,14 @@ A property type is actually an object, thus, this function returns an array of p objects. See the :ref:`Property Types` section below for more information about these object classes. -In the code block below you can see the steps where the field settings are +In the example code block below you can see the steps where the field settings are retrieved, and then used to create an array containing a single property. More about properties is described in the next section. -Note that we return from this function early if we do not have a base table +Note that we return from this function early if we do not yet have a base table defined. This will happen during manual addition of a field through the GUI. One of the first steps during this process is to select the base table, so -before that is selected, this function simply returns. +before that is selected, this function should return without doing anything. .. code-block:: php @@ -743,7 +743,7 @@ All of these classes can be instantiated with the following arguments: - The field ID: the unique ID of the field this property belongs to. - The property "key": a unique key for this property. - The property controlled vocabulary term: namespace and accession. -- (For ChadoVarCharStoragePropertyType only): The maximum length of the string. +- (For `ChadoVarCharStoragePropertyType` only): The maximum length of the string. - The property settings: an array of settings for this property. See the :ref:`Property Settings` section below for more information on how to specify the property settings array. @@ -752,30 +752,31 @@ Property Settings ``````````````````` The :ref:`Property Types` section above indicated that each property type class -has a fourth argument that provides settings for the property. These settings +has a fourth argument (fifth argument in the case of `ChadoVarCharStoragePropertyType`) +that provides settings for the property. These settings are critical for describing how the property is managed by the ``ChadoStorage`` backend. The settings are an associative array of key-value pairs that specify an "action" to perform for each property and corresponding helper information. Several of the actions use a **path** specification, which is a sequence of join actions to reach the desired column in the desired table. For example if the base -table for the record is `feature` and we want to retrieve the organism species, -then the path would be: `feature.organism_id>organism.organism_id;species`. +table for the record is ``feature`` and we want to retrieve the organism species, +then the path would be: ``feature.organism_id>organism.organism_id;species``. Separate multiple joins with a semicolon. For example to get the infraspecific -type name of an organism: `feature.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id;name`. -If we want a column in the base table, then the path might be as simple as: `feature.feature_id`. +type name of an organism: ``feature.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id;name``. +If we want to specify a column in the base table, then the path might be as simple as: ``feature.feature_id``. The following actions can be used: - **store_id**: indicates that the value of this property will hold the record ID (or primary key ID) of the record in the base table of Chado. Common base tables include: analysis, feature, stock, pub, organism. There will only - be a single store_id action in any given field. This action uses the following + be a single `store_id` action in any given field. This action uses the following key/value pairs: - **path**: (required) this path specifies the primary key of the base table, and is composed of the base table name, a period, and the primary key of - the base table (e.g., feature_id). + the base table (e.g., ``feature.feature_id``). - **drupal_store**: (required) this setting should always be TRUE for this action. - **store_link**: indicates that the value of this property will hold the @@ -783,12 +784,12 @@ The following actions can be used: is required for fields that provide ancillary information about a record but that information is not stored in a column of the base table, but instead in a linked table. Examples for such a situation would be - values from property table: e.g., analysisprop, featureprop, stockprop, etc. + values from property table: e.g., analysisprop, featureprop, feature_synonym, etc. This action uses the following key/value pairs: - - **path**: (required) this path specifies the primary key of the linking table, - and is composed of a join to the linking table's foreign key, for example - `$base_table . '.' . $base_pkey_col . '>' . $linker_table . '.' . $linker_fkey_col`. + - **path**: (required) this path specifies the primary key of the linked table, + and specifies a join to the linked table's foreign key, for example + ``$base_table . '.' . $base_pkey_col . '>' . $linker_table . '.' . $linker_fkey_col``. - **drupal_store**: (required) this setting should always be TRUE for this action. This forces Tripal to store this value in the Drupal field tables. Without this, Tripal cannot link the fields in Drupal with a base record. @@ -798,27 +799,26 @@ The following actions can be used: property with this action is required for fields that provide ancillary information about a record but that information is not stored in a column of the base table, but instead in a linked table. Examples for such a situation would be - values from property table: e.g., analysisprop, featureprop, stockprop, etc. + values from property table: e.g., analysisprop, featureprop, feature_synonym, etc. This action uses the following key/value pairs: - - **chado_table**: (required) the name of the linked table (e.g. analysisprop) - - **chado_column**: (required) the name of the primary key column that - links to the base table (e.g. analysisprop_id) + - **path**: (required) this path specifies the primary key of the linked table, + and specifies a join to the linked table's primary key, for example + ``$base_table . '.' . $base_pkey_col . '>' . $linker_table . '.' . $linker_table_pkey``. - **drupal_store**: (required) this setting should always be TRUE for this action. This forces Tripal to store this value in the Drupal field tables. Without this, Tripal cannot link the fields in Drupal with a base record. -- **store**: indicates that the value of this property should be stored in the +- **store**: indicates that the value of this property should be stored in a Chado table. This action uses the following key/value pairs: - - **chado_table**: (required) the name of the table that this property will - get stored in. - - **chado_column**: (required) the name of the column in the table where this - property value will get stored. + - **path**: (required) this path specifies the table and column that the + value will be stored in, for example + ``$linker_table . '.synonym_id'``. - **delete_if_empty**: (optional) if TRUE and this field is for ancillary data then the ancillary record should be removed if this value is empty. - **empty_value**: (optional) the value that indicates an empty state. This - could be ``0``, an empty string or NULL. Whichever is appropriate for the + could be ``0``, an empty string, or NULL, whichever is appropriate for the property. This value is used in conjunction with the **delete_if_empty** setting. @@ -840,19 +840,18 @@ The following actions can be used: `ChadoStorage` backend will generate, you can rename the `chado_column` with a different name. -- **read_value**: this is almost the same as join, but we will not be modifying the - value if we edit a content type, we just look up the existing value. +- **read_value**: this is almost the same as join, but there will be no modification + to the value if we edit a content type, only look up an existing value. - **replace**: indicates that the value of this property is a tokenized string and should be replaced with values from other properties. - **template**: (required) a string containing the value of the field. The string should contain tokens that will be replaced by values of other properties. Tokens are - surrounded by square brackets and contain the keys of other properties. For example. - if the keys for other properties are "genus", "species", "iftype", "ifname" you can - create a property that builds the full scientific name of an organism with the - following template string: - "[genus] [species] [iftype] [ifname]". + surrounded by square brackets and contain the keys of other properties. For example, + in the `ChadoOrganismTypeDefault` field, a property is created that builds + the full scientific name of an organism with the following template string: + "[organism_genus] [organism_species] [organism_infraspecific_type] [organism_infraspecific_name]". - **function**: indicates that the value of this property will be set by a callback function. You will need to pass the namespace for the @@ -861,12 +860,30 @@ The following actions can be used: The callback function then returns a single value which becomes the value for this property. + - **namespace**: (required) the namespace of the callback function, which may be the + namespace of the field itself, or the namespace of a parent class. + - **function**: (required) the name of the callback function + +.. note:: + + All of the core tripal fields that link to another content type define a `function` property to + retrieve the Drupal entity corresponding to that linked content. We also pass through the + foreign key which is needed to look up the Chado entity. + For an example, see any of the linking fields such as the `ChadoContactTypeDefault` field. + As an example, let's look at the ``tripalTypes()`` function of the field that allows an end-user to add an organism to content. This is a simplified version of the code that is found in the ``tripal_chado\src\Plugin\Field\FieldType\ChadoOrganismTypeDefault.php`` -file of Tripal: +file of Tripal. + +.. warning:: + + This is just an example, the full version of this field is significantly more complicated. + In the full version we store all of the organism table columns, and in addition to + supporting base tables with an `organism_id` column, we also handle organisms connected + through a linking table such as `organism_pub` or `featuremap_organism`. .. code:: php @@ -892,50 +909,69 @@ file of Tripal: $species_len = $organism_def['fields']['species']['size']; $iftype_len = $cvterm_def['fields']['name']['size']; $ifname_len = $organism_def['fields']['infraspecific_name']['size']; - $label_len = $genus_len + $species_len + $iftype_len + $ifname_len; + $scientific_name_len = $genus_len + $species_len + $iftype_len + $ifname_len + 3; // Get the base table columns needed for this field. $base_schema_def = $schema->getTableDef($base_table, ['format' => 'Drupal']); $base_pkey_col = $base_schema_def['primary key']; $base_fk_col = array_keys($base_schema_def['foreign keys']['organism']['columns'])[0]; + // Get the CV terms used for each of the properties + $storage = \Drupal::entityTypeManager()->getStorage('chado_term_mapping'); + $mapping = $storage->load('core_mapping'); + $record_id_term = 'SIO:000729'; + $drupal_entity_term = 'schema:ItemPage'; + $organism_id_term = $mapping->getColumnTermId($base_table, 'organism_id'); + $genus_term = $mapping->getColumnTermId('organism', 'genus'); + $species_term = $mapping->getColumnTermId('organism', 'genus'); + $infraspecific_type_term = $mapping->getColumnTermId('cvterm', 'name'); + $infraspecific_name_term = $mapping->getColumnTermId('organism', 'infraspecific_name'); + $scientific_name_term = 'NCBITaxon:scientific_name'; + // Return the properties for this field. return [ - new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'record_id', [ + new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'record_id', $record_id_term, [ 'action' => 'store_id', 'drupal_store' => TRUE, 'path' => $base_table . '.' . $base_pkey_col, ]), - new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'organism_id', [ + new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'entity_id', $drupal_entity_term, [ + 'action' => 'function', + 'drupal_store' => TRUE, + 'namespace' => self::$chadostorage_namespace, // the namespace of the parent class Drupal\tripal_chado\Plugin\TripalStorage\ChadoStorage + 'function' => self::$drupal_entity_callback, // i.e. drupalEntityIdLookupCallback + 'fkey' => 'organism_id', + ]), + new ChadoIntStoragePropertyType($entity_type_id, self::$id, 'organism_id', $organism_id_term, [ 'action' => 'store', 'drupal_store' => TRUE, 'path' => $base_table . '.' . $base_fk_col, ]), - new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'label', $label_len, [ - 'action' => 'replace', - 'drupal_store' => FALSE, - 'template' => "[genus] [species] [infraspecific_type] [infraspecific_name]", - ]), - new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'genus', $genus_len, [ + new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'organism_genus', $genus_term, $genus_len, [ 'action' => 'read_value', 'drupal_store' => FALSE, 'path' => $base_table . '.organism_id>organism.organism_id;genus', ]), - new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'species', $species_len, [ + new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'organism_species', $species_term, $species_len, [ 'action' => 'read_value', 'drupal_store' => FALSE, 'path' => $base_table . '.organism_id>organism.organism_id;species', ]), - new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'infraspecific_name', $ifname_len, [ + new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'organism_infraspecific_type', $infraspecific_type_term, [ 'action' => 'read_value', 'drupal_store' => FALSE, - 'path' => $base_table . '.organism_id>organism.organism_id;infraspecific_name', + 'path' => $base_table . '.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id;name', + 'as' => 'infraspecific_type' ]), - new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'infraspecific_type', [ + new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'organism_infraspecific_name', $infraspecific_name_term, $ifname_len, [ 'action' => 'read_value', 'drupal_store' => FALSE, - 'path' => $base_table . '.organism_id>organism.organism_id;organism.type_id>cvterm.cvterm_id;name', - 'as' => 'infraspecific_type' + 'path' => $base_table . '.organism_id>organism.organism_id;infraspecific_name', + ]), + new ChadoVarCharStoragePropertyType($entity_type_id, self::$id, 'organism_scientific_name', $scientific_name_term, $scientific_name_len, [ + 'action' => 'replace', + 'drupal_store' => FALSE, + 'template' => "[organism_genus] [organism_species] [organism_infraspecific_type] [organism_infraspecific_name]", ]) ]; } @@ -947,17 +983,23 @@ end-user to enter a numeric organism is not ideal. Also we want our formatter to print a nicely formatted scientific name for the organism. We need more properties. -In the code above, we create seven properties for this field. As required we +In the code above, we create eight properties for this field. As required, we must have a property that uses the action ``store_id`` that will house the record ID (e.g., feature.feature_id). Because this field is supposed to store the ``organism_id`` for the feature, stock, etc., we have a property that uses the action ``store`` and maps to the ``organism_id`` column of the table. -We also have a variety of properties with a join action. These are used to +Because when the field displays the organism on a page, we would like to be able +to click on the organism name, and go to the corresponding organism page, we need +to store the Drupal entity ID for that organism. This is handled by the `entity_id` +property. The field formatter can use this value to create a URI to the organism +page. + +We also have a variety of properties with a `read_value` action. These are used to join on the base table to get information such as the genus, species, -and infraspecific type. Lastly, we have a property with the action ``replace`` -that uses a tokenized string to create a nicely formatted scientific name for -the organism. +and infraspecific type, but these are read-only, the values stored in Chado will +not be modified by this field. Lastly, we have a property with the action ``replace`` +that uses a tokenized string to create the full scientific name for the organism. Implementing a TripalWidgetBase Class From 96544b92df5aa741096628acb86b9ead6e80bd53 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sat, 11 May 2024 16:29:17 -0500 Subject: [PATCH 04/15] fix 9.4.x links --- docs/dev_guide/module_dev/fields.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dev_guide/module_dev/fields.rst b/docs/dev_guide/module_dev/fields.rst index 5ac60a3..6160594 100644 --- a/docs/dev_guide/module_dev/fields.rst +++ b/docs/dev_guide/module_dev/fields.rst @@ -23,7 +23,7 @@ of data in biological databases such as Chado. Not every custom module will require fields. But if you need a new way to store and retrieve data, or if you need data to appear on an existing - Tripal content type then you will want to create a new field for your + Tripal content type, then you will want to create a new field for your custom module. Field Classes @@ -31,13 +31,13 @@ Field Classes Anyone who wants to implement a new field in Drupal must implement three different classes: -- `FieldItemBase `_: +- `FieldItemBase `_: the class that defines a new field. This class interacts directly with the data storage plugin to load and save the data managed by this field. -- `WidgetBase `_: +- `WidgetBase `_: the class that defines the form elements (widgets) provided to the end-user to supply or change the data managed by this field. -- `FormatterBase `_: +- `FormatterBase `_: the class that defines how the field is rendered on the page. These classes were extended by Tripal to provide additional @@ -169,7 +169,7 @@ Default Drupal Behavior ```````````````````````` By default, all built-in fields provided by Drupal store their data in the Drupal database. This is provided by Drupal's -`SqlContentEntityStorage `_ +`SqlContentEntityStorage `_ storage plugin. This storage plugin will create a database table for every field. For example, if you explore the Drupal database tables you will see the following for the body field attached to the node content type: From 61cdb953e29d1a6aec89e8cc939f5a54f633384a Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sun, 12 May 2024 10:08:12 -0500 Subject: [PATCH 05/15] WARNING: Title underline too short --- docs/dev_guide/lessons/custom_tables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev_guide/lessons/custom_tables.rst b/docs/dev_guide/lessons/custom_tables.rst index 9eb1621..2562cb5 100644 --- a/docs/dev_guide/lessons/custom_tables.rst +++ b/docs/dev_guide/lessons/custom_tables.rst @@ -80,7 +80,7 @@ The code above will create an instance of a ``ChadoCustomTable`` object but it d In the code above, the ``$schema`` variable contains the Schema API array defined above. Calling ``setTableSchema()`` will automatically create the table in the Chado schema and return ``TRUE`` on success. If there are any errors in the structure of the ``$schema`` array or any problems creating the table, messages will be logged to Drupal, the attempt will fail and the function will return ``FALSE``. Locking a Custom Table ---------------------- +----------------------- Tripal provides to the site developers an interface by which they can add custom tables. Site developers can see custom tables in the interface which allows them to delete them, rename them or alter them. If you are adding a custom table for use by your extension module and you do not want the site developers to alter it in any way, you can lock the table. Non-custom Chado tables are not available for alteration and custom tables that are necessary for the functioning of a module should not be either. After creation of your custom table, you can lock the table from the site developers by calling the ``setLocked()`` function on the ``ChadoCustomTable`` object and passing ``TRUE`` as the only argument. From 9e328cbbfd0d42183228af22357fcccb1a4a2c81 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sun, 12 May 2024 11:10:46 -0500 Subject: [PATCH 06/15] WARNING: Inline literal start-string without end-string --- docs/dev_guide/biodata/importers.rst | 2 +- docs/site_building/example_genomic/organisms.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev_guide/biodata/importers.rst b/docs/dev_guide/biodata/importers.rst index 1bf2338..8564bcb 100644 --- a/docs/dev_guide/biodata/importers.rst +++ b/docs/dev_guide/biodata/importers.rst @@ -298,7 +298,7 @@ When the **Tripal Job** system executes an importer job it will call three diffe - ``preRun()``: contains code to be executed prior to the the ``run()`` function. - ``run()``: contains the code that performs the import of the file. -- ``postRun()``: contains code to be executed after executiong of the ``run()` function. +- ``postRun()``: contains code to be executed after executiong of the ``run()`` function. These functions were added to our class as "stubs" in Step 3 above and now we discuss each of these. diff --git a/docs/site_building/example_genomic/organisms.rst b/docs/site_building/example_genomic/organisms.rst index 8d65d17..77a9efb 100755 --- a/docs/site_building/example_genomic/organisms.rst +++ b/docs/site_building/example_genomic/organisms.rst @@ -83,7 +83,7 @@ Run the jobs as mentioned earlier followed by : docker exec -it $cntr_name drush cr -to clear the drush cache. Now, clicking on **Tripal Content -> Organism=Citrus ** will show Taxonomy Reference Annotation of NCBI 2711 associated with Citrus sinensis. Editing of this organism also shows it. +to clear the drush cache. Now, clicking on **Tripal Content -> Organism=Citrus** will show Taxonomy Reference Annotation of NCBI 2711 associated with Citrus sinensis. Editing of this organism also shows it. From b70ab78841581c04a5788f89233d9738500e0a27 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sun, 12 May 2024 11:22:35 -0500 Subject: [PATCH 07/15] WARNING: duplicate label install tripal --- docs/install.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 49451e0..c451dac 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,6 +1,6 @@ -Install Tripal -================ +Installing Tripal +================== The recommended method to get involved in Tripal 4 development is through a Tripal Docker installation. This most closely mimics the environment automated tests are run on. From 926cd33a8fdda089ea738ab478d02321d5d5f9ae Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sun, 12 May 2024 11:26:04 -0500 Subject: [PATCH 08/15] WARNING: duplicate label fields --- docs/dev_guide/testing/fields.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev_guide/testing/fields.rst b/docs/dev_guide/testing/fields.rst index 9144af2..dda0fde 100644 --- a/docs/dev_guide/testing/fields.rst +++ b/docs/dev_guide/testing/fields.rst @@ -1,6 +1,6 @@ -Fields -========= +Field Tests +============ For fields there are three main components to test: From 4b1c1cf8a502df2b112353f0c656d32ae95cd55c Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sun, 12 May 2024 11:32:46 -0500 Subject: [PATCH 09/15] Split into multiple pages --- docs/dev_guide/module_dev.rst | 2 +- .../module_dev/fields/formatters.rst | 14 + docs/dev_guide/module_dev/fields/overview.rst | 273 ++++++++++++++++ .../{fields.rst => fields/types.rst} | 295 +----------------- docs/dev_guide/module_dev/fields/widgets.rst | 13 + docs/dev_guide/module_dev/tripal_fields.rst | 37 +++ 6 files changed, 344 insertions(+), 290 deletions(-) create mode 100644 docs/dev_guide/module_dev/fields/formatters.rst create mode 100644 docs/dev_guide/module_dev/fields/overview.rst rename docs/dev_guide/module_dev/{fields.rst => fields/types.rst} (70%) create mode 100644 docs/dev_guide/module_dev/fields/widgets.rst create mode 100644 docs/dev_guide/module_dev/tripal_fields.rst diff --git a/docs/dev_guide/module_dev.rst b/docs/dev_guide/module_dev.rst index 35223de..9fbc580 100644 --- a/docs/dev_guide/module_dev.rst +++ b/docs/dev_guide/module_dev.rst @@ -11,7 +11,7 @@ Custom Module Development module_dev/file_structure module_dev/routing module_dev/entities - module_dev/fields + module_dev/tripal_fields module_dev/forms module_dev/logging module_dev/views diff --git a/docs/dev_guide/module_dev/fields/formatters.rst b/docs/dev_guide/module_dev/fields/formatters.rst new file mode 100644 index 0000000..7706cab --- /dev/null +++ b/docs/dev_guide/module_dev/fields/formatters.rst @@ -0,0 +1,14 @@ +Field Formatters +=================================== + +Implementing a ChadoFormatterBase Class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + + This documentation is still being developed. In the meantime there are examples + in the Tripal core codebase. Specifically, look in the + `tripal_chado/src/Plugin/Field/FieldFormatter` directory. + +The next section :ref:`Field Widgets` will describe how to create a widget +for this new field to allow editing content. diff --git a/docs/dev_guide/module_dev/fields/overview.rst b/docs/dev_guide/module_dev/fields/overview.rst new file mode 100644 index 0000000..a1073c9 --- /dev/null +++ b/docs/dev_guide/module_dev/fields/overview.rst @@ -0,0 +1,273 @@ + +Fields Overview +================ + +Fields are the building blocks of content in Drupal. For example, all content +types (e.g. "Article", or "Basic Page") provide content to the end-user via +fields that are bundled with them. For example, when adding a basic +page (a default Drupal content type), the end-user is provided with form +elements (or widgets) that allow the user to set the title and the body text +for the page. The "Body" is a field. When a basic page is +viewed, the body is rendered on the page using formatters, and +Drupal stores the values for the body in the database. Every +field, therefore, provides three types of functionality: instructions +for storage, widgets for allowing input, and formatters for rendering. + +Drupal provides a variety of built-in fields, and extension module developers +have created a multitude of new fields that can be added by the site admin +to add new functionality and support new types of data. Tripal follows this +model, but adds a variety of new object oriented classes to support storage +of data in biological databases such as Chado. + +.. note:: + + Not every custom module will require fields. But if you need a new way + to store and retrieve data, or if you need data to appear on an existing + Tripal content type, then you will want to create a new field for your + custom module. + +Field Classes +--------------- +Anyone who wants to implement a new field in Drupal must implement three +different classes: + +- `FieldItemBase `_: + the class that defines a new field. This class interacts directly with the + data storage plugin to load and save the data managed by this field. +- `WidgetBase `_: + the class that defines the form elements (widgets) provided to the end-user + to supply or change the data managed by this field. +- `FormatterBase `_: + the class that defines how the field is rendered on the page. + +These classes were extended by Tripal to provide additional +functionality that allows Tripal-based fields to communicate with additional +data stores housing biological data. There is support for a number of +types of datastores (e.g. MySQL, PostgreSQL, SQLite) in core Drupal but you are +required to choose a single data store for your site. The extension to support +multiple data stores provided by Tripal allows you to keep your biological data +separate from the website and still available to scientific analysis and +visualization tools. + +Chado is the default data store implemented within Tripal as it offers flexible +support for a wide breadth of biological data types, ontology-focused metadata, +and robust data integrity. The following documentation will demonstrate how to +develop custom fields with data stored in Chado. However, the Tripal data storage +plugin and Tripal Fields are designed to work with additional data stores and +documentation showing how to take advantage of this will be written in the future. + +Custom module developers who wish to add new fields to Tripal whose data are +stored in Chado should implement the following three classes for every new field: + +- **ChadoFieldItemBase**: extends the Tripal class `TripalFieldItemBase` + which extends the Drupal class `FieldItemBase`. The `TripalFieldItemBase` + must be used for all fields attached to Tripal content types, and the + `ChadoFieldItemBase` adds Chado-specific support. +- **ChadoWidgetBase**: extends the Tripal class `TripalWidgetBase` + which extends the Drupal class `WidgetBase`. +- **ChadoFormatterBase**: extends the Tripal class `TripalFormatterBase` + which extends the Drupal class `FormatterBase`. + + +How to Write a New Field for Chado +------------------------------------ + +Directory Setup +^^^^^^^^^^^^^^^^ +Drupal manages fields using its `Plugin API `_. +This means that as long as new field classes are placed in the correct directory +and have the correct "annotations" in the class comments, then Drupal will find them +and make the field available. All new fields must be placed in the custom +extension module inside of the `src/Plugin/Field` directory. There are three +subdirectories, one each for the three elements of a field: +`FieldType`, `FieldWidget`, `FieldFormatter`. For a new field named `MyField` +the directory structure would look like the following: + + +.. code:: + + mymodule + ├── config + ├── src + │   └── Plugin + │    └── Field + │         ├── FieldFormatter + | | └── MyFieldFormatter.php + │         ├── FieldType + | | └── MyFieldType.php + │         └── FieldWidget + | └── MyFieldWidget.php + | + ├── tests + └── templates + +Note that the file name must match the class name. + +Naming convention +^^^^^^^^^^^^^^^^^ + +The filename for your new field should adhere to the following schema. Please +note the casing used. In addition, for fields that will be included in Tripal +Core, note the 'Default' designation, any fields added by extension modules +should **not** use 'Default': + + .. table:: Tripal Core modules: + + +------------------+-----------------------------+ + | File | Filename | + +==================+=============================+ + | Type | MyFieldTypeDefault.php | + +------------------+-----------------------------+ + | Formatter | MyFieldFormatterDefault.php | + +------------------+-----------------------------+ + | Widget | MyFieldWidgetDefault.php | + +------------------+-----------------------------+ + + .. table:: Extension modules: + + +------------------+-----------------------------+ + | File | Filename | + +==================+=============================+ + | Type | MyFieldType.php | + +------------------+-----------------------------+ + | Formatter | MyFieldFormatter.php | + +------------------+-----------------------------+ + | Widget | MyFieldWidget.php | + +------------------+-----------------------------+ + +Within the individual files, in the annotation section, the ID also has to follow +a specific format, and would look like the following: + + .. table:: Tripal Core modules: + + +------------------+----------------------------+ + | File | ID within annotation | + +==================+============================+ + | Type | my_field_type_default | + +------------------+----------------------------+ + | Formatter | my_field_formatter_default | + +------------------+----------------------------+ + | Widget | my_field_widget_default | + +------------------+----------------------------+ + + .. table:: Extension modules: + + +------------------+----------------------------+ + | File | ID within annotation | + +==================+============================+ + | Type | my_field_type | + +------------------+----------------------------+ + | Formatter | my_field_formatter | + +------------------+----------------------------+ + | Widget | my_field_widget | + +------------------+----------------------------+ + +About the Storage Backend +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Default Drupal Behavior +```````````````````````` +By default, all built-in fields provided by Drupal store their data in the +Drupal database. This is provided by Drupal's +`SqlContentEntityStorage `_ +storage plugin. This storage plugin will create a database table for every field. +For example, if you explore the Drupal database tables you will see the +following for the body field attached to the node content type: + +.. code:: + + Table "public.node__body" + Column | Type | Collation | Nullable | Default + --------------+------------------------+-----------+----------+----------------------- + bundle | character varying(128) | | not null | ''::character varying + deleted | smallint | | not null | 0 + entity_id | bigint | | not null | + revision_id | bigint | | not null | + langcode | character varying(32) | | not null | ''::character varying + delta | bigint | | not null | + body_value | text | | not null | + body_summary | text | | | + body_format | character varying(255) | | | + Indexes: + "node__body____pkey" PRIMARY KEY, btree (entity_id, deleted, delta, langcode) + "node__body__body_format__idx" btree (body_format) + "node__body__bundle__idx" btree (bundle) + "node__body__revision_id__idx" btree (revision_id) + Check constraints: + "node__body_delta_check" CHECK (delta >= 0) + "node__body_entity_id_check" CHECK (entity_id >= 0) + "node__body_revision_id_check" CHECK (revision_id >= 0) + +The values provided by the user for the body of a node type are housed in this +table. The following describes the columns of the table. + +These columns are present for all fields + +- `bundle`: the machine name of the content type (e.g. node) +- `deleted`: a value of 1 indicates the field is marked for deletion +- `entity_id`: the unique ID of the node that this field belongs to. +- `revision_id`: the node revision ID. +- `langcode`: for fields that are translatable, this indicates the language + of the saved value. +- `delta`: for fields that support multiple values, this is the index (starting + at zero) for the order of the values. + +These columns are specific to the field: + +- `body_value`: stores the value for the body +- `body_summary`: stores the body summary +- `body_format`: instructions for how the body should be rendered (e.g. plain + text, HTML, etc.) + + +Support for Chado +``````````````````` +For fields storing biological data in something other than Drupal tables, +Tripal provides its own plugin named `TripalStorage`. If a custom module wants to +store data in a data backend other than in Drupal tables, it must create an implementation +of this plugin. By default, Tripal provides the `ChadoStorage` implementation +that allows a field to interact with a Chado database. + +The `ChadoStorage` backend extends the `SqlContentEntityStorage` and +will create a table in the Drupal schema for every Tripal field that is +added to a content type. The table columns will include the same default +columns as a Drupal field, and will also include a set of additional columns +for every property the field wants to manage. + +The `ChadoStorage` backend is different from `SqlContentEntityStorage` +in that it will not store the values of the properties in the table. This is +because those values need to be stored in Chado--we do not want to duplicate +the data in the Drupal schema and the Chado schema. The `ChadoStorage` +backend is also different in that it requires a set of property settings that +help it control how properties of a field are stored, edited and loaded from +Chado. Instructions for working with properties and storing data in Chado are +described in the following sections. + +.. note:: + + The `ChadoStorage` backend will not store biological data in the Drupal + tables--only in the Chado tables. The only exceptions are record IDs that + associate the field with data in Chado. + + +See the next section :ref:`Field Types` to learn how to define the +properties for a new field. + +Automate Adding a Field to a Content Type +------------------------------------------ + +.. warning:: + + This documentation is still being developed. In the meantime there are + examples for programmatically adding TripalFields in the Tripal core codebase. + Specifically, look in the Chado Preparer class in + `tripal_chado/src/Task/ChadoPreparer.php`. + +What About Fields not for Chado? +--------------------------------- + +.. warning:: + + This documentation is still being developed. Currently ChadoStorage provides + an example for implementing the TripalStorage data store extension. It can be + found in `tripal_chado/src/Plugin/TripalStorage/ChadoStorage.php`. diff --git a/docs/dev_guide/module_dev/fields.rst b/docs/dev_guide/module_dev/fields/types.rst similarity index 70% rename from docs/dev_guide/module_dev/fields.rst rename to docs/dev_guide/module_dev/fields/types.rst index 6160594..124ea7a 100644 --- a/docs/dev_guide/module_dev/fields.rst +++ b/docs/dev_guide/module_dev/fields/types.rst @@ -1,256 +1,8 @@ -Fields (content building blocks) -================================== - -Fields are the building blocks of content in Drupal. For example, all content -types (e.g. "Article", or "Basic Page") provide content to the end-user via -fields that are bundled with them. For example, when adding a basic -page (a default Drupal content type), the end-user is provided with form -elements (or widgets) that allow the user to set the title and the body text -for the page. The "Body" is a field. When a basic page is -viewed, the body is rendered on the page using formatters, and -Drupal stores the values for the body in the database. Every -field, therefore, provides three types of functionality: instructions -for storage, widgets for allowing input, and formatters for rendering. - -Drupal provides a variety of built-in fields, and extension module developers -have created a multitude of new fields that can be added by the site admin -to add new functionality and support new types of data. Tripal follows this -model, but adds a variety of new object oriented classes to support storage -of data in biological databases such as Chado. +Field Types +============ -.. note:: - - Not every custom module will require fields. But if you need a new way - to store and retrieve data, or if you need data to appear on an existing - Tripal content type, then you will want to create a new field for your - custom module. - -Field Classes ---------------- -Anyone who wants to implement a new field in Drupal must implement three -different classes: - -- `FieldItemBase `_: - the class that defines a new field. This class interacts directly with the - data storage plugin to load and save the data managed by this field. -- `WidgetBase `_: - the class that defines the form elements (widgets) provided to the end-user - to supply or change the data managed by this field. -- `FormatterBase `_: - the class that defines how the field is rendered on the page. - -These classes were extended by Tripal to provide additional -functionality that allows Tripal-based fields to communicate with additional -data stores housing biological data. There is support for a number of -types of datastores (e.g. MySQL, PostgreSQL, SQLite) in core Drupal but you are -required to choose a single data store for your site. The extension to support -multiple data stores provided by Tripal allows you to keep your biological data -separate from the website and still available to scientific analysis and -visualization tools. - -Chado is the default data store implemented within Tripal as it offers flexible -support for a wide breadth of biological data types, ontology-focused metadata, -and robust data integrity. The following documentation will demonstrate how to -develop custom fields with data stored in Chado. However, the Tripal data storage -plugin and Tripal Fields are designed to work with additional data stores and -documentation showing how to take advantage of this will be written in the future. - -Custom module developers who wish to add new fields to Tripal whose data are -stored in Chado should implement the following three classes for every new field: - -- **ChadoFieldItemBase**: extends the Tripal class `TripalFieldItemBase` - which extends the Drupal class `FieldItemBase`. The `TripalFieldItemBase` - must be used for all fields attached to Tripal content types, and the - `ChadoFieldItemBase` adds Chado-specific support. -- **ChadoWidgetBase**: extends the Tripal class `TripalWidgetBase` - which extends the Drupal class `WidgetBase`. -- **ChadoFormatterBase**: extends the Tripal class `TripalFormatterBase` - which extends the Drupal class `FormatterBase`. - - -How to Write a New Field for Chado ------------------------------------- - -Directory Setup -^^^^^^^^^^^^^^^^ -Drupal manages fields using its `Plugin API `_. -This means that as long as new field classes are placed in the correct directory -and have the correct "annotations" in the class comments, then Drupal will find them -and make the field available. All new fields must be placed in the custom -extension module inside of the `src/Plugin/Field` directory. There are three -subdirectories, one each for the three elements of a field: -`FieldType`, `FieldWidget`, `FieldFormatter`. For a new field named `MyField` -the directory structure would look like the following: - - -.. code:: - - mymodule - ├── config - ├── src - │   └── Plugin - │    └── Field - │         ├── FieldFormatter - | | └── MyFieldFormatter.php - │         ├── FieldType - | | └── MyFieldType.php - │         └── FieldWidget - | └── MyFieldWidget.php - | - ├── tests - └── templates - -Note that the file name must match the class name. - -Naming convention -^^^^^^^^^^^^^^^^^ - -The filename for your new field should adhere to the following schema. Please -note the casing used. In addition, for fields that will be included in Tripal -Core, note the 'Default' designation, any fields added by extension modules -should **not** use 'Default': - - .. table:: Tripal Core modules: - - +------------------+-----------------------------+ - | File | Filename | - +==================+=============================+ - | Type | MyFieldTypeDefault.php | - +------------------+-----------------------------+ - | Formatter | MyFieldFormatterDefault.php | - +------------------+-----------------------------+ - | Widget | MyFieldWidgetDefault.php | - +------------------+-----------------------------+ - - .. table:: Extension modules: - - +------------------+-----------------------------+ - | File | Filename | - +==================+=============================+ - | Type | MyFieldType.php | - +------------------+-----------------------------+ - | Formatter | MyFieldFormatter.php | - +------------------+-----------------------------+ - | Widget | MyFieldWidget.php | - +------------------+-----------------------------+ - -Within the individual files, in the annotation section, the ID also has to follow -a specific format, and would look like the following: - - .. table:: Tripal Core modules: - - +------------------+----------------------------+ - | File | ID within annotation | - +==================+============================+ - | Type | my_field_type_default | - +------------------+----------------------------+ - | Formatter | my_field_formatter_default | - +------------------+----------------------------+ - | Widget | my_field_widget_default | - +------------------+----------------------------+ - - .. table:: Extension modules: - - +------------------+----------------------------+ - | File | ID within annotation | - +==================+============================+ - | Type | my_field_type | - +------------------+----------------------------+ - | Formatter | my_field_formatter | - +------------------+----------------------------+ - | Widget | my_field_widget | - +------------------+----------------------------+ - -About the Storage Backend -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Default Drupal Behavior -```````````````````````` -By default, all built-in fields provided by Drupal store their data in the -Drupal database. This is provided by Drupal's -`SqlContentEntityStorage `_ -storage plugin. This storage plugin will create a database table for every field. -For example, if you explore the Drupal database tables you will see the -following for the body field attached to the node content type: - -.. code:: - - Table "public.node__body" - Column | Type | Collation | Nullable | Default - --------------+------------------------+-----------+----------+----------------------- - bundle | character varying(128) | | not null | ''::character varying - deleted | smallint | | not null | 0 - entity_id | bigint | | not null | - revision_id | bigint | | not null | - langcode | character varying(32) | | not null | ''::character varying - delta | bigint | | not null | - body_value | text | | not null | - body_summary | text | | | - body_format | character varying(255) | | | - Indexes: - "node__body____pkey" PRIMARY KEY, btree (entity_id, deleted, delta, langcode) - "node__body__body_format__idx" btree (body_format) - "node__body__bundle__idx" btree (bundle) - "node__body__revision_id__idx" btree (revision_id) - Check constraints: - "node__body_delta_check" CHECK (delta >= 0) - "node__body_entity_id_check" CHECK (entity_id >= 0) - "node__body_revision_id_check" CHECK (revision_id >= 0) - -The values provided by the user for the body of a node type are housed in this -table. The following describes the columns of the table. - -These columns are present for all fields - -- `bundle`: the machine name of the content type (e.g. node) -- `deleted`: a value of 1 indicates the field is marked for deletion -- `entity_id`: the unique ID of the node that this field belongs to. -- `revision_id`: the node revision ID. -- `langcode`: for fields that are translatable, this indicates the language - of the saved value. -- `delta`: for fields that support multiple values, this is the index (starting - at zero) for the order of the values. - -These columns are specific to the field: - -- `body_value`: stores the value for the body -- `body_summary`: stores the body summary -- `body_format`: instructions for how the body should be rendered (e.g. plain - text, HTML, etc.) - - -Support for Chado -``````````````````` -For fields storing biological data in something other than Drupal tables, -Tripal provides its own plugin named `TripalStorage`. If a custom module wants to -store data in a data backend other than in Drupal tables, it must create an implementation -of this plugin. By default, Tripal provides the `ChadoStorage` implementation -that allows a field to interact with a Chado database. - -The `ChadoStorage` backend extends the `SqlContentEntityStorage` and -will create a table in the Drupal schema for every Tripal field that is -added to a content type. The table columns will include the same default -columns as a Drupal field, and will also include a set of additional columns -for every property the field wants to manage. - -The `ChadoStorage` backend is different from `SqlContentEntityStorage` -in that it will not store the values of the properties in the table. This is -because those values need to be stored in Chado--we do not want to duplicate -the data in the Drupal schema and the Chado schema. The `ChadoStorage` -backend is also different in that it requires a set of property settings that -help it control how properties of a field are stored, edited and loaded from -Chado. Instructions for working with properties and storing data in Chado are -described in the following sections. - -.. note:: - - The `ChadoStorage` backend will not store biological data in the Drupal - tables--only in the Chado tables. The only exceptions are record IDs that - associate the field with data in Chado. - - -Implementing a ChadoFieldItemBase Class +Implementing a ChadoFieldItemType Class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When creating a new Tripal field, the first class that must be created is the "type" class. This must extend the `ChadoFieldItemBase` class. @@ -266,7 +18,7 @@ field is used to provide the genus. Tripal provides some ready-to-use field classes for single-values. These are: - **ChadoIntegerTypeDefault**: for integer data. -- **ChadoStringTypeDefault**: for string data with a max length. +- **ChadoStringTypeDefault**: for string data with a maximum length. - **ChadoTextTypeDefault**: for string data with unlimited length. - **ChadoRealTypeDefault**: for real (floating point) numeric data. - **ChadoBoolTypeDefault**: for boolean data. @@ -1001,40 +753,5 @@ and infraspecific type, but these are read-only, the values stored in Chado will not be modified by this field. Lastly, we have a property with the action ``replace`` that uses a tokenized string to create the full scientific name for the organism. - -Implementing a TripalWidgetBase Class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: - - This documentation is still being developed. In the meantime there are examples - in the Tripal core codebase. Specifically, look in the - `tripal_chado/src/Plugin/Field/FieldWidget` directory. - -Implementing a TripalFormatterBase Class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: - - This documentation is still being developed. In the meantime there are examples - in the Tripal core codebase. Specifically, look in the - `tripal_chado/src/Plugin/Field/FieldFormatter` directory. - -Automate Adding a Field to a Content Type ------------------------------------------- - -.. warning:: - - This documentation is still being developed. In the meantime there are - examples for programmatically adding TripalFields in the Tripal core codebase. - Specifically, look in the Chado Preparer class in - `tripal_chado/src/Task/ChadoPreparer.php`. - -What About Fields not for Chado? ---------------------------------- - -.. warning:: - - This documentation is still being developed. Currently ChadoStorage provides - an example for implementing the TripalStorage data store extension. It can be - found in `tripal_chado/src/Plugin/TripalStorage/ChadoStorage.php`. +The next section :ref:`Field Formatters` will describe how to create a formatter +for this new field. diff --git a/docs/dev_guide/module_dev/fields/widgets.rst b/docs/dev_guide/module_dev/fields/widgets.rst new file mode 100644 index 0000000..d02fcfd --- /dev/null +++ b/docs/dev_guide/module_dev/fields/widgets.rst @@ -0,0 +1,13 @@ +Field Widgets +============== + + +Implementing a ChadoWidget Class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + + This documentation is still being developed. In the meantime there are examples + in the Tripal core codebase. Specifically, look in the + `tripal_chado/src/Plugin/Field/FieldWidget` directory. + diff --git a/docs/dev_guide/module_dev/tripal_fields.rst b/docs/dev_guide/module_dev/tripal_fields.rst new file mode 100644 index 0000000..9549603 --- /dev/null +++ b/docs/dev_guide/module_dev/tripal_fields.rst @@ -0,0 +1,37 @@ + +Tripal Fields (Content building blocks) +======================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + fields/overview + fields/types + fields/formatters + fields/widgets + +Fields are the building blocks of content in Drupal. For example, all content +types (e.g. "Article", or "Basic Page") provide content to the end-user via +fields that are bundled with them. For example, when adding a basic +page (a default Drupal content type), the end-user is provided with form +elements (or widgets) that allow the user to set the title and the body text +for the page. The "Body" is a field. When a basic page is +viewed, the body is rendered on the page using formatters, and +Drupal stores the values for the body in the database. Every +field, therefore, provides three types of functionality: instructions +for storage, widgets for allowing input, and formatters for rendering. + +Drupal provides a variety of built-in fields, and extension module developers +have created a multitude of new fields that can be added by the site admin +to add new functionality and support new types of data. Tripal follows this +model, but adds a variety of new object oriented classes to support storage +of data in biological databases such as Chado. + +.. note:: + + Not every custom module will require fields. But if you need a new way + to store and retrieve data, or if you need data to appear on an existing + Tripal content type, then you will want to create a new field for your + custom module. + From 44837f88b6361cb7ecfe36835363860d3f3d6f26 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Sun, 12 May 2024 21:45:01 -0500 Subject: [PATCH 10/15] draft of the formatter page --- .../module_dev/fields/formatters.rst | 253 +++++++++++++++++- docs/dev_guide/module_dev/fields/types.rst | 54 ++-- docs/dev_guide/module_dev/fields/widgets.rst | 25 +- 3 files changed, 301 insertions(+), 31 deletions(-) diff --git a/docs/dev_guide/module_dev/fields/formatters.rst b/docs/dev_guide/module_dev/fields/formatters.rst index 7706cab..ac6b19e 100644 --- a/docs/dev_guide/module_dev/fields/formatters.rst +++ b/docs/dev_guide/module_dev/fields/formatters.rst @@ -1,13 +1,260 @@ Field Formatters -=================================== +================== Implementing a ChadoFormatterBase Class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When creating a new Tripal field, the class responsible for displaying +information to the site users is the "Formatter" class. +This class extends the `ChadoFormatterBase` class. + +To illustrate with a simple example, we will create a simple field to display the +organism's scientific name on a germplasm page. +We earlier created a property to store this, ``organism_scientific_name``, +so the formatter will use the value from that property. + +Formatter Class Setup +``````````````````````` +To create a new field, we will extend the `ChadoFormatterBase` class. +For a new field named `MyField` we would create a new file in our module here: +`src/Plugin/Field/FieldFormatter/MyfieldFormatter.php` +The following is a simple class example: + +.. code-block:: php + + $item) { + $values = [ + 'entity_id' => $item->get('entity_id')->getString(), + 'scientific_name' => $item->get('organism_scientific_name')->getString(), + ]; + + // Create a clickable link to the corresponding entity when one exists. + $renderable_item = $lookup_manager->getRenderableItem($values['scientific_name'], $values['entity_id']); + + $list[$delta] = $renderable_item; + } + + // If only one element has been found, don't make into a list. + if (count($list) == 1) { + $elements = $list; + } + // If more than one value has been found, display all values in an + // unordered list. + elseif (count($list) > 1) { + $elements[0] = [ + '#theme' => 'item_list', + '#list_type' => 'ul', + '#items' => $list, + '#wrapper_attributes' => ['class' => 'container'], + ]; + } + + return $elements; + } + + } + +Below is a line-by-line explanation of each section of the code snippet above. + +Formatter Namespace and Use Statements +```````````````````````````````````````` + +The following should always be present and specifies the namespace for this +field. + +.. code-block:: php + + namespace Drupal\mymodule\Plugin\Field\FieldFormatter; + + +.. note:: + + Be sure to change `mymodule` in the `namespace` to the name of your module. .. warning:: - This documentation is still being developed. In the meantime there are examples - in the Tripal core codebase. Specifically, look in the + If you misspell the `namespace` your field will not work properly. + +The following "use" statement is required for all Chado fields. + +.. code-block:: php + + use Drupal\tripal_chado\TripalField\ChadoFormatterBase; + +Unless you are sure that your field will only handle a single value in all +cases, you will likely want the following use statement to allow the +formatter to handle a list of multiple items. + +.. code-block:: php + + use Drupal\Core\Field\FieldItemListInterface; + + +Formatter Annotation Section +`````````````````````````````` +The annotation section in the class file is the set of in-line comments for the class. +This annotation is required. +This section is similar to the annotation section in the previous Type class. +Note the ``field_types`` annotation, which specifies what field types this formatter can be used +for. This should match the ``id`` annotation in the Type class. + +.. code-block:: php + + /** + * Plugin implementation of an organism scientific name formatter. + * + * @FieldFormatter( + * id = "my_field_formatter", + * label = @Translation("Organism scientific name formatter"), + * description = @Translation("A chado organism scientific name formatter"), + * field_types = { + * "my_field" + * }, + * ) + */ + +.. note:: + + Multiple formatters can exist for a given field, and the site + administrator can select which formatter is appropriate for a + particular content type. + +.. warning:: + + If the annotation section is not present, has misspellings, or is not + complete, the field will not be recognized by Drupal. + +Formatter Class Definition +```````````````````````````` + +Next, the class definition line must extend the `ChadoFormatterBase` class. You +must name your class the same as the filename in which it is contained (minus +the `.php` extension). + +.. code-block:: php + + class MyFieldFormatter extends ChadoFormatterBase { + +.. warning:: + + If you misspell the class name such that it is not the same as the filename + of the file in which it is contained, then the field will not be recognized by + Drupal. + +The defaultSettings() Function +````````````````````````````````````` +This is an optional function. If your field requires some additional settings +for content display. An example might be how many decimal places to display +for a real numner. This example does not add any settings. + +.. code-block:: php + + public static function defaultSettings() { + $settings = parent::defaultSettings(); + return $settings; + } + +The viewElements() Function +```````````````````````````` + +The `viewElements()` function is used to retrieve one or more property types +managed by this field, and prepare them for display. Our example will just display +a single value, but some fields can be configured for a cardinality greater than +one, meaning multiple values may be present, so the values can be made into a +list or a table as appropriate. + +In the example code block below you can see the steps where the property values + +.. code-block:: php + + public function viewElements(FieldItemListInterface $items, $langcode) { + $elements = []; + $list = []; + $lookup_manager = \Drupal::service('tripal.tripal_entity.lookup'); + + foreach ($items as $delta => $item) { + $values = [ + 'entity_id' => $item->get('entity_id')->getString(), + 'scientific_name' => $item->get('organism_scientific_name')->getString(), + ]; + +Then for each retrieved item, we convert the returned value into a render array +item using the `tripal.tripal_entity.lookup` service. When possible, the item +includes a link to the corresponding organism entity page, and if not, then +plain markup is generated. Either way, it is then added to the array of values to display. + +.. code-block:: php + + // Create a clickable link to the corresponding entity when one exists. + $renderable_item = $lookup_manager->getRenderableItem($values['scientific_name'], $values['entity_id']); + + $list[$delta] = $renderable_item; + } + +The last step is to populate the `$elements` array. If there is only one value, as will be the case for this +example, it becomes the only value in the array. When multiple values are present, however, we can present them +as a list. Alternatively, you might want to display them in a table format. +For an example of a table formatter, see the `ChadoSequenceCoordinatesFormatterTable` formatter. + +.. code-block:: php + + // If only one element has been found, don't make into a list. + if (count($list) == 1) { + $elements = $list; + } + // If more than one value has been found, display all values in an + // unordered list. + elseif (count($list) > 1) { + $elements[0] = [ + '#theme' => 'item_list', + '#list_type' => 'ul', + '#items' => $list, + '#wrapper_attributes' => ['class' => 'container'], + ]; + } + + return $elements; + } + +.. note:: + + A good way to learn about fields is to look at examples of fields in the Tripal + core codebase. Specifically, look in the `tripal_chado/src/Plugin/Field/FieldFormatter` directory. The next section :ref:`Field Widgets` will describe how to create a widget diff --git a/docs/dev_guide/module_dev/fields/types.rst b/docs/dev_guide/module_dev/fields/types.rst index 124ea7a..8cd48c7 100644 --- a/docs/dev_guide/module_dev/fields/types.rst +++ b/docs/dev_guide/module_dev/fields/types.rst @@ -1,11 +1,13 @@ Field Types -============ +============= -Implementing a ChadoFieldItemType Class +Implementing a ChadoFieldItemBase Class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When creating a new Tripal field, the first class that must be created is the -"type" class. This must extend the `ChadoFieldItemBase` class. +"Type" class. This class extends the `ChadoFieldItemBase` class. +This "Type" class will specify the columns of one or more Chado tables that +the field needs to display its content. Single-Value Fields ````````````````````` @@ -13,7 +15,7 @@ A single-value field is the simplest Chado field. This is a field that manages a data value from a single column in a single Chado table. For example, the `genus` column of the `organism` table of Chado stores the genus of an organism. For the organism pages provided by Tripal, a single-value -field is used to provide the genus. +field could be used to provide the genus. Tripal provides some ready-to-use field classes for single-values. These are: @@ -40,7 +42,7 @@ A complex field is one that manages multiple properties (or multiple values) wit of a complex field is one that stores/loads the organism of a germplasm content type. Within Chado, a record in the `stock` table is used to store germplasm data. The `stock` table has a foreign key constraint with the `organism` table. Therefore, -a germplasm page must provide a field that allows the user to specify an organism +a germplasm page will usually include a field that allows the user to specify an organism for saving. It should also format the organism name for display. In practice, the `stock` table stores the numeric `organism_id` when saving @@ -54,16 +56,18 @@ Instead what we need is: user will never see. - A field that has access to the genus, species, infraspecific type, infraspecific name, etc. of the organism. -- A widget (form element) that allows the user to select an existing organism. -- A formatter that displays the full scientific name of the organism. +- A widget (form element) that allows the user to select an existing + organism when adding or editing a `stock` record. +- A formatter that displays the full scientific name of the organism, and + might include other information such as the organism's common name. -Class Setup -````````````` -To create a new field, we will extend the `ChadoFieldItemBase`. For a new -field named `MyField` we would create a new file in our module here: -`src/Plugin/Field/FieldType/MyfieldType.php`. The following is an empty -class example: +Type Class Setup +`````````````````` +To create a new field, we will extend the `ChadoFieldItemBase` class. +For a new field named `MyField` we would create a new file in our module here: +`src/Plugin/Field/FieldType/MyfieldType.php` +The following is a simple class example: .. code-block:: php @@ -81,7 +85,7 @@ class example: * Plugin implementation of Tripal string field type. * * @FieldType( - * id = "MyField", + * id = "my_field", * label = @Translation("MyField Field"), * description = @Translation("An example field"), * default_widget = "MyFieldWidget", @@ -90,7 +94,7 @@ class example: */ class MyField extends ChadoFieldItemBase { - public static $id = "MyField"; + public static $id = "my_field"; /** * {@inheritdoc} @@ -167,8 +171,8 @@ class example: Below is a line-by-line explanation of each section of the code snippet above. -Namespace and Use Statements -`````````````````````````````` +Type Namespace and Use Statements +``````````````````````````````````` The following should always be present and specifies the namespace for this field. @@ -205,8 +209,8 @@ classes you could import if needed. use Drupal\tripal_chado\TripalStorage\ChadoTextStoragePropertyType; -Annotation Section -```````````````````` +Type Annotation Section +````````````````````````` The annotation section in the class file is the set of in-line comments for the class. Note the @FieldType stanza in the comments. Drupal @@ -234,8 +238,8 @@ and formatter class. This annotation is required. complete, the field will not be recognized by Drupal. -Class Definition -`````````````````` +Type Class Definition +``````````````````````` Next, the class definition line must extend the `ChadoFieldItemBase` class. You must name your class the same as the filename in which it is contained (minus @@ -593,7 +597,7 @@ The following actions can be used: with a different name. - **read_value**: this is almost the same as join, but there will be no modification - to the value if we edit a content type, only look up an existing value. + to the value if we edit a content type, we only look up an existing value. - **replace**: indicates that the value of this property is a tokenized string and should be replaced with values from other properties. @@ -753,5 +757,11 @@ and infraspecific type, but these are read-only, the values stored in Chado will not be modified by this field. Lastly, we have a property with the action ``replace`` that uses a tokenized string to create the full scientific name for the organism. +.. note:: + + A good way to learn about fields is to look at examples of fields in the Tripal + core codebase. Specifically, look in the + `tripal_chado/src/Plugin/Field/FieldType` directory. + The next section :ref:`Field Formatters` will describe how to create a formatter for this new field. diff --git a/docs/dev_guide/module_dev/fields/widgets.rst b/docs/dev_guide/module_dev/fields/widgets.rst index d02fcfd..f60976d 100644 --- a/docs/dev_guide/module_dev/fields/widgets.rst +++ b/docs/dev_guide/module_dev/fields/widgets.rst @@ -1,13 +1,26 @@ Field Widgets -============== +=============== +Implementing a ChadoWidgetBase Class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When creating a new Tripal field, the class responsible for allowing +entry or editing of content by a site administrator is the "Widget" class. +This class extends the `ChadoWidgetBase` class. -Implementing a ChadoWidget Class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Class Setup +````````````` +To create a new field, we will extend the `ChadoWidgetBase` class. +For a new field named `MyField` we would create a new file in our module here: +`src/Plugin/Field/FieldWidget/MyfieldWidget.php` +The following is a simple class example: -.. warning:: +.. code-block:: php - This documentation is still being developed. In the meantime there are examples - in the Tripal core codebase. Specifically, look in the + Date: Tue, 14 May 2024 11:42:10 -0500 Subject: [PATCH 11/15] part of widget documented --- .../module_dev/fields/formatters.rst | 33 +-- docs/dev_guide/module_dev/fields/widgets.rst | 203 ++++++++++++++++++ 2 files changed, 222 insertions(+), 14 deletions(-) diff --git a/docs/dev_guide/module_dev/fields/formatters.rst b/docs/dev_guide/module_dev/fields/formatters.rst index ac6b19e..5535133 100644 --- a/docs/dev_guide/module_dev/fields/formatters.rst +++ b/docs/dev_guide/module_dev/fields/formatters.rst @@ -124,7 +124,6 @@ formatter to handle a list of multiple items. use Drupal\Core\Field\FieldItemListInterface; - Formatter Annotation Section `````````````````````````````` The annotation section in the class file is the set of in-line comments for the class. @@ -177,10 +176,10 @@ the `.php` extension). Drupal. The defaultSettings() Function -````````````````````````````````````` +```````````````````````````````` This is an optional function. If your field requires some additional settings for content display. An example might be how many decimal places to display -for a real numner. This example does not add any settings. +for a real number. This example does not add any settings. .. code-block:: php @@ -190,7 +189,7 @@ for a real numner. This example does not add any settings. } The viewElements() Function -```````````````````````````` +````````````````````````````` The `viewElements()` function is used to retrieve one or more property types managed by this field, and prepare them for display. Our example will just display @@ -199,6 +198,7 @@ one, meaning multiple values may be present, so the values can be made into a list or a table as appropriate. In the example code block below you can see the steps where the property values +are retrieved from the ``$items`` array.. .. code-block:: php @@ -213,22 +213,25 @@ In the example code block below you can see the steps where the property values 'scientific_name' => $item->get('organism_scientific_name')->getString(), ]; -Then for each retrieved item, we convert the returned value into a render array +Then for each retrieved item, we convert the returned value into a +`render array `_ item using the `tripal.tripal_entity.lookup` service. When possible, the item includes a link to the corresponding organism entity page, and if not, then -plain markup is generated. Either way, it is then added to the array of values to display. +plain markup is generated. Either way, it is then added to the array +``$list`` of values to display. .. code-block:: php // Create a clickable link to the corresponding entity when one exists. - $renderable_item = $lookup_manager->getRenderableItem($values['scientific_name'], $values['entity_id']); - + $renderable_item = $lookup_manager->getRenderableItem($values['scientific_name'], + $values['entity_id']); $list[$delta] = $renderable_item; } -The last step is to populate the `$elements` array. If there is only one value, as will be the case for this -example, it becomes the only value in the array. When multiple values are present, however, we can present them -as a list. Alternatively, you might want to display them in a table format. +The last step is to populate the ``$elements`` array. If there is only one value, +as will be the case for this example, it becomes the only value in the array. +However, when multiple values are present, we can present them as a list. +Alternatively, you might want to display them in a table format. For an example of a table formatter, see the `ChadoSequenceCoordinatesFormatterTable` formatter. .. code-block:: php @@ -251,11 +254,13 @@ For an example of a table formatter, see the `ChadoSequenceCoordinatesFormatterT return $elements; } +This completes your field formatter! + +The next section :ref:`Field Widgets` will describe how to create a widget +for this new field to allow editing content. + .. note:: A good way to learn about fields is to look at examples of fields in the Tripal core codebase. Specifically, look in the `tripal_chado/src/Plugin/Field/FieldFormatter` directory. - -The next section :ref:`Field Widgets` will describe how to create a widget -for this new field to allow editing content. diff --git a/docs/dev_guide/module_dev/fields/widgets.rst b/docs/dev_guide/module_dev/fields/widgets.rst index f60976d..cab2f0b 100644 --- a/docs/dev_guide/module_dev/fields/widgets.rst +++ b/docs/dev_guide/module_dev/fields/widgets.rst @@ -18,6 +18,209 @@ The following is a simple class example: getValue(); + $record_id = $item_vals['record_id'] ?? 0; + $organism_id = $item_vals['organism_id'] ?? 0; + + $elements = []; + $elements['record_id'] = [ + '#type' => 'value', + '#default_value' => $record_id, + ]; + $elements['organism_id'] = $element + [ + '#type' => 'select', + '#options' => $organisms, + '#default_value' => $organism_id, + '#empty_option' => '-- Select --', + ]; + + return $elements; + } + + /** + * {@inheritDoc} + */ + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + // Remove any empty values that don't have an organism + foreach ($values as $delta => $item) { + if ($item['organism_id'] == '') { + unset($values[$delta]); + } + } + + // Reset the weights + $i = 0; + foreach ($values as $val_key => $value) { + $values[$val_key]['_weight'] = $i; + $i++; + } + return $values; + } + + } + +Below is a line-by-line explanation of each section of the code snippet above. + +Formatter Namespace and Use Statements +```````````````````````````````````````` + +The following should always be present and specifies the namespace for this +field. + +.. code-block:: php + + namespace Drupal\mymodule\Plugin\Field\FieldWidget; + +.. note:: + + Be sure to change `mymodule` in the `namespace` to the name of your module. + +.. warning:: + + If you misspell the `namespace` your field will not work properly. + +The following "use" statements are required for all Chado fields. + +.. code-block:: php + + use Drupal\Core\Field\FieldItemListInterface; + use Drupal\Core\Form\FormStateInterface; + use Drupal\tripal_chado\TripalField\ChadoWidgetBase; + +Widget Annotation Section +`````````````````````````````` +The annotation section in the class file is the set of in-line comments for the class. +This annotation is required. +This section is similar to the annotation section in the previous Type and Formatter classes. +Note the ``field_types`` annotation, which specifies what field types this formatter can be used +for. This should match the ``id`` annotation in the Type class. + +.. code-block:: php + + /** + * Plugin implementation of organism name widget. + * + * @FieldWidget( + * id = "my_field_widget", + * label = @Translation("Organism Scientific Name Widget"), + * description = @Translation("A chado organism scientific name widget."), + * field_types = { + * "my_field" + * } + * ) + */ + +.. warning:: + + If the annotation section is not present, has misspellings, or is not + complete, the field will not be recognized by Drupal. + +Widget Class Definition +```````````````````````````` + +Next, the class definition line must extend the `ChadoWidgetBase` class. You +must name your class the same as the filename in which it is contained (minus +the `.php` extension). + +.. code-block:: php + + class MyFieldWidget extends ChadoWidgetBase { + +.. warning:: + + If you misspell the class name such that it is not the same as the filename + of the file in which it is contained, then the field will not be recognized by + Drupal. + +The formElement() Function +````````````````````````````` +The `formElement()` function is used to define how the form for editing the +field content is constructed. +For our example, a site administrator will want to select one of the existing +organisms to attach to the current content type. + +In the example code block below you can see the steps where the list of existing +organisms is retrieved using a Chado API function. + +.. code-block:: php + + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + + // Get the list of organisms. Second parameter true includes common names. + $organisms = chado_get_organism_select_options(FALSE, TRUE); + +Next we retrieve the values for the existing organism, or zero if one is not set. + +.. code-block:: php + + $item_vals = $items[$delta]->getValue(); + $record_id = $item_vals['record_id'] ?? 0; + $organism_id = $item_vals['organism_id'] ?? 0; + +Next we define two form elements. The first one, ``record_id`` is not +shown on the form, it is the value of the Drupal entity. This is the "host" +page on which this field is being displayed + +.. code-block:: php + + $elements = []; + $elements['record_id'] = [ + '#type' => 'value', + '#default_value' => $record_id, + ]; + +The second form element is an organism select. This select list contains all +organisms currently defined, and the site administrator can select the +appropriate organism. + +.. code-block:: php + + $elements['organism_id'] = $element + [ + '#type' => 'select', + '#options' => $organisms, + '#default_value' => $organism_id, + '#empty_option' => '-- Select --', + ]; + + return $elements; + } + +.. note:: + + For fields with a large number of possible items, it may be more appropriate + to use an autocomplete field. @@@to-do describe this + + + + .. note:: A good way to learn about fields is to look at examples of fields in the Tripal From fe86dd0d32a492b045f243e6e31f28c3fb4b478d Mon Sep 17 00:00:00 2001 From: dsenalik Date: Thu, 13 Jun 2024 16:34:13 -0500 Subject: [PATCH 12/15] fix duplicated label --- docs/dev_guide/module_dev/fields/widgets.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev_guide/module_dev/fields/widgets.rst b/docs/dev_guide/module_dev/fields/widgets.rst index cab2f0b..4242cc9 100644 --- a/docs/dev_guide/module_dev/fields/widgets.rst +++ b/docs/dev_guide/module_dev/fields/widgets.rst @@ -89,8 +89,8 @@ The following is a simple class example: Below is a line-by-line explanation of each section of the code snippet above. -Formatter Namespace and Use Statements -```````````````````````````````````````` +Widgets Namespace and Use Statements +`````````````````````````````````````` The following should always be present and specifies the namespace for this field. From bedfde366e5ceacaac177aad838f92433b25eab9 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Mon, 15 Jul 2024 12:09:22 -0500 Subject: [PATCH 13/15] remove duplicated section --- docs/dev_guide/module_dev/fields/overview.rst | 4 +-- docs/dev_guide/module_dev/fields/widgets.rst | 6 +++-- docs/dev_guide/module_dev/tripal_fields.rst | 25 ------------------- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/docs/dev_guide/module_dev/fields/overview.rst b/docs/dev_guide/module_dev/fields/overview.rst index a1073c9..832e5da 100644 --- a/docs/dev_guide/module_dev/fields/overview.rst +++ b/docs/dev_guide/module_dev/fields/overview.rst @@ -4,10 +4,10 @@ Fields Overview Fields are the building blocks of content in Drupal. For example, all content types (e.g. "Article", or "Basic Page") provide content to the end-user via -fields that are bundled with them. For example, when adding a basic +fields that are bundled with the content type. For example, when adding a basic page (a default Drupal content type), the end-user is provided with form elements (or widgets) that allow the user to set the title and the body text -for the page. The "Body" is a field. When a basic page is +for the page. The "Body" is a field. When a basic page is viewed, the body is rendered on the page using formatters, and Drupal stores the values for the body in the database. Every field, therefore, provides three types of functionality: instructions diff --git a/docs/dev_guide/module_dev/fields/widgets.rst b/docs/dev_guide/module_dev/fields/widgets.rst index 4242cc9..beef6a9 100644 --- a/docs/dev_guide/module_dev/fields/widgets.rst +++ b/docs/dev_guide/module_dev/fields/widgets.rst @@ -199,7 +199,8 @@ page on which this field is being displayed The second form element is an organism select. This select list contains all organisms currently defined, and the site administrator can select the -appropriate organism. +appropriate organism. If editing an existing record, then the current +organism is presented as the default. .. code-block:: php @@ -216,7 +217,8 @@ appropriate organism. .. note:: For fields with a large number of possible items, it may be more appropriate - to use an autocomplete field. @@@to-do describe this + to use an autocomplete field. + @@@to-do describe this diff --git a/docs/dev_guide/module_dev/tripal_fields.rst b/docs/dev_guide/module_dev/tripal_fields.rst index 9549603..1700ea2 100644 --- a/docs/dev_guide/module_dev/tripal_fields.rst +++ b/docs/dev_guide/module_dev/tripal_fields.rst @@ -10,28 +10,3 @@ Tripal Fields (Content building blocks) fields/types fields/formatters fields/widgets - -Fields are the building blocks of content in Drupal. For example, all content -types (e.g. "Article", or "Basic Page") provide content to the end-user via -fields that are bundled with them. For example, when adding a basic -page (a default Drupal content type), the end-user is provided with form -elements (or widgets) that allow the user to set the title and the body text -for the page. The "Body" is a field. When a basic page is -viewed, the body is rendered on the page using formatters, and -Drupal stores the values for the body in the database. Every -field, therefore, provides three types of functionality: instructions -for storage, widgets for allowing input, and formatters for rendering. - -Drupal provides a variety of built-in fields, and extension module developers -have created a multitude of new fields that can be added by the site admin -to add new functionality and support new types of data. Tripal follows this -model, but adds a variety of new object oriented classes to support storage -of data in biological databases such as Chado. - -.. note:: - - Not every custom module will require fields. But if you need a new way - to store and retrieve data, or if you need data to appear on an existing - Tripal content type, then you will want to create a new field for your - custom module. - From a5da01d14faa3c666f8c8147f93f4868232ddc11 Mon Sep 17 00:00:00 2001 From: dsenalik Date: Thu, 15 Aug 2024 15:15:16 -0500 Subject: [PATCH 14/15] finish widget --- docs/dev_guide/module_dev/fields/widgets.rst | 31 ++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/dev_guide/module_dev/fields/widgets.rst b/docs/dev_guide/module_dev/fields/widgets.rst index beef6a9..519fbd4 100644 --- a/docs/dev_guide/module_dev/fields/widgets.rst +++ b/docs/dev_guide/module_dev/fields/widgets.rst @@ -218,9 +218,37 @@ organism is presented as the default. For fields with a large number of possible items, it may be more appropriate to use an autocomplete field. - @@@to-do describe this + A working example of this can be found in the additional type field in + `tripal_chado/src/Plugin/Field/FieldWidget/ChadoAdditionalTypeWidgetDefault.php`. + Additional documentation about adding an autocomplete to a form can be found in + :ref:`The form() function`. +The massageFormValues() function +`````````````````````````````````` +This function is called afther the form is submitted by the user, and takes care of +removing any records that may have been present earlier, but were removed before saving. +This is a simple example, but your field may need to do more, particularly if the field +is referencing a linked table. + +.. code-block:: php + + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + // Remove any empty values that don't have an organism + foreach ($values as $delta => $item) { + if ($item['organism_id'] == '') { + unset($values[$delta]); + } + } + + // Reset the weights + $i = 0; + foreach ($values as $val_key => $value) { + $values[$val_key]['_weight'] = $i; + $i++; + } + return $values; + } .. note:: @@ -228,4 +256,3 @@ organism is presented as the default. A good way to learn about fields is to look at examples of fields in the Tripal core codebase. Specifically, look in the `tripal_chado/src/Plugin/Field/FieldWidget` directory. - From b098836ccd9e1abe6e869aa9316160662b6d6802 Mon Sep 17 00:00:00 2001 From: Lacey Sanderson Date: Mon, 23 Sep 2024 11:35:13 -0600 Subject: [PATCH 15/15] Small reorganizing for better flow. --- .../module_dev/fields/auto_attach.rst | 13 ++++ .../fields/custom_field_backend.rst | 10 +++ docs/dev_guide/module_dev/fields/overview.rst | 73 ++++--------------- docs/dev_guide/module_dev/tripal_fields.rst | 28 ++++++- 4 files changed, 64 insertions(+), 60 deletions(-) create mode 100644 docs/dev_guide/module_dev/fields/auto_attach.rst create mode 100644 docs/dev_guide/module_dev/fields/custom_field_backend.rst diff --git a/docs/dev_guide/module_dev/fields/auto_attach.rst b/docs/dev_guide/module_dev/fields/auto_attach.rst new file mode 100644 index 0000000..7d8396d --- /dev/null +++ b/docs/dev_guide/module_dev/fields/auto_attach.rst @@ -0,0 +1,13 @@ +Add Fields to Content Types +============================ + +In order for a field to be used, it needs to be added to a specific content type. This can be done through the "Manage Fields" interface for a given Content Type. More specifically, go to Tripal > Page Structure and then choose "Manage Fields" for the Content Type you want to add a field to. + +This can also be done programmatically, however documentation for this is still being developed. + +.. warning:: + + This documentation is still being developed. In the meantime there are + examples for programmatically adding TripalFields in the Tripal core codebase. + Specifically, look in the Chado Preparer class in + `tripal_chado/src/Task/ChadoPreparer.php`. diff --git a/docs/dev_guide/module_dev/fields/custom_field_backend.rst b/docs/dev_guide/module_dev/fields/custom_field_backend.rst new file mode 100644 index 0000000..c450bb7 --- /dev/null +++ b/docs/dev_guide/module_dev/fields/custom_field_backend.rst @@ -0,0 +1,10 @@ +Non-Chado Field Storage +========================= + +The TripalStorage plugin supports providing custom backends for fields. You can have fields with different storage backends on the same Content Type. + +.. warning:: + + This documentation is still being developed. Currently ChadoStorage provides + an example for implementing the TripalStorage data store extension. It can be + found in `tripal_chado/src/Plugin/TripalStorage/ChadoStorage.php`. diff --git a/docs/dev_guide/module_dev/fields/overview.rst b/docs/dev_guide/module_dev/fields/overview.rst index 832e5da..c1c3351 100644 --- a/docs/dev_guide/module_dev/fields/overview.rst +++ b/docs/dev_guide/module_dev/fields/overview.rst @@ -1,33 +1,10 @@ -Fields Overview -================ - -Fields are the building blocks of content in Drupal. For example, all content -types (e.g. "Article", or "Basic Page") provide content to the end-user via -fields that are bundled with the content type. For example, when adding a basic -page (a default Drupal content type), the end-user is provided with form -elements (or widgets) that allow the user to set the title and the body text -for the page. The "Body" is a field. When a basic page is -viewed, the body is rendered on the page using formatters, and -Drupal stores the values for the body in the database. Every -field, therefore, provides three types of functionality: instructions -for storage, widgets for allowing input, and formatters for rendering. - -Drupal provides a variety of built-in fields, and extension module developers -have created a multitude of new fields that can be added by the site admin -to add new functionality and support new types of data. Tripal follows this -model, but adds a variety of new object oriented classes to support storage -of data in biological databases such as Chado. - -.. note:: - - Not every custom module will require fields. But if you need a new way - to store and retrieve data, or if you need data to appear on an existing - Tripal content type, then you will want to create a new field for your - custom module. +Tripal Field Overview +======================= Field Classes --------------- + Anyone who wants to implement a new field in Drupal must implement three different classes: @@ -68,12 +45,9 @@ stored in Chado should implement the following three classes for every new field - **ChadoFormatterBase**: extends the Tripal class `TripalFormatterBase` which extends the Drupal class `FormatterBase`. - -How to Write a New Field for Chado ------------------------------------- - Directory Setup -^^^^^^^^^^^^^^^^ +----------------- + Drupal manages fields using its `Plugin API `_. This means that as long as new field classes are placed in the correct directory and have the correct "annotations" in the class comments, then Drupal will find them @@ -103,8 +77,8 @@ the directory structure would look like the following: Note that the file name must match the class name. -Naming convention -^^^^^^^^^^^^^^^^^ +Naming conventions +------------------- The filename for your new field should adhere to the following schema. Please note the casing used. In addition, for fields that will be included in Tripal @@ -133,9 +107,9 @@ should **not** use 'Default': | Formatter | MyFieldFormatter.php | +------------------+-----------------------------+ | Widget | MyFieldWidget.php | - +------------------+-----------------------------+ + +------------------+-----------------------------+ -Within the individual files, in the annotation section, the ID also has to follow +Within the individual files, in the annotation section, the ID also has to follow a specific format, and would look like the following: .. table:: Tripal Core modules: @@ -148,7 +122,7 @@ a specific format, and would look like the following: | Formatter | my_field_formatter_default | +------------------+----------------------------+ | Widget | my_field_widget_default | - +------------------+----------------------------+ + +------------------+----------------------------+ .. table:: Extension modules: @@ -160,13 +134,13 @@ a specific format, and would look like the following: | Formatter | my_field_formatter | +------------------+----------------------------+ | Widget | my_field_widget | - +------------------+----------------------------+ + +------------------+----------------------------+ About the Storage Backend -^^^^^^^^^^^^^^^^^^^^^^^^^^ +-------------------------- Default Drupal Behavior -```````````````````````` +^^^^^^^^^^^^^^^^^^^^^^^^^ By default, all built-in fields provided by Drupal store their data in the Drupal database. This is provided by Drupal's `SqlContentEntityStorage `_ @@ -221,7 +195,7 @@ These columns are specific to the field: Support for Chado -``````````````````` +^^^^^^^^^^^^^^^^^^ For fields storing biological data in something other than Drupal tables, Tripal provides its own plugin named `TripalStorage`. If a custom module wants to store data in a data backend other than in Drupal tables, it must create an implementation @@ -252,22 +226,3 @@ described in the following sections. See the next section :ref:`Field Types` to learn how to define the properties for a new field. - -Automate Adding a Field to a Content Type ------------------------------------------- - -.. warning:: - - This documentation is still being developed. In the meantime there are - examples for programmatically adding TripalFields in the Tripal core codebase. - Specifically, look in the Chado Preparer class in - `tripal_chado/src/Task/ChadoPreparer.php`. - -What About Fields not for Chado? ---------------------------------- - -.. warning:: - - This documentation is still being developed. Currently ChadoStorage provides - an example for implementing the TripalStorage data store extension. It can be - found in `tripal_chado/src/Plugin/TripalStorage/ChadoStorage.php`. diff --git a/docs/dev_guide/module_dev/tripal_fields.rst b/docs/dev_guide/module_dev/tripal_fields.rst index 1700ea2..be928c0 100644 --- a/docs/dev_guide/module_dev/tripal_fields.rst +++ b/docs/dev_guide/module_dev/tripal_fields.rst @@ -1,7 +1,31 @@ -Tripal Fields (Content building blocks) +Fields (Content building blocks) ======================================== +Fields are the building blocks of content in Drupal. For example, all content +types (e.g. "Article", or "Basic Page") provide content to the end-user via +fields that are bundled with the content type. For example, when adding a basic +page (a default Drupal content type), the end-user is provided with form +elements (or widgets) that allow the user to set the title and the body text +for the page. The "Body" is a field. When a basic page is +viewed, the body is rendered on the page using formatters, and +Drupal stores the values for the body in the database. Every +field, therefore, provides three types of functionality: instructions +for storage, widgets for allowing input, and formatters for rendering. + +Drupal provides a variety of built-in fields, and extension module developers +have created a multitude of new fields that can be added by the site admin +to add new functionality and support new types of data. Tripal follows this +model, but adds a variety of new object oriented classes to support storage +of data in biological databases such as Chado. + +.. note:: + + Not every custom module will require fields. But if you need a new way + to store and retrieve data, or if you need data to appear on an existing + Tripal content type, then you will want to create a new field for your + custom module. + .. toctree:: :maxdepth: 2 :caption: Contents: @@ -10,3 +34,5 @@ Tripal Fields (Content building blocks) fields/types fields/formatters fields/widgets + fields/auto_attach + fields/custom_field_backend