Skip to content

Commit

Permalink
Merge pull request #1610 from tripal/tv4g1-issue1609-field_token_service
Browse files Browse the repository at this point in the history
TripalTokenParser Class
  • Loading branch information
spficklin committed Sep 16, 2023
2 parents 0cc0479 + a4142d3 commit 7de4481
Show file tree
Hide file tree
Showing 8 changed files with 759 additions and 130 deletions.
156 changes: 31 additions & 125 deletions tripal/src/Entity/TripalEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Drupal\tripal\TripalField\Interfaces\TripalFieldItemInterface;
use Drupal\field\Entity\FieldConfig;
use Symfony\Component\Routing\Route;

use Drupal\tripal\TripalField\TripalFieldItemBase;

/**
* Defines the Tripal Content entity.
Expand Down Expand Up @@ -129,12 +129,8 @@ public function setTitle($title = NULL, $cache = []) {
$bundle = \Drupal\tripal\Entity\TripalEntityType::load($this->getType());
}

// If no title was supplied then we should try to generate one using the
// default format set by admins.
// @todo figure out how to override the title while still allowing
// tokenized titles to be updated on edit.
$title = $bundle->getTitleFormat();
$title = $this->replaceTokens($title, ['tripal_entity_type' => $bundle]);
$title = $this->replaceTokens($title, $bundle);

$this->title = $title;
}
Expand Down Expand Up @@ -165,8 +161,7 @@ public function setAlias($path_alias = NULL) {

$bundle = \Drupal\tripal\Entity\TripalEntityType::load($this->getType());
$path_alias = $bundle->getURLFormat();
$path_alias = $this->replaceTokens($path_alias,
['tripal_entity_type' => $bundle]);
$path_alias = $this->replaceTokens($path_alias, $bundle);
}

// Ensure there is a leading slash.
Expand Down Expand Up @@ -253,126 +248,37 @@ public function setPublished($published) {
}

/**
* {@inheritdoc}
* Replaces tokens in a given tokens in URL Aliases and Titles.
*
* @param string $string
* The string to replace.
* @param array $cache
*/
public function replaceTokens($string, $cache = []) {

// Pull any items out of the cache.
if (isset($cache['bundle'])) {
$bundle_entity = $cache['bundle'];
}
else {
$bundle_entity = \Drupal\tripal\Entity\TripalEntityType::load($this->getType());
}

// Determine which tokens were used in the format string
$used_tokens = [];
if (preg_match_all('/\[.*?\]/', $string, $matches)) {
$used_tokens = $matches[0];
}

// If there are no tokens then just return the string.
if (count($used_tokens) == 0) {
return $string;
}

// @todo UPGRADE THIS CODE ONCE TRIPAL FIELDS HAVE BEEN ADDED
//
// If the fields are not loaded for the entity then we want to load them
// but we won't do a field_attach_load() as that will load all of the
// fields. For syncing (publishing) of content loading all fields for
// all synced entities causes extreme slowness, so we'll only attach
// the necessary fields for replacing tokens.
// $attach_fields = [];
//
// foreach ($used_tokens as $token) {
// $token = preg_replace('/[\[\]]/', '', $token);
// $elements = explode(',', $token);
// $field_name = array_shift($elements);
//
// if (!property_exists($entity, $field_name) or empty($entity->{$field_name})) {
// $field = field_info_field($field_name);
// $storage = $field['storage'];
// $attach_fields[$storage['type']]['storage'] = $storage;
// $attach_fields[$storage['type']]['fields'][] = $field;
// }
// }
//
// // If we have any fields that need attaching, then do so now.
// if (count(array_keys($attach_fields)) > 0) {
// foreach ($attach_fields as $storage_type => $details) {
// $field_ids = [];
// $storage = $details['storage'];
// $fields = $details['fields'];
// foreach ($fields as $field) {
// $field_ids[$field['id']] = [$entity->id];
// }
// $entities = [$entity->id => $entity];
// module_invoke($storage['module'], 'field_storage_load', 'TripalEntity',
// $entities, FIELD_LOAD_CURRENT, $field_ids, []);
// }
// }

// Now that all necessary fields are attached process the tokens.
foreach ($used_tokens as $token) {
$token = preg_replace('/[\[\]]/', '', $token);
$elements = explode(',', $token);
$field_name = array_shift($elements);
$value = '';

// The TripalBundle__bundle_id is a special token for substituting the
// bundle id.
if ($token === 'TripalBundle__bundle_id') {
// This token should be the id of the TripalBundle.
$value = $bundle_entity->getID();
}
// The TripalBundle__bundle_id is a special token for substituting the
// entity id.
elseif ($token === 'TripalEntity__entity_id') {
// This token should be the id of the TripalEntity.
$value = $this->getID();
}
elseif ($token == 'TripalEntityType__label') {
$value = $bundle_entity->getLabel();
}
else {
$value_obj = $this->get($field_name);
if ($value_obj) {
// A field may have multiple properties. We should use the
// main property key for the field when replacing the value.
$field_type = $this->getFieldDefinition($field_name)->getType();
$field_types = \Drupal::service('plugin.manager.field.field_type');
$field_type_def = $field_types->getDefinition($field_type);
$field_class = $field_type_def['class'];
$main_key = $field_class::mainPropertyName();

// Get the value if the property has one.
$value_raw = $value_obj->getValue();
$value = '';
if (array_key_exists($main_key, $value_raw[0])) {
$value = $value_raw[0][$main_key];
}

// If the value is an array it means we have sub elements and we can
// descend through the array to look for matching value.
if (is_array($value) and count($elements) > 0) {
// @todo we still need to handle this case.
$value = '';
//$value = _tripal_replace_entity_tokens_for_elements($elements, $value);
}
protected function replaceTokens($string, $bundle) {

// Intiailze the Tripal token parser service.
/** @var \Drupal\tripal\Services\TripalTokenParser $token_parser **/
$token_parser = \Drupal::service('tripal.token_parser');
$token_parser->initParser($bundle, $this);
$field_defs = $this->getFieldDefinitions();
foreach ($field_defs as $field_name => $field_def) {
/** @var \Drupal\Core\Field\FieldItemList $items **/
$items = $this->get($field_name);
if ($items->count() == 1) {
/** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item **/
/** @var \Drupal\Core\TypedData\TypedDataInterface $prop **/
$item = $items->get(0);
if (! $item instanceof TripalFieldItemBase) {
continue;
}
$props = $item->getProperties();
foreach ($props as $prop) {
$token_parser->addFieldValue($field_name, $prop->getName(), $prop->getValue());
}
}

// We can't support tokens that have multiple elements (i.e. in an array).
if (is_array($value)) {
$string = str_replace('[' . $token . ']', '', $string);
}
else {
$string = str_replace('[' . $token . ']', $value ?? '', $string);
}
}

return $string;
$replaced = $token_parser->replaceTokens([$string]);
return $replaced[0];
}

/**
Expand Down Expand Up @@ -578,7 +484,7 @@ public function preSave(EntityStorageInterface $storage) {
}

// Set the property values that should be saved in Drupal, everything
// else will stay in the underlying data store (e.g. Chado)..
// else will stay in the underlying data store (e.g. Chado).
$delta_remove = [];
$fields = $this->getFields();
foreach ($fields as $field_name => $items) {
Expand Down
2 changes: 1 addition & 1 deletion tripal/src/Services/TripalFieldCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public function install() {
* 'cardinality' => 1,
* 'required' => TRUE,
* 'storage_settings' => [
* 'storage_plugin_id' => 'tripal_default_storage',
* 'storage_plugin_id' => 'drupal_sql_storage',
* 'storage_plugin_settings'=> [
* ],
* 'max_length' => 255,
Expand Down
189 changes: 189 additions & 0 deletions tripal/src/Services/TripalTokenParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php
namespace Drupal\tripal\Services;

use \Drupal\tripal\Entity\TripalEntity;
use \Drupal\tripal\Entity\TripalEntityType;
use \Drupal\tripal\TripalStorage\StoragePropertyValue;
use Symfony\Component\Validator\Constraints\IsNull;

class TripalTokenParser {

/**
* The content type object.
* @var \Drupal\tripal\Entity\TripalEntityType $bundle
*/
protected $bundle = NULL;

/**
*
* @var \Drupal\tripal\Entity\TripalEntity $entity
*/
protected $entity = NULL;

/**
* An array of field instances.
*
* @var array $fields
*/
protected $fields = [];

/**
* An array of field values indexed first by field name then by property key.
*
* @var array $values.
*/
protected $values = [];

/**
* Returns bundle object given to the parser.
*
* @return \Drupal\tripal\Entity\TripalEntityType
*/
public function getBunde() {
return $this->bundle;
}

/**
* Returns the array of values given to the parser.
* @return array
*/
public function getValues() {
return $this->values;
}

/**
* Returns the entity given to the parser.
*
* @return \Drupal\tripal\Entity\TripalEntity
*/
public function getEntity() {
return $this->entity;
}

/**
* Returns the names of the fields that have been added.
*
* @return array
*/
public function getFieldNames() {
return array_keys($this->fields);
}

/**
*
* @param TripalEntity $entity
*/
public function setEntity(TripalEntity $entity) {
if ($entity->getType() != $this->bundle->getId()) {
throw new \Exception(t('TripalTokenParser: The entity provide is not of the same time as the bundle'));
}

$this->entity = $entity;
}


/**
* Initializes the token parser service
*
* The content type or bundle Id.
* @param string \Drupal\tripal\Entity\TripalEntityType $bundle
*/
public function initParser(TripalEntityType $bundle, TripalEntity $entity = NULL) {
$this->bundle = $bundle;
if ($entity) {
$this->setEntity($entity);
}

// Get the field manager, field definitions for the bundle type, and
// the field type manager.
/** @var \Drupal\Core\Entity\EntityFieldManager $field_manager **/
/** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager **/
$field_manager = \Drupal::service('entity_field.manager');
$field_defs = $field_manager->getFieldDefinitions('tripal_entity', $bundle->getID());
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');

// Iterate over the field definitions for the bundle and createa field instance.
/** @var \Drupal\Core\Field\BaseFieldDefinition $field_definition **/
$field_definition = NULL;
foreach ($field_defs as $field_name => $field_definition) {
if (!empty($field_definition->getTargetBundle())) {
$configuration = [
'field_definition' => $field_definition,
'name' => $field_name,
'parent' => NULL,
];
$field = $field_type_manager->createInstance($field_definition->getType(), $configuration);
$this->fields[$field_name] = $field;
}
}
}

/**
* Adds the field values that should be used for replacement.
*
* @param string $field_name
* The name of the field that the value belongs to
* @param StoragePropertyValue $value
* The property values
*/
public function addFieldValue($field_name, string $key, $value){
$this->values[$field_name][$key] = $value;
}

/**
* Replaces the tokens with field values within the provided strings.
*
* @param array $tokenized_strings
* Ann array of strings with field names as tokens. Field name should be
* surrounded by square brackets.
*
* @return array
* An array with all of the strings from the input $tokenized_strings array
* but with field tokens replaced with approprivate values.
*/
public function replaceTokens(array $tokenized_strings) {

$replaced = $tokenized_strings;
foreach ($tokenized_strings as $index => $tokenized_string) {
$value = NULL;

// Get the tokens in the string.
$tokens = [];
$matches = [];
if (preg_match_all('/\[.*?\]/', $tokenized_string, $matches)) {
$tokens = $matches[0];
}
foreach ($tokens as $token) {
$token = preg_replace('/\[/', '', $token);
$token = preg_replace('/\]/', '', $token);

// Look for values for bundle or entity related tokens.
if ($token === 'TripalBundle__bundle_id') {
$value = $this->bundle->getID();
$replaced[$index] = trim(preg_replace("/\[$token\]/", $value, $replaced[$index]));
}
elseif ($token == 'TripalEntityType__label') {
$value = $this->bundle->getLabel();
$replaced[$index] = trim(preg_replace("/\[$token\]/", $value, $replaced[$index]));
}
elseif ($token === 'TripalEntity__entity_id' and !is_null($this->entity)) {
$value = $this->entity->getID();
$replaced[$index] = trim(preg_replace("/\[$token\]/", $value, $replaced[$index]));
}
// Look for values for field related tokens
elseif (in_array($token, array_keys($this->fields))) {
$field = $this->fields[$token];
$key = $field->mainPropertyName();
if (array_key_exists($token, $this->values)) {
$value = $this->values[$token][$key];
if (!is_null($value)) {
$replaced[$index] = trim(preg_replace("/\[$token\]/", $value, $replaced[$index]));
}
}
}
}
}

return $replaced;
}
}

0 comments on commit 7de4481

Please sign in to comment.