You are here

rdfx.terms.inc in RDF Extensions 7.2

Functions for managing RDF Terms.

File

rdfx.terms.inc
View source
<?php

/**
 * @file
 * Functions for managing RDF Terms.
 */
function rdfx_get_properties() {
  $property_tids = _rdfx_get_terms('property');
  return $property_tids;
}
function rdfx_get_classes() {
  $class_tids = _rdfx_get_terms('class');
  return $class_tids;
}
function _rdfx_get_terms($term_type) {
  $term_types = rdfx_term_types();
  switch ($term_type) {
    case 'property':
      $types = array_keys($term_types['properties']['term_types']);
      break;
    case 'class':
      $types = array_keys($term_types['classes']['term_types']);
      break;
    default:
      $types = array_merge(array_keys($term_types['properties']['term_types']), array_keys($term_types['classes']['term_types']));
  }
  $query = db_select('rdfx_term_types', 'rdftt')
    ->fields('rdft', array(
    'tid',
  ))
    ->condition('rdftt.type', $types, 'IN');
  $query
    ->join('rdfx_terms', 'rdft', 'rdftt.tid = rdft.tid');
  $query
    ->join('rdfx_namespaces', 'rdfns', 'rdfns.nsid = rdft.nsid');
  $query
    ->join('rdfx_vocabulary_graphs', 'rdfvg', 'rdfvg.main_ns = rdfns.nsid');
  $terms = $query
    ->execute()
    ->fetchCol();
  return $terms;
}

/**
 * Gets a list of all defined namespaces.
 */
function rdfx_get_namespaces() {
  $rdf_namespaces =& drupal_static(__FUNCTION__);
  if (empty($rdf_namespaces)) {
    $rdf_namespaces = rdf_get_namespaces();
  }
  return $rdf_namespaces;
}

/**
 * Implements hook_rdf_namespaces.
 */
function rdfx_rdf_namespaces() {

  // Starts with some additionnal common namespaces which core does not include.
  $ns_mappings = array(
    'owl' => 'http://www.w3.org/2002/07/owl#',
    'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
    'rss' => 'http://purl.org/rss/1.0/',
    // url() does not support empty fragment.
    'site' => url('ns', array(
      'absolute' => TRUE,
    )) . '#',
  );

  // Gets the custom namespaces stored in the database.
  $query = db_select('rdfx_vocabulary_graphs', 'g');
  $query
    ->fields('n', array(
    'prefix',
    'uri',
  ));
  $query
    ->join('rdfx_namespaces', 'n', 'g.main_ns = n.nsid');
  $query
    ->orderBy('n.prefix');
  $namespaces = $query
    ->execute()
    ->fetchAllKeyed();
  foreach ($namespaces as $prefix => $uri) {
    $ns_mappings[$prefix] = $uri;
  }
  return $ns_mappings;
}

/**
 * Saves the main namespace mapping for a vocabulary graph and the additional
 * namespace mappings as defined in the document.
 */
