sparql_entity_storage-8.x-1.0-alpha8/sparql_entity_storage.module

sparql_entity_storage.module
<?php

/**
 * @file
 * Main functions and hook implementations of the SPARQL entity storage module.
 */

declare(strict_types=1);

use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Database\Database;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\FieldStorageConfigInterface;
use Drupal\sparql_entity_storage\Entity\SparqlMapping;
use Drupal\sparql_entity_storage\SparqlEntityStorageFieldHandler;
use Drupal\sparql_entity_storage\SparqlEntityStorageInterface;
use Drupal\sparql_entity_storage\SparqlGraphInterface;
use EasyRdf\Http;

/**
 * Implements hook_entity_base_field_info_alter().
 */
function sparql_entity_storage_entity_base_field_info_alter(array &$fields, EntityTypeInterface $entity_type) {
  if (!in_array(SparqlEntityStorageInterface::class, class_implements($entity_type->getStorageClass()))) {
    return;
  }

  $fields['graph'] = BaseFieldDefinition::create('entity_reference')
    ->setName('graph')
    ->setLabel(t('The graph where the entity is stored.'))
    ->setTargetEntityTypeId($entity_type->id())
    ->setCustomStorage(TRUE)
    ->setSetting('target_type', 'sparql_graph');
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function sparql_entity_storage_form_field_storage_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Only add mapping form element to fields of entities implementing
  // SparqlEntityStorageInterface.
  $id = $form_state->get('entity_type_id');
  if (!$id) {
    return;
  }
  $storage = \Drupal::entityTypeManager()->getStorage($id);
  if (!$storage instanceof SparqlEntityStorageInterface) {
    return;
  }

  $form_obj = $form_state->getFormObject();
  /** @var \Drupal\field\Entity\FieldStorageConfig $entity */
  $entity = $form_obj->getEntity();
  $schema = $entity->getSchema();
  $form['sparql_mapping'] = [
    '#type' => 'details',
    '#title' => t('SPARQL field mapping'),
    '#description' => t('This field uses a SPARQL backend. Please map the fields to their corresponding RDF properties.'),
    '#weight' => 99,
  ];
  $form_state->set('sparql_mapping_columns', array_keys($schema['columns']));
  foreach ($schema['columns'] as $column => $column_desc) {
    $description = isset($column_desc['description']) ? $column_desc['description'] . "<br>" : '';
    foreach (['type', 'length', 'size', 'serialize'] as $key) {
      if (!empty($column_desc[$key])) {
        $description .= '<strong>' . $key . "</strong>: " . $column_desc[$key] . ' ';
      }
    }

    $settings = sparql_entity_storage_get_mapping_property($entity, 'mapping', $column);

    $form['sparql_mapping'][$column] = [
      '#type' => 'details',
      '#title' => $column,
      '#description' => $description,
    ];

    $form['sparql_mapping'][$column]['predicate_' . $column] = [
      '#type' => 'url',
      '#title' => t('Mapping'),
      '#weight' => 150,
      '#default_value' => $settings['predicate'] ?? '',
    ];

    $form['sparql_mapping'][$column]['format_' . $column] = [
      '#type' => 'select',
      '#title' => t('Value format'),
      '#options' => SparqlEntityStorageFieldHandler::getSupportedDataTypes(),
      '#empty_value' => 'no_format',
      '#weight' => 151,
      '#default_value' => $settings['format'] ?? NULL,
    ];
  }

  $form['#entity_builders'][] = 'sparql_entity_storage_form_alter_builder';
  $form['#validate'][] = 'sparql_entity_storage_field_storage_form_validate';
}

/**
 * Validation callback for the field storage form.
 *
 * @param array $form
 *   Form definition.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state.
 */
function sparql_entity_storage_field_storage_form_validate(array &$form, FormStateInterface $form_state) {
  $cardinality = (int) $form_state->getValue('cardinality_number');
  if ($cardinality < 2 && (int) $form_state->getValue('cardinality') !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
    return;
  }

  $columns = $form_state->get('sparql_mapping_columns');

  // We can only have 1 predicate mapped if the cardinality is higher than 1.
  $predicate_count = 0;
  foreach ($columns as $column) {
    $predicate = $form_state->getValue('predicate_' . $column);
    if ($predicate) {
      $predicate_count++;
    }

    if ($predicate_count > 1) {
      $form_state->setErrorByName('sparql_mapping', t('Multiple SPARQL mapping predicates are not supported with a field cardinality higher than 1.'));
      break;
    }
  }
}

