tripal_chado/src/Plugin/TripalVocabulary/ChadoVocabulary.php

Summary

Maintainability
A
0 mins
Test Coverage
B
81%
<?php

namespace Drupal\tripal_chado\Plugin\TripalVocabulary;

use Drupal\tripal\TripalVocabTerms\TripalVocabularyBase;

/**
 * Chado implementation of the TripalVocabularyBase.
 *
 *  @TripalVocabulary(
 *    id = "chado_vocabulary",
 *    label = @Translation("Vocabulary in Chado"),
 *  )
 */
class ChadoVocabulary extends TripalVocabularyBase {

  /**
   * The definition for the `db` table of Chado.
   *
   * @var array
   */
  protected $db_def = NULL;

  /**
   * The definition for the `cv` table of Chado.
   *
   * @var array
   */
  protected $cv_def = NULL;

  /**
   * An instance of the TripalLogger.
   *
   * @var \Drupal\tripal\Services\TripalLogger
   */
  protected $messageLogger = NULL;

  /**
   * A simple boolean to prevent Chado queries if the vocabulary isn't valid.
   *
   * @var bool
   */
  protected $is_valid = False;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);

    // Instantiate the TripalLogger
    $this->messageLogger = \Drupal::service('tripal.logger');

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    // Get the chado definition for the `cv` and `db` tables.
    $this->db_def = $chado->schema()->getTableDef('db', ['Source' => 'file']);
    $this->cv_def = $chado->schema()->getTableDef('cv', ['Source' => 'file']);
  }


  /**
   * {@inheritdoc}
   */
  public function isValid() {

    // Make sure the name of this collection does not exceeed the allowed size in Chado.
    $name = $this->getName();
    if (!empty($name) AND (strlen($name) > $this->cv_def['fields']['name']['size'])) {
      $this->messageLogger->error('ChadoVocabulary: The vocabulary name must not be longer than @size characters. ' +
          'The value provided was: @value',
          ['@size' => $this->cv_def['fields']['name']['size'],
           '@value' => $this->getName()]);

    }
    $this->is_valid = True;

    return $this->is_valid;
  }

  /**
   * {@inheritdoc}
   */
  public function recordExists() {
   $cv = $this->loadVocab();
   if ($cv and $cv['name'] == $this->getName()) {
      return True;
    }
    return False;
  }

  /**
   * {@inheritdoc}
   */
  public function create(){

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    // Check if the record already exists in the database, if it
    // doesn't then insert it.  We don't yet have the definition,
    // but that's okay, the name is all that isrequired to create
    // a record in the `cv` table.
    $vocab = $this->loadVocab();
    if (!$vocab) {
      $query = $chado->insert('1:cv')
        ->fields(['name' => $this->getName()]);
      $query->execute();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function destroy(){
    // The destroy function is meant to delete the vocabulary.
    // But, because CVs and DBs are so critical to almost all
    // data in Chado we don't want to remove the records.
    // Let's let the collection be deleted as far as
    // Tripal is concerned but leave the record in Chado.
    // So, do nothing here.
  }


  /**
   * Loads an Vocbulary record from Chado.
   *
   * This function queries the `cv` table of Chado to get the values
   * for the vocabulary.
   *
   * @return
   *   An associative array containing the columns of the `db1 table
   *   of Chado or NULL if the db could not be found.
   */
  protected function loadVocab() {

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    // Get the Chado `db` record for this ID space.
    $query = $chado->select('1:cv', 'cv')
      ->condition('cv.name', $this->getName(), '=')
      ->fields('cv', ['name', 'definition']);
    $result = $query->execute();
    if ($result) {
      return $result->fetchAssoc();
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getIdSpaceNames(){
    return $this->getIdSpacesCache();
  }

  /**
   * Returns the IDSpace cache ID.
   *
   * @return string
   */
  protected function getIdSpaceCacheID() {

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    $idSpace = $this->getName();
    $chado_schema = $chado->getSchemaName();
    return 'chado_vocabulary_' . $chado_schema . '_' . $idSpace . '_id_spaces';
  }

  /**
   * Sets the ID spaces for this vocabulary in the Drupal cache.
   *
   * The current way to map CV's to DB's is to use the `cv2db`
   * materialized view but there is no guarantee that that mview
   * is up-to-date and it would take too long to force an update
   * every time we need to get the ID spaces for a vocabulary.  This
   * function caches it.
   *
   * @param $id_spaces
   *   An array containing the names of the ID spaces.
   */
  protected function setIdSpacesCache($id_spaces) {
    $cid = $this->getIdSpaceCacheID();
    \Drupal::cache()->set($cid, $id_spaces);
  }

  /**
   * Retrieves from the Drupal cache the ID spaces of this vocabulary.
   *
   * The current way to map CV's to DB's is to use the `cv2db`
   * materialized view but there is no guarantee that that mview
   * is up-to-date and it would take too long to force an update
   * every time we need to get the ID spaces for a vocabulary.  This
   * function retrieves the ID spaces for a vocabulary from a
   * Drupal cache.
   *
   * @return array
   *   An array of ID Space names.
   */
  protected function getIdSpacesCache() {
    $cid = $this->getIdSpaceCacheID();
    $id_spaces = [];
    if ($cache = \Drupal::cache()->get($cid)) {
      $id_spaces = $cache->data;
    }
    return $id_spaces;
  }


  /**
   * {@inheritdoc}
   */
  public function addIdSpace($idSpace){

    // Get the ID collection for this idSpace and save it for future
    // reference, then add the idSpace to our list.
    $idsmanager = \Drupal::service('tripal.collection_plugin_manager.idspace');
    $id = $idsmanager->loadCollection($idSpace);
    if ($id) {
      $id_spaces = $this->getIdSpacesCache();
      $id_spaces[] = $idSpace;
      $this->setIdSpacesCache($id_spaces);
      return True;
    }
    return False;
  }


  /**
   * {@inheritdoc}
   */
  public function removeIdSpace($idSpace){
    $id_spaces = $this->getIdSpacesCache();
    $new_ids = [];
    $found = False;
    foreach ($id_spaces as $name) {
      if ($name != $idSpace) {
        $new_ids[] = $name;
      }
      else {
        $found = True;
      }
    }
    $this->setIdSpacesCache($new_ids);
    return $found;
  }

  /**
   * {@inheritdoc}
   */
  public function getTerms($name, $exact = True){

  }

  /**
   * {@inheritdoc}
   */
  public function getURL(){
    // Don't get a value for a vocabulary that isn't valid.
    if (!$this->is_valid) {
      return NULL;
    }

    // If we don't have any ID spaces then there is no URL.
    $id_spaces = $this->getIdSpacesCache();
    if (count($id_spaces) == 0) {
      $this->messageLogger->error('ChadoVocabulary: Cannot get the URL when no ID spaces are present for the vocabulary.');
      return NULL;
    }

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    // All of the ID spaces for the vocabulary should
    // have the same URL, so only query the first corresponding
    // `db` record to get the URL.
    $db = $chado->select('1:db', 'db')
      ->fields('db', ['url'])
      ->condition('db.name', $id_spaces[0], '=')
      ->execute();
    if (!$db) {
      return NULL;
    }
    return $db->fetchField();
  }

  /**
   * {@inheritdoc}
   */
  public function setURL($url){

    // @todo there may be a problem in the future if we are able to
    // associate borrowed terms with a vocabulary in Chado.  If we
    // add the ID space of borrowed terms to a vocabulary then
    // setting the URL will be incorrect for those ID spaces.

    // Don't set a value for a vocabulary that isn't valid.
    if (!$this->is_valid) {
      return False;
    }

    // If we don't have any ID spaces then there is no URL.
    $id_spaces = $this->getIdSpacesCache();
    if (count($id_spaces) == 0) {
      $this->messageLogger->error('ChadoVocabulary: Cannot set the URL when no ID spaces are present for the vocabulary.');
      return False;
    }

    // This value goes to the Chado `db.url` column, so check it's size
    // to make sure it doesn't exceed it.
    if (strlen($url) > $this->db_def['fields']['url']['size']) {
      $this->messageLogger->error('ChadoVocabulary: The vocabulary name must not be longer than @size characters. ' +
          'The value provided was: @value',
          ['@size' => $this->cv_def['fields']['name']['size'],
            '@value' => $this->getName()]);
      return False;
    }

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    // Update the record in the Chado `db` table for the URL for all ID spaces.
    foreach ($id_spaces as $name) {
      $num_updated = $chado->update('1:db')
        ->fields(['url' => $url])
        ->condition('name', $name, '=')
        ->execute();
      if ($num_updated != 1) {
        $this->messageLogger->error(t('ChadoVocabulary: The URL could not be updated for the vocabulary, "@vocab.',
          ['@vocab' => $this->getName()]));
        return False;
      }
    }
    return True;
  }

  /**
   * Returns the namespace of the vocabulary
   *
   * This should be identical to the name of the collection, and
   * therefore, there is no setter function.
   *
   * @return string $namespace
   *   The namespace of the vocabulary.
   */
  public function getNameSpace() {
    return $this->getName();
  }


  /**
   * {@inheritdoc}
   */
  public function setLabel($label) {
    // Don't set a value for an vocubulary that isn't valid.
    if (!$this->is_valid) {
      return False;
    }

    // Note: there's no need to check the size of the label value
    // because the Chado column where this goes (cv.definition) is an
    // unlimited text field.

    // Instantiate a TripalDBX connection for Chado.
    $chado = \Drupal::service('tripal_chado.database');

    // Update the record in the Chado `cv` table.
    $query = $chado->update('1:cv')
      ->fields(['definition' => $label])
      ->condition('name', $this->getName(), '=');
    $num_updated = $query->execute();
    if ($num_updated != 1) {
      $this->logInvalidCondition('ChadoVocabulary: The label could not be updated for the vocabulary.');
      return False;
    }
    return True;
  }


  /**
   * {@inheritdoc}
   */
  public function getLabel() {
    $cv = $this->loadVocab();
    if (!$cv) {
      return NULL;
    }
    return $cv['definition'];
  }
}