function _rdfx_save_vocabulary($main_ns, $main_ns_prefix, $vocabulary) {
  $current_time = REQUEST_TIME;

  // If the vocabulary URI matches the main_ns of a vocabulary source, then
  // this is an update to that record. Otherwise, this is a newly imported
  // source.
  $gid = rdfx_get_gid($main_ns);

  // If there is an existing vocabulary, make sure that the main_ns is in the
  // namespaces array and that the user defined mapping is the last in the
  // array so the prefix reflects the user definition. Also change the
  // vocabulary graph updated date.
  if ($gid) {
    $vocabulary['namespaces'][$main_ns_prefix] = $main_ns;
    db_update('rdfx_vocabulary_graphs')
      ->fields(array(
      'date_updated' => $current_time,
    ))
      ->execute();
  }
  else {

    // @todo If the vocab URI isn't used in any terms, don't add it to ns table.
    // This may happen where multiple files are defining a vocabulary.
    // @todo This should be handled as a transaction in case there is an error
    // in the middle. If there is an error, then there will be an SQL error
    // when the user retries the import.
    // Insert this namespace to get the nsid. The vocabulary_source entry will
    // point to this nsid for the main_ns. We temporarily insert 0 for the gid,
    // then update when we have the real gid.
    $nsid = db_insert('rdfx_namespaces')
      ->fields(array(
      'uri' => $main_ns,
      'prefix' => $main_ns_prefix,
      'gid' => '0',
    ))
      ->execute();
    $gid = db_insert('rdfx_vocabulary_graphs')
      ->fields(array(
      'main_ns' => $nsid,
      'date_created' => $current_time,
      'date_updated' => $current_time,
    ))
      ->execute();
    db_update('rdfx_namespaces')
      ->condition('nsid', $nsid)
      ->fields(array(
      'gid' => $gid,
    ))
      ->execute();
  }

  // Insert/update the vocabulary title.
  if (count($vocabulary['title']) > 0) {
    foreach ($vocabulary['title'] as $langcode => $title) {
      $query = db_merge('rdfx_vocabulary_details')
        ->key(array(
        'gid' => $gid,
        'language' => $langcode,
      ))
        ->fields(array(
        'language' => $langcode,
        'label' => $title,
      ));
      $status = $query
        ->execute();
    }
  }

  // Insert/update the vocabulary description.
  if (count($vocabulary['description']) > 0) {
    foreach ($vocabulary['description'] as $langcode => $description) {
      $query = db_merge('rdfx_vocabulary_details')
        ->key(array(
        'gid' => $gid,
        'language' => $langcode,
      ))
        ->fields(array(
        'language' => $langcode,
        'description' => $description,
      ));
      $status = $query
        ->execute();
    }
  }

  // Insert/update the other namespace mappings used in this vocabulary graph.
  if (count($vocabulary['namespaces']) > 0) {
    foreach ($vocabulary['namespaces'] as $prefix => $namespace) {
      if ($namespace != $main_ns) {
        $query = db_merge('rdfx_namespaces')
          ->key(array(
          'gid' => $gid,
          'uri' => $namespace,
        ))
          ->fields(array(
          'uri' => $namespace,
          'prefix' => $prefix,
          'gid' => $gid,
        ))
          ->updateFields(array(
          'prefix' => $prefix,
        ));
        $status = $query
          ->execute();
      }
    }
  }
  $nsids = rdfx_get_nsids($main_ns);
  return $nsids;
}

/**
 * Saves vocabulary terms.
 */
function rdfx_save_terms($vocabulary_uri, $prefix, $vocabulary) {
  $nsids = _rdfx_save_vocabulary($vocabulary_uri, $prefix, $vocabulary);
  foreach ($vocabulary['terms'] as $term_type => $terms) {
    foreach ($terms as $term_uri => $term_description) {
      list($term_ns, $term_local_name) = rdfx_split_uri($term_uri);
      if (isset($nsids[$term_ns])) {
        $nsid = $nsids[$term_ns];
      }
      else {

        // If the namespace wasn't mapped to a prefix in the source graph, we
        // didn't save it to the namespaces table, so we need to add an entry.
        // @todo For the prefix value, we save the URI... should this be changed?
        $gid = rdfx_get_gid($vocabulary_uri);
        $nsid = db_insert('rdfx_namespaces')
          ->fields(array(
          'uri' => $term_ns,
          'prefix' => $term_ns,
          'gid' => $gid,
        ))
          ->execute();
        $nsids[$term_ns] = $nsid;
      }

      // Get the tid of this term, saving to {rdfx_terms} if not already there.
      $tid = db_query("SELECT tid FROM {rdfx_terms} WHERE nsid = :nsid AND local_name = :localname", array(
        ':nsid' => $nsid,
        ':localname' => $term_local_name,
      ))
        ->fetchField();
      if ($tid == NULL) {
        $tid = db_insert('rdfx_terms')
          ->fields(array(
          'nsid',
          'local_name',
        ))
          ->values(array(
          'nsid' => $nsid,
          'local_name' => $term_local_name,
        ))
          ->execute();
      }

      // Add the current type to this term in {rdfx_term_types}.
      db_merge('rdfx_term_types')
        ->key(array(
        'tid' => $tid,
        'type' => $term_type,
      ))
        ->fields(array(
        'tid' => $tid,
        'type' => $term_type,
      ))
        ->execute();

      // Add label and comment to {rdfx_term_details}.
      $term_details = array();
      if (isset($term_description['label'])) {
        foreach ($term_description['label'] as $lang => $text) {
          $term_details[$lang]['label'] = $text;
        }
      }
      if (isset($term_description['comment'])) {
        foreach ($term_description['comment'] as $lang => $text) {
          $term_details[$lang]['comment'] = $text;
        }
      }
      if (!empty($term_details)) {
        foreach ($term_details as $lang => $details) {
          db_merge('rdfx_term_details')
            ->key(array(
            'tid' => $tid,
            'language' => $lang,
          ))
            ->fields(array(
            'tid' => $tid,
            'language' => $lang,
            'label' => isset($details['label']) ? $details['label'] : NULL,
            'comment' => isset($details['comment']) ? $details['comment'] : NULL,
          ))
            ->execute();
        }
      }

      // Add relationships to their respective tables. This is handled as a
      // complicated set of loops to reduce code duplication. To add a new
      // relationship, just add the array key that is used in
      // $types['properties']['description'] to define the relationship, and
      // then add the name of the table that stores the relationship.
      $relationships = array(
        'domain' => 'rdfx_term_domains',
        'range' => 'rdfx_term_ranges',
      );
      foreach ($relationships as $relationship => $table_name) {
        if (isset($term_description[$relationship])) {
          foreach ($term_description[$relationship] as $related_term) {
            $related_term_tid = rdfx_get_tid($related_term, $vocabulary_uri);
            if ($related_term_tid) {
              db_merge($table_name)
                ->key(array(
                'tid' => $tid,
                $relationship . '_tid' => $related_term_tid,
              ))
                ->fields(array(
                'tid' => $tid,
                $relationship . '_tid' => $related_term_tid,
              ))
                ->execute();
            }
          }
        }
      }
    }
  }

  // @todo Add a hook that passes the $vocabulary and the $model.
}

/**
 * Returns metadata about term types defined by rdf modules.
 *
 * If your module needs to determine what term types are being supplied by
 * other modules, call this function. Querying rdfx database tables directly
 * for this information is discouraged. Any additional term types should be
 * added through the corresponding alter hook.
 *
 * Three major bins of data are stored: tags, value_types, and functions. Each
 * entry in these bins is keyed by the value stored in the actual VotingAPI
 * tables, and contains an array with (minimally) 'name' and 'description' keys.
 * Modules can add extra keys to their entries if desired.
 *
 * This metadata can be modified or expanded using hook_rdfx_term_types_alter().
 *
 * @return
 *   An array of metadata defined by RDFx Terms and altered by rdf modules.
 *
 * @see hook_rdfx_term_types_alter()
 *
 * Modeled on VotingAPI votingapi_metadata.
 */
function rdfx_term_types($reset = FALSE) {
  static $types;
  if ($reset || !isset($types)) {
    $types['classes']['term_types'] = array();
    $types['properties']['term_types'] = array();
    $term_details = '';

    // @todo Should the inference consider subProp and subClass relationships
    // as well. ie. should all OWL classes also have the type RDFS Class
    // @todo Switch to drupal cache
    $types['classes']['term_types'] = array(
      'rdfs_class' => array(
        'uri' => 'http://www.w3.org/2000/01/rdf-schema#Class',
        'inference' => array(
          'http://www.w3.org/2000/01/rdf-schema#subClassOf' => array(
            'subject',
            'object',
          ),
          'http://www.w3.org/2000/01/rdf-schema#domain' => array(
            'object',
          ),
          'http://www.w3.org/2000/01/rdf-schema#range' => array(
            'object',
          ),
        ),
      ),
      'owl_class' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#Class',
        'inference' => array(
          'http://www.w3.org/2002/07/owl#equivalentClass' => array(
            'subject',
            'object',
          ),
          'http://www.w3.org/2002/07/owl#disjointWith' => array(
            'subject',
            'object',
          ),
        ),
      ),
    );
    $types['properties']['term_types'] = array(
      'rdf_property' => array(
        'uri' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property',
        'inference' => array(
          'http://www.w3.org/2000/01/rdf-schema#domain' => array(
            'subject',
          ),
          'http://www.w3.org/2000/01/rdf-schema#range' => array(
            'subject',
          ),
          'http://www.w3.org/2000/01/rdf-schema#subPropertyOf' => array(
            'subject',
            'object',
          ),
          'http://www.w3.org/2002/07/owl#equivalentProperty' => array(
            'subject',
            'object',
          ),
          'http://www.w3.org/2002/07/owl#inverseOf' => array(
            'subject',
            'object',
          ),
        ),
      ),
      'owl_property_datatype' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#DatatypeProperty',
        'inference' => array(),
      ),
      'owl_property_object' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#ObjectProperty',
        'inference' => array(),
      ),
      'owl_property_functional' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#FunctionalProperty',
        'inference' => array(),
      ),
      'owl_property_inverse_functional' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#InverseFunctionalProperty',
        'inference' => array(),
      ),
      'owl_property_symmetric' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#SymmetricProperty',
        'inference' => array(),
      ),
      'owl_property_asymmetric' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#AsymmetricProperty',
        'inference' => array(),
      ),
      'owl_property_annotation' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#AnnotationProperty',
        'inference' => array(),
      ),
      'owl_property_reflexive' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#ReflexiveProperty',
        'inference' => array(),
      ),
      'owl_property_irreflexive' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#IrreflexiveProperty',
        'inference' => array(),
      ),
      'owl_property_transitive' => array(
        'uri' => 'http://www.w3.org/2002/07/owl#TransitiveProperty',
        'inference' => array(),
      ),
    );
    $types['classes']['description'] = array(
      'superclass' => array(
        'http://www.w3.org/2000/01/rdf-schema#subClassOf' => array(
          'object',
        ),
      ),
      'disjoint' => array(
        'http://www.w3.org/2002/07/owl#disjointWith' => array(
          'object',
        ),
      ),
    );
    $types['properties']['description'] = array(
      'domain' => array(
        'http://www.w3.org/2000/01/rdf-schema#domain' => array(
          'object',
        ),
      ),
      'range' => array(
        'http://www.w3.org/2000/01/rdf-schema#range' => array(
          'object',
        ),
      ),
      'superproperty' => array(
        'http://www.w3.org/2000/01/rdf-schema#subPropertyOf' => array(
          'object',
        ),
      ),
      'inverse' => array(
        'http://www.w3.org/2002/07/owl#inverseOf' => array(
          'object',
        ),
      ),
    );
    drupal_alter('rdfx_term_types', $types);
  }
  return $types;
}

/**
 * Splits a URI into namespace and localpart.
 */
function rdfx_split_uri($uri) {
  $parts = ARC2::splitURI($uri);
  return $parts;
}
function rdfx_get_tid($term_uri, $graph_main_ns) {
  $nsids = rdfx_get_nsids($graph_main_ns);
  list($term_ns, $term_local_name) = rdfx_split_uri($term_uri);
  if (isset($nsids[$term_ns])) {
    $tid = db_query("SELECT tid FROM {rdfx_terms} WHERE nsid = :nsid AND local_name = :localname", array(
      ':nsid' => $nsids[$term_ns],
      ':localname' => $term_local_name,
    ))
      ->fetchField();
    return $tid;
  }
  else {
    return NULL;
  }
}
function rdfx_get_gid($main_ns) {
  $gids = db_select('rdfx_namespaces', 'rdfns', array());
  $gids
    ->join('rdfx_vocabulary_graphs', 'rdfvg', 'rdfvg.main_ns = rdfns.nsid');
  $gids
    ->fields('rdfns', array(
    'gid',
  ))
    ->condition('rdfns.uri', $main_ns);

  // @todo There should only be one result if there is a matching vocab source.
  // However, perhaps we should test to make sure and throw an error?
  $gid = $gids
    ->execute()
    ->fetchField();
  return $gid;
}
function rdfx_get_nsids($main_ns) {
  $gid = rdfx_get_gid($main_ns);
  $nsids = db_query("SELECT uri, nsid FROM {rdfx_namespaces} WHERE gid = :gid", array(
    ':gid' => $gid,
  ))
    ->fetchAllKeyed();
  return $nsids;
}
function rdfx_curie($tid) {
  $query = db_select('rdfx_terms', 'rdft')
    ->fields('rdft', array(
    'local_name',
  ))
    ->fields('rdfns', array(
    'prefix',
  ))
    ->condition('rdft.tid', $tid, '=');
  $query
    ->join('rdfx_namespaces', 'rdfns', 'rdfns.nsid = rdft.nsid');
  $result = $query
    ->execute()
    ->fetch();
  $curie = $result->prefix . ':' . $result->local_name;
  return $curie;
}
function _rdfx_get_term_details($tid, $langcode = 'und') {
  $query_language = db_query("SELECT language FROM {rdfx_term_details} WHERE tid = :tid", array(
    ':tid' => $tid,
  ));
  $languages = $query_language
    ->fetchCol();
  if (!in_array($langcode, $languages)) {
    if (in_array('und', $languages)) {
      $langcode = 'und';
    }
    elseif (in_array('en', $languages)) {
      $langcode = 'en';
    }
    else {
      return;
    }
  }
  $query = db_select('rdfx_term_details', 'rdfd')
    ->fields('rdfd', array(
    'label',
    'comment',
  ))
    ->condition('rdfd.tid', $tid, '=')
    ->condition('language', $langcode, '=');
  $details = $query
    ->execute()
    ->fetch();
  return $details;
}

/**
 * Queries a set of triples for classes and properties, and builds
 * an associative array describing the vocabulary and any
 * classes and properties found.
 *
 * @param array $model An ARC2-style array of triples an RDFS vocabulary or OWL ontology
 * @param array $namespaces Associative array of namespaces parsed from the RDF file
 * @param string $ns_prefix Namespace prefix for the vocabulary
 * @param string $ns_uri Only terms in this namespace will be considered
 * @return array Array describing the vocabulary, its classes and properties.
 */
function _rdfx_extract_schema(&$model, $namespaces, $ns_prefix, $ns_uri) {
  $vocabulary_details = _rdfx_get_vocabulary_details($model, $ns_uri);
  $terms = _rdfx_fetch_terms($model);
  $vocabulary = array(
    'uri' => $ns_uri,
    'title' => $vocabulary_details['title'],
    'description' => $vocabulary_details['description'],
    'terms' => $terms,
    'namespaces' => $namespaces,
  );
  return $vocabulary;
}
function _rdfx_fetch_terms(&$model) {
  $terms = array();
  $term_uris = array();

  // Retrieve the queries for term retrieval. This may have been modified by
  // other modules.
  $term_type_groups = rdfx_term_types();
  foreach ($term_type_groups as $term_type_group => $group) {
    foreach ($group['term_types'] as $term_type => $term) {
      $query = array(
        array(
          '?',
          'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
          $term['uri'],
        ),
      );
      foreach ($term['inference'] as $inference_uri => $query_types) {
        foreach ($query_types as $query_type) {
          switch ($query_type) {
            case 'subject':
              $query[] = array(
                '?',
                $inference_uri,
                null,
              );
              break;
            case 'object':
              $query[] = array(
                null,
                $inference_uri,
                '?',
              );
              break;
          }
        }
      }
      $term_uris[$term_type] = _rdfx_query_find_uris($model, $query);

      // Add term details and various relationships for each term, as defined
      // in rdfx_term_types() and altered by hook_rdfx_term_types_alter().
      $query_x = array();
      foreach ($term_uris[$term_type] as $term_uri) {
        $terms[$term_type][$term_uri] = _evoc_query_for_term_description($model, $term_uri);
        foreach ($group['description'] as $property => $queries) {
          foreach ($queries as $predicate_uri => $query_types) {
            foreach ($query_types as $query_type) {
              switch ($query_type) {
                case 'subject':
                  $query_x[$term_uri][$property][] = array(
                    '?',
                    $predicate_uri,
                    $term_uri,
                  );
                  break;
                case 'object':
                  $query_x[$term_uri][$property][] = array(
                    $term_uri,
                    $predicate_uri,
                    '?',
                  );
                  break;
              }
            }
          }
          $terms[$term_type][$term_uri][$property] = _rdfx_query_find_uris($model, $query_x[$term_uri][$property]);
        }
      }
    }
  }
  return $terms;
}
function _rdfx_get_vocabulary_details(&$model, $ns_uri) {
  $query_predicates = array(
    'title' => array(
      'http://www.w3.org/2000/01/rdf-schema#label',
      'http://purl.org/dc/elements/1.1/title',
      'http://purl.org/dc/terms/title',
    ),
    'description' => array(
      'http://www.w3.org/2000/01/rdf-schema#comment',
      'http://purl.org/dc/elements/1.1/description',
      'http://purl.org/dc/terms/description',
    ),
  );
  if (substr($ns_uri, -1) == '#') {
    $uri = substr($ns_uri, 0, -1);
  }
  foreach ($query_predicates as $query_element => $predicates) {
    foreach ($predicates as $predicate) {
      $queries[$query_element][] = array(
        $ns_uri,
        $predicate,
        '?',
      );

      //       if ($uri !== NULL) {
      //         $queries[$query_element][] = array($uri, $predicate, '?');
      //       }
    }
    $details[$query_element] = _rdfx_query_find_literal($model, $queries[$query_element]);
  }
  return $details;
}

Functions

Namesort descending Description
rdfx_curie
rdfx_get_classes
rdfx_get_gid
rdfx_get_namespaces Gets a list of all defined namespaces.
rdfx_get_nsids
rdfx_get_properties @file Functions for managing RDF Terms.
rdfx_get_tid
rdfx_rdf_namespaces Implements hook_rdf_namespaces.
rdfx_save_terms Saves vocabulary terms.
rdfx_split_uri Splits a URI into namespace and localpart.
rdfx_term_types Returns metadata about term types defined by rdf modules.
_rdfx_extract_schema Queries a set of triples for classes and properties, and builds an associative array describing the vocabulary and any classes and properties found.
_rdfx_fetch_terms
_rdfx_get_terms
_rdfx_get_term_details
_rdfx_get_vocabulary_details
_rdfx_save_vocabulary Saves the main namespace mapping for a vocabulary graph and the additional namespace mappings as defined in the document.