/**
 * Retrieve nested third party settings from object.
 *
 * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $object
 *   The object may be either a bundle entity or a field storage config entity.
 * @param string $property
 *   The property for which to retrieve the mapping.
 * @param string $column
 *   The field column.
 * @param mixed $default
 *   (optional) The default value. Defaults to NULL.
 *
 * @return mixed
 *   The mapping.
 *
 * @todo Move this to a service (or to SPARQL storage?)
 */
function sparql_entity_storage_get_mapping_property(ConfigEntityInterface $object, string $property, string $column, $default = NULL) {
  // Mapping data requested for a configurable field.
  if ($object instanceof FieldStorageConfigInterface) {
    $property_value = $object->getThirdPartySetting('sparql_entity_storage', $property, FALSE);
  }
  // Mapping data requested for a bundle entity.
  else {
    $entity_type_id = $object->getEntityType()->getBundleOf();
    $bundle = $object->id();
    $mapping = SparqlMapping::loadByName($entity_type_id, $bundle);
    $property_value = $mapping->get($property) ?: FALSE;
  }
  if (!is_array($property_value) || !isset($property_value[$column])) {
    return $default;
  }

  return $property_value[$column];
}

/**
 * Entity builder callback: Save the mapping.
 */
function sparql_entity_storage_form_alter_builder($entity_type, FieldStorageConfig $entity, array &$form, FormStateInterface $form_state) {
  $schema = $entity->getSchema();
  $data = [];
  foreach ($schema['columns'] as $column => $column_desc) {
    $data[$column]['predicate'] = $form_state->getValue('predicate_' . $column);
    $data[$column]['format'] = $form_state->getValue('format_' . $column);

  }
  $entity->setThirdPartySetting('sparql_entity_storage', 'mapping', $data);
}

/**
 * Implements hook_form_alter().
 *
 * Configurations for the entity bundle.
 */
function sparql_entity_storage_form_alter(&$form, FormStateInterface $form_state) {
  $form_object = $form_state->getFormObject();
  if (!$form_object instanceof BundleEntityFormBase) {
    return;
  }
  /** @var \Drupal\Core\Config\Entity\ConfigEntityBundleBase $bundle_entity */
  $bundle_entity = $form_object->getEntity();
  if (!$bundle_entity instanceof ConfigEntityBundleBase) {
    return;
  }
  $entity_type_id = $bundle_entity->getEntityType()->getBundleOf();
  $form_state->set('entity_type_id', $entity_type_id);

  /** @var \Drupal\sparql_entity_storage\SparqlEntityStorageInterface $storage */
  $storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
  if (!$storage instanceof SparqlEntityStorageInterface) {
    return;
  }
  $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($entity_type_id);
  $id_key = $storage->getEntityType()->getKey('id');
  $form_state->set('bundle_id_key', $storage->getEntityType()->getKey('bundle'));

  $form['sparql_entity_storage'] = [
    '#type' => 'details',
    '#title' => t('SPARQL Entity Storage'),
    '#description' => t('SPARQL entity storage configurations.'),
    '#open' => TRUE,
    '#weight' => 99,
    '#tree' => TRUE,
  ];

  // When creating a new bundle we can't yet use it for creating a mapping. In
  // this case we defer to the custom submit handler.
  $mapping = NULL;
  if (!$bundle_entity->isNew()) {
    $mapping = SparqlMapping::loadByName($entity_type_id, $bundle_entity->id());
    if (!$mapping) {
      $mapping = SparqlMapping::create([
        'entity_type_id' => $entity_type_id,
        'bundle' => !$bundle_entity->isNew() ? $bundle_entity->id() : NULL,
      ]);
    }
    $form_state->set('sparql_mapping', $mapping);
  }

  $form['sparql_entity_storage']['rdf_type'] = [
    '#type' => 'textfield',
    '#title' => t('RDF type mapping'),
    '#default_value' => $mapping ? $mapping->getRdfType() : NULL,
  ];

  /** @var \Drupal\sparql_entity_storage\SparqlEntityStorageEntityIdPluginManager $id_plugin_manager */
  $id_plugin_manager = \Drupal::service('plugin.manager.sparql_entity_id');
  $plugins = array_map(function (array $definition) {
    return $definition['name'];
  }, $id_plugin_manager->getDefinitions());

  $form['sparql_entity_storage']['entity_id_plugin'] = [
    '#type' => 'select',
    '#title' => t('Entity ID generator'),
    '#description' => t("The generator used to create IDs for new entities."),
    '#options' => $plugins,
    '#default_value' => $mapping && $mapping->getEntityIdPlugin() ? $mapping->getEntityIdPlugin() : $id_plugin_manager->getFallbackPluginId(NULL),
  ];

  $form['sparql_entity_storage']['graph'] = [
    '#type' => 'details',
    '#title' => t('Graphs'),
    '#description' => t('Graph URI mapping'),
  ];

  foreach ($storage->getGraphDefinitions() as $graph_id => $graph) {
    $form['sparql_entity_storage']['graph'][$graph_id] = [
      '#type' => 'url',
      '#title' => t('@title (@id)', [
        '@title' => $graph['title'],
        '@id' => $graph_id,
      ]),
      '#description' => $graph['description'],
      '#default_value' => $mapping ? $mapping->getGraphUri($graph_id) : NULL,
      '#required' => $graph_id === SparqlGraphInterface::DEFAULT,
    ];
  }

  $form['sparql_entity_storage']['base_fields_mapping'] = [
    '#type' => 'details',
    '#title' => t('Field mapping'),
    '#description' => t('This entity type uses a SPARQL backend. Please map the bundle base fields to their corresponding RDF properties.'),
  ];

  /** @var \Drupal\Core\Field\BaseFieldDefinition $base_field */
  foreach ($base_fields as $field_name => $base_field) {
    // The entity id doesn't need a mapping as it's the subject of the triple.
    if ($field_name === $id_key) {
      continue;
    }

    $title = $base_field->getLabel();
    $form['sparql_entity_storage']['base_fields_mapping'][$field_name] = [
      '#type' => 'details',
      '#title' => $title,
      '#description' => $base_field->getDescription(),
    ];

    $columns = $base_field->getColumns();
    foreach ($columns as $column_name => $column) {
      $column_title = $base_field->getLabel();
      if (count($columns) > 1) {
        $column_title = $title . ' (' . $column_name . ')';
      }

      $has_mapping = $mapping && $mapping->getMapping($field_name, $column_name);
      $form['sparql_entity_storage']['base_fields_mapping'][$field_name][$column_name]['predicate'] = [
        '#type' => 'url',
        '#title' => t(':field_title mapping', [
          ':field_title' => $column_title,
        ]),
        '#description' => t('The RDF predicate.'),
        '#weight' => 150,
        '#default_value' => $has_mapping ? $mapping->getMapping($field_name, $column_name)['predicate'] : NULL,
      ];

      $form['sparql_entity_storage']['base_fields_mapping'][$field_name][$column_name]['format'] = [
        '#type' => 'select',
        '#title' => t(':field_title value format', [
          ':field_title' => $column_title,
        ]),
        '#description' => t('The RDF format. Required if format is filled.'),
        '#options' => SparqlEntityStorageFieldHandler::getSupportedDataTypes(),
        '#empty_value' => '',
        '#weight' => 151,
        '#default_value' => $has_mapping ? $mapping->getMapping($field_name, $column_name)['format'] : NULL,
      ];
    }
  }

  $form['actions']['submit']['#submit'][] = 'sparql_entity_storage_type_mapping_submit';
}

/**
 * Stores the mapping of base fields and RDF properties.
 *
 * @param array $form
 *   The form API form render array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state object.
 *
 * @throws \Exception
 *   If the mapping fails to save.
 *
 * @see sparql_entity_storage_form_alter()
 */
function sparql_entity_storage_type_mapping_submit(array &$form, FormStateInterface $form_state): void {
  $values = $form_state->getValue('sparql_entity_storage');
  /** @var \Drupal\sparql_entity_storage\SparqlMappingInterface $mapping */
  $mapping = $form_state->get('sparql_mapping');
  if (!$mapping) {
    /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $bundle_entity */
    $bundle_entity = $form_state->getFormObject()->getEntity();
    $mapping = SparqlMapping::create([
      'entity_type_id' => $bundle_entity->getEntityType()->getBundleOf(),
      'bundle' => $form_state->getFormObject()->getEntity()->id(),
    ]);
  }

  $mapping
    ->setRdfType($values['rdf_type'])
    ->setEntityIdPlugin($values['entity_id_plugin'])
    // Add only non-empty values.
    ->setGraphs(array_filter($values['graph']))
    ->setMappings($values['base_fields_mapping'])
    ->save();
}

/**
 * Returns the requirements related to virtuoso version.
 *
 * @return array
 *   The virtuoso version requirements.
 */
function sparql_entity_storage_virtuoso_version_requirements() {
  $minimum_version = '07.00.0000';
  $requirements = [
    'sparql_endpoint' => [
      'title' => t('Virtuoso endpoint availability'),
      'description' => t('Virtuoso endpoint is available.'),
    ],
    'sparql_virtuoso_version' => [
      'title' => t('Virtuoso version'),
      'description' => t('Virtuoso version meets minimum requirements.'),
    ],
  ];

  /** @var \Drupal\sparql_entity_storage\Driver\Database\sparql\ConnectionInterface $connection */
  $connection = Database::getConnection('default', 'sparql_default');
  $client = Http::getDefaultHttpClient();
  $client->resetParameters(TRUE);
  $client->setUri($connection->getQueryUri());
  $client->setMethod('GET');

  try {
    $response = $client->request();
  }
  catch (Exception $e) {
    // If the endpoint could not be reached, return early.
    $requirements['sparql_endpoint']['description'] = t('Virtuoso endpoint could not be reached.');
    $requirements['sparql_endpoint']['severity'] = REQUIREMENT_ERROR;
    return $requirements;
  }

  $server_header = $response->getHeader('Server');
  preg_match('/Virtuoso\/(.*?)\s/', $server_header, $matches);
  $version = (is_array($matches) && count($matches) === 2) ? $matches[1] : [];
  if (version_compare($version, $minimum_version, 'lt')) {
    $description = t('The minimum virtuoso version supported is :version', [
      ':version' => $minimum_version,
    ]);
    $requirements['sparql_virtuoso_version']['description'] = $description;
    $requirements['sparql_virtuoso_version']['severity'] = REQUIREMENT_ERROR;
    $requirements['sparql_virtuoso_version']['value'] = $version;
  }

  return $requirements;
}

/**
 * Returns the requirements related to virtuoso query permissions.
 *
 * Since there is no direct way to draw information from the virtuoso instance
 * the function simply tries to create a triple in a random graph and then
 * delete the whole graph.
 *
 * @return array
 *   The virtuoso query requirements.
 */
function sparql_entity_storage_virtuoso_permission_requirements() {
  $rand = random_int(10000, 50000);
  $uri = 'http://example.com/id/' . $rand;
  $query = <<<QUERY
    WITH <{$uri}>
    INSERT { <{$uri}> <http://example.com/predicate> "test value" }
    CLEAR GRAPH <{$uri}>
QUERY;

  /** @var \Drupal\sparql_entity_storage\Driver\Database\sparql\ConnectionInterface $connection */
  $connection = Database::getConnection('default', 'sparql_default');
  $requirements = [
    'sparql_virtuoso_query' => [
      'title' => t('Virtuoso permissions'),
      'description' => t('Virtuoso update/delete permissions are properly set.'),
      'value' => $query,
    ],
  ];

  try {
    $connection->query($query);
  }
  catch (Exception $e) {
    $requirements['sparql_virtuoso_query']['description'] = $e->getMessage();
    $requirements['sparql_virtuoso_query']['severity'] = REQUIREMENT_ERROR;
  }

  return $requirements;
}

/**
 * Implements hook_cache_flush().
 */
function sparql_entity_storage_cache_flush() {
  \Drupal::service('sparql.graph_handler')->clearCache();
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc