updated synonyms to 1.3

This commit is contained in:
Bachir Soussi Chiadmi
2016-11-05 17:52:37 +01:00
parent 86c993f638
commit 8d24211bf5
37 changed files with 4562 additions and 1923 deletions

View File

@@ -1,116 +0,0 @@
<?php
/**
* @file
* Define interface required for extracting synonyms from field types.
*/
abstract class AbstractSynonymsExtractor {
/**
* Return array of supported field types for synonyms extraction.
*
* @return array
* Array of Field API field types from which this class is able to extract
* synonyms
*/
public static function fieldTypesSupported() {
return array();
}
/**
* Extract synonyms from a field attached to an entity.
*
* We try to pass as many info about context as possible, however, normally
* you will only need $items to extract the synonyms.
*
* @param array $items
* Array of items
* @param array $field
* Array of field definition according to Field API
* @param array $instance
* Array of instance definition according to Field API
* @param object $entity
* Fully loaded entity object to which the $field and $instance with $item
* values is attached to
* @param string $entity_type
* Type of the entity $entity according to Field API definition of entity
* types
*
* @return array
* Array of synonyms extracted from $items
*/
public static function synonymsExtract($items, $field, $instance, $entity, $entity_type) {
return array();
}
/**
* Allow you to hook in during autocomplete suggestions generation.
*
* Allow you to include entities for autocomplete suggestion that are possible
* candidates based on your field as a source of synonyms. This method is
* void, however, you have to alter and add your condition to $query
* parameter.
*
* @param string $tag
* What user has typed in into autocomplete widget. Normally you would
* run LIKE '%$tag%' on your column
* @param EntityFieldQuery $query
* EntityFieldQuery object where you should put your conditions to
* @param array $field
* Array of field definition according to Field API
* @param array $instance
* Array of instance definition according to Field API
*/
public static function processEntityFieldQuery($tag, EntityFieldQuery $query, $field, $instance) {}
/**
* Add an entity as a synonym into a field of another entity.
*
* Basically this method should be called when you want to add some entity
* as a synonym to another entity (for example when you merge one entity
* into another and besides merging want to add synonym of the merging
* entity into the trunk entity). You should extract synonym value (according
* to what value is expected in this field) and return it. We try to provide
* you with as much of context as possible, but normally you would only need
* $synonym_entity and $synonym_entity_type parameters. Return an empty array
* if entity of type $synonym_entity_type cannot be converted into a format
* expected by $field.
*
* @param array $items
* Array items that already exist in the field into which new synonyms is to
* be added
* @param array $field
* Field array definition according to Field API of the field into which new
* synonym is to be added
* @param array $instance
* Instance array definition according to Field API of the instance into
* which new synonym is to be added
* @param object $synonym_entity
* Fully loaded entity object which has to be added as synonym
* @param string $synonym_entity_type
* Entity type of $synonym_entity
*
* @return array
* Array of extra items to be merged into the items that already exist in
* field values
*/
public static function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
return array();
}
/**
* Supportive method.
*
* Set such a condition on $query that it will always yield no results. Should
* be called from $this->processEntityFieldQuery() when for whatever reason
* the object can't alter $query to include matched synonyms. As a fallback it
* should call this method to make sure it filtered everything out.
*
* @param EntityFieldQuery $query
* Query object passed to $this->processEntityFieldQuery() method
*/
protected static function emptyResultsCondition(EntityFieldQuery $query) {
$query->range(0, 0);
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @file
* Enables Entity Reference field type to be source of synonyms.
*/
/**
* Definition of EntityReferenceSynonymsBehavior class.
*/
class EntityReferenceSynonymsBehavior extends AbstractSynonymsSynonymsBehavior implements SynonymsSynonymsBehavior, AutocompleteSynonymsBehavior, SelectSynonymsBehavior {
public function extractSynonyms($items, $field, $instance, $entity, $entity_type) {
$synonyms = array();
$target_tids = array();
foreach ($items as $item) {
$target_tids[] = $item['target_id'];
}
$entities = entity_load($field['settings']['target_type'], $target_tids);
foreach ($entities as $entity) {
$synonyms[] = entity_label($field['settings']['target_type'], $entity);
}
return $synonyms;
}
public function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
// Firstly validating that this entity reference is able to reference to
// that type of entity.
$expected_synonym_entity_type = $field['settings']['target_type'];
if ($expected_synonym_entity_type != $synonym_entity_type) {
return array();
}
$synonym_entity_id = entity_id($synonym_entity_type, $synonym_entity);
return array(array(
'target_id' => $synonym_entity_id,
));
}
public function synonymItemHash($item, $field, $instance) {
return $field['settings']['target_type'] . $item['target_id'];
}
public function synonymsFind(QueryConditionInterface $condition, $field, $instance) {
if ($field['storage']['type'] != 'field_sql_storage') {
throw new SynonymsSynonymsBehaviorException(t('Not supported storage engine %type in synonymsFind() method.', array(
'%type' => $field['storage']['type'],
)));
}
$table = array_keys($field['storage']['details']['sql'][FIELD_LOAD_CURRENT]);
$table = reset($table);
$column = $field['storage']['details']['sql'][FIELD_LOAD_CURRENT][$table]['target_id'];
$query = db_select($table, 'field');
$target_entity_type_info = entity_get_info($field['settings']['target_type']);
if (!isset($target_entity_type_info['base table']) || !$target_entity_type_info['base table']) {
throw new SynonymsSynonymsBehaviorException(t('Target entity type %entity_type is not stored in database.', array(
'%entity_type' => $field['settings']['target_type'],
)));
}
if (!isset($target_entity_type_info['entity keys']['id'])) {
throw new SynonymsSynonymsBehaviorException(t('Target entity type %entity_type does not declare primary key.', array(
'%entity_type' => $field['settings']['target_type'],
)));
}
if (!isset($target_entity_type_info['entity keys']['label'])) {
throw new SynonymsSynonymsBehaviorException(t('Target entity type %entity_type does not declare label column.', array(
'%entity_type' => $field['settings']['target_type'],
)));
}
$target_entity_alias = $query->innerJoin($target_entity_type_info['base table'], 'target_entity', 'field.' . $column . ' = target_entity.' . $target_entity_type_info['entity keys']['id']);
$query->addField($target_entity_alias, $target_entity_type_info['entity keys']['label'], 'synonym');
$query->fields('field', array('entity_id'));
$query->condition('field.entity_type', $instance['entity_type']);
$query->condition('field.bundle', $instance['bundle']);
$this->synonymsFindProcessCondition($condition, $target_entity_alias . '.' . $target_entity_type_info['entity keys']['label']);
$query->condition($condition);
return $query->execute();
}
}

View File

@@ -1,70 +0,0 @@
<?php
/**
* @file
* Enables Entity Reference field type to be source of synonyms.
*/
class EntityReferenceSynonymsExtractor extends AbstractSynonymsExtractor {
public static function fieldTypesSupported() {
return array('entityreference');
}
public static function synonymsExtract($items, $field, $instance, $entity, $entity_type) {
$synonyms = array();
// For speading up loading all the entities at once.
$target_tids = array();
foreach ($items as $item) {
$target_tids[] = $item['target_id'];
}
$entities = entity_load($field['settings']['target_type'], $target_tids);
foreach ($entities as $entity) {
$synonyms[] = entity_label($field['settings']['target_type'], $entity);
}
return $synonyms;
}
public static function processEntityFieldQuery($tag, EntityFieldQuery $query, $field, $instance) {
// Unfortunately EntityFieldQuery does not currently support INNER JOINing
// referenced entities via any field type.
// Thus, we use an ugly solution -- going through all entities that exist
// in such entity type trying to see if there is a match by entity's label.
$efq = new EntityFieldQuery();
$efq->entityCondition('entity_type', $field['settings']['target_type']);
// Additionally we have to figure out which column in the entity table
// represents entity label.
$entity_info = entity_get_info($field['settings']['target_type']);
if (!isset($entity_info['entity keys']['label'])) {
// We can't get any matches if we do not know what column to query
// against. So we add a condition to $query which will 100% yield empty
// results.
self::emptyResultsCondition($query);
return;
}
$efq->propertyCondition($entity_info['entity keys']['label'], '%' . $tag . '%', 'LIKE');
$result = $efq->execute();
if (!isset($result[$field['settings']['target_type']]) || !is_array($result[$field['settings']['target_type']])) {
self::emptyResultsCondition($query);
return;
}
$result = $result[$field['settings']['target_type']];
$query->fieldCondition($field, 'target_id', array_keys($result));
}
public static function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
// Firstly validating that this entity reference is able to reference to
// that type of entity.
$expected_synonym_entity_type = $field['settings']['target_type'];
if ($expected_synonym_entity_type != $synonym_entity_type) {
return array();
}
$synonym_entity_id = entity_id($synonym_entity_type, $synonym_entity);
return array(array(
'target_id' => $synonym_entity_id,
));
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
* @file
* Interfaces of synonyms behaviors that are shipped with Synonyms module.
*/
/**
* General interface of a synonyms behavior.
*
* All synonyms behaviors must extend this interface.
*/
interface SynonymsBehavior {
}
/**
* Interface of "synonyms" behavior.
*
* The most basic synonyms behavior.
*/
interface SynonymsSynonymsBehavior extends SynonymsBehavior {
/**
* Extract synonyms from a field attached to an entity.
*
* We try to pass as many info about context as possible, however, normally
* you will only need $items to extract the synonyms.
*
* @param array $items
* Array of items
* @param array $field
* Array of field definition according to Field API
* @param array $instance
* Array of instance definition according to Field API
* @param object $entity
* Fully loaded entity object to which the $field and $instance with $item
* values is attached to
* @param string $entity_type
* Type of the entity $entity according to Field API definition of entity
* types
*
* @return array
* Array of synonyms extracted from $items
*/
public function extractSynonyms($items, $field, $instance, $entity, $entity_type);
/**
* Add an entity as a synonym into a field of another entity.
*
* Basically this method should be called when you want to add some entity
* as a synonym to another entity (for example when you merge one entity
* into another and besides merging want to add synonym of the merged entity
* into the trunk entity). You should extract synonym value (according to what
* value is expected in this field) and return it. We try to provide you with
* as much of context as possible, but normally you would only need
* $synonym_entity and $synonym_entity_type parameters. Return an empty array
* if entity of type $synonym_entity_type cannot be converted into a format
* expected by $field.
*
* @param array $items
* Array items that already exist in the field into which new synonyms is to
* be added
* @param array $field
* Field array definition according to Field API of the field into which new
* synonym is to be added
* @param array $instance
* Instance array definition according to Field API of the instance into
* which new synonym is to be added
* @param object $synonym_entity
* Fully loaded entity object which has to be added as synonym
* @param string $synonym_entity_type
* Entity type of $synonym_entity
*
* @return array
* Array of extra items to be merged into the items that already exist in
* field values
*/
public function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type);
/**
* Hash a field item that is enabled as synonym.
*
* Your hash function must return such hash that for 2 items that yield the
* same synonyms their hash must be the same. There is no limit on minimal or
* maximum hash length, but keep it reasonable, something below 512 symbols.
* Also, your hash function should strive to minimize hash collisions, i.e.
* when 2 different items yield the same hash.
*
* @param array $item
* Field item whose hash is requested
* @param array $field
* Field from which the $item comes from
* @param array $instance
* Instance from which the $item comes from
*
* @return string
* Hash of the provided $item
*/
public function synonymItemHash($item, $field, $instance);
/**
* Look up entities by their synonyms within a provided field.
*
* You are provided with a SQL condition that you should apply to the storage
* of synonyms within the provided field. And then return result: what
* entities match by the provided condition through what synonyms.
*
* @param QueryConditionInterface $condition
* Condition that defines what to search for. It may contain a placeholder
* of AbstractSynonymsSynonymsBehavior::COLUMN_PLACEHOLDER which you should
* replace by the column name where the synonyms data for your field is
* stored in plain text. For it to do, you may extend the
* AbstractSynonymsSynonymsBehavior class and then just invoke the
* AbstractSynonymsSynonymsBehavior->synonymsFindProcessCondition() method,
* so you won't have to worry much about it
* @param array $field
* Field API field definition array of the field within which the search
* for synonyms should be performed
* @param array $instance
* Field API instance definition array of the instance within which the
* search for synonyms should be performed
*
* @return Traversable
* Traversable result set of found synonyms and entity IDs to which those
* belong. Each element in the result set should be an object and will have
* the following structure:
* - synonym: (string) Synonym that was found and which satisfies the
* provided condition
* - entity_id: (int) ID of the entity to which the found synonym belongs
*/
public function synonymsFind(QueryConditionInterface $condition, $field, $instance);
}
/**
* Exception thrown by implementations of SynonymsSynonymsBehavior interface.
*/
class SynonymsSynonymsBehaviorException extends Exception {}
/**
* Starting point for implementing SynonymsSynonymsBehavior interface.
*/
abstract class AbstractSynonymsSynonymsBehavior implements SynonymsSynonymsBehavior {
/**
* Constant which denotes placeholder of a synonym column.
*
* @var string
*/
const COLUMN_PLACEHOLDER = '***COLUMN***';
/**
* Process condition in 'synonymsFind' method.
*
* Process condition in 'synonymsFind' method replacing all references of
* synonym column with the real name of that column.
*
* @param QueryConditionInterface $condition
* Condition that should be processed
* @param string $column
* Real name of the synonym column
*/
protected function synonymsFindProcessCondition(QueryConditionInterface $condition, $column) {
$condition_array = &$condition->conditions();
foreach ($condition_array as &$v) {
if (is_array($v) && isset($v['field'])) {
if ($v['field'] instanceof QueryConditionInterface) {
// Recursively process this condition too.
$this->synonymsFindProcessCondition($v['field'], $column);
}
else {
$v['field'] = str_replace(self::COLUMN_PLACEHOLDER, $column, $v['field']);
}
}
}
}
}
/**
* Interface of the autocomplete synonyms behavior.
*/
interface AutocompleteSynonymsBehavior extends SynonymsSynonymsBehavior {
}
/**
* Interface of the synonyms friendly select behavior.
*/
interface SelectSynonymsBehavior extends SynonymsSynonymsBehavior {
}

View File

@@ -1,48 +0,0 @@
<?php
/**
* @file
* Default Synonyms Extractor class that ships together with the Synonym module.
*/
class SynonymsSynonymsExtractor extends AbstractSynonymsExtractor {
public static function fieldTypesSupported() {
return array('text', 'number_integer', 'number_float', 'number_decimal');
}
public static function synonymsExtract($items, $field, $instance, $entity, $entity_type) {
$synonyms = array();
foreach ($items as $item) {
$synonyms[] = $item['value'];
}
return $synonyms;
}
public static function processEntityFieldQuery($tag, EntityFieldQuery $query, $field, $instance) {
$query->fieldCondition($field, 'value', '%' . $tag . '%', 'LIKE');
}
public static function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
$synonym = entity_label($synonym_entity_type, $synonym_entity);
switch ($field['type']) {
case 'text':
break;
// We add synonyms for numbers only if $synonym is a number.
case 'number_integer':
case 'number_float':
case 'number_decimal':
if (!is_numeric($synonym)) {
return array();
}
break;
}
return array(array(
'value' => $synonym,
));
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* @file
* Enables Taxonomy Term Reference field type to be source of synonyms.
*/
/**
* Definition of TaxonomySynonymsBehavior class.
*/
class TaxonomySynonymsBehavior extends AbstractSynonymsSynonymsBehavior implements SynonymsSynonymsBehavior, AutocompleteSynonymsBehavior, SelectSynonymsBehavior {
public function extractSynonyms($items, $field, $instance, $entity, $entity_type) {
$synonyms = array();
$terms = array();
foreach ($items as $item) {
$terms[] = $item['tid'];
}
$terms = taxonomy_term_load_multiple($terms);
foreach ($terms as $term) {
$synonyms[] = entity_label('taxonomy_term', $term);
}
return $synonyms;
}
public function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
// Taxonomy term reference supports only referencing of entity types
// 'taxonomy_term'.. duh.
if ($synonym_entity_type != 'taxonomy_term') {
return array();
}
// Checking that $field is configured to reference the vocabulary of
// $synonym_entity term.
$is_allowed = FALSE;
foreach ($field['settings']['allowed_values'] as $setting) {
if ($synonym_entity->vocabulary_machine_name == $setting['vocabulary']) {
if ($setting['parent'] == 0) {
// No need to check parent property as there is no limitation on it.
$is_allowed = TRUE;
break;
}
else {
foreach (taxonomy_get_parents_all($synonym_entity->tid) as $parent) {
if ($parent->tid == $setting['parent']) {
$is_allowed = TRUE;
break(2);
}
}
}
}
}
if (!$is_allowed) {
// Synonym term is from a vocabulary that is not expected by this field,
// or under unexpected parent.
return array();
}
return array(array(
'tid' => $synonym_entity->tid,
));
}
public function synonymItemHash($item, $field, $instance) {
return $item['tid'];
}
public function synonymsFind(QueryConditionInterface $condition, $field, $instance) {
if ($field['storage']['type'] != 'field_sql_storage') {
throw new SynonymsSynonymsBehaviorException(t('Not supported storage engine %type in synonymsFind() method.', array(
'%type' => $field['storage']['type'],
)));
}
$table = array_keys($field['storage']['details']['sql'][FIELD_LOAD_CURRENT]);
$table = reset($table);
$column = $field['storage']['details']['sql'][FIELD_LOAD_CURRENT][$table]['tid'];
$query = db_select($table, 'field');
$term_alias = $query->innerJoin('taxonomy_term_data', 'term', 'field.' . $column . ' = term.tid');
$query->addField($term_alias, 'name', 'synonym');
$query->fields('field', array('entity_id'));
$query->condition('field.entity_type', $instance['entity_type']);
$query->condition('field.bundle', $instance['bundle']);
$this->synonymsFindProcessCondition($condition, $term_alias . '.name');
$query->condition($condition);
return $query->execute();
}
}

View File

@@ -1,94 +0,0 @@
<?php
/**
* @file
* Enables Taxonomy Term Reference field types to be source of synonyms.
*/
class TaxonomySynonymsExtractor extends AbstractSynonymsExtractor {
public static function fieldTypesSupported() {
return array('taxonomy_term_reference');
}
public static function synonymsExtract($items, $field, $instance, $entity, $entity_type) {
$synonyms = array();
$terms = array();
foreach ($items as $item) {
$terms[] = $item['tid'];
}
$terms = taxonomy_term_load_multiple($terms);
foreach ($terms as $term) {
$synonyms[] = entity_label('taxonomy_term', $term);
}
return $synonyms;
}
public static function processEntityFieldQuery($tag, EntityFieldQuery $query, $field, $instance) {
// Unfortunately EntityFieldQuery does not currently support INNER JOINing
// term entity that is referenced via taxonomy_term_reference field type.
// Thus, we use an ugly solution -- going through all terms that exist in
// vocabulary and trying to see if there is a match by term's name.
$tids = array();
foreach ($field['settings']['allowed_values'] as $settings) {
$efd = new EntityFieldQuery();
$efd->entityCondition('bundle', $settings['vocabulary'])
->entityCondition('entity_type', 'taxonomy_term')
->propertyCondition('name', '%' . $tag . '%', 'LIKE');
$result = $efd->execute();
if (isset($result['taxonomy_term'])) {
foreach ($result['taxonomy_term'] as $tid) {
$tids[] = $tid->tid;
}
}
}
// Now we have tids of terms from the referenced vocabulary which names
// LIKE %$tag%, suggested are the terms that refer to any of these $tids.
if (empty($tids)) {
// No possible suggestions were found. We have to make sure $query yields
// no results.
self::emptyResultsCondition($query);
return;
}
$query->fieldCondition($field, 'tid', $tids);
}
public static function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
// Taxonomy term reference supports only referencing of entity types
// 'taxonomy_term'.. duh.
if ($synonym_entity_type != 'taxonomy_term') {
return array();
}
// Checking that $field is configured to reference the vocabulary of
// $synonym_entity term.
$is_allowed = FALSE;
foreach ($field['settings']['allowed_values'] as $setting) {
if ($synonym_entity->vocabulary_machine_name == $setting['vocabulary']) {
if ($setting['parent'] == 0) {
// No need to check parent property as there is no limitation on it.
$is_allowed = TRUE;
break;
}
else {
foreach (taxonomy_get_parents_all($synonym_entity->tid) as $parent) {
if ($parent->tid == $setting['parent']) {
$is_allowed = TRUE;
break(2);
}
}
}
}
}
if (!$is_allowed) {
// Synonym term is from a vocabulary that is not expected by this field,
// or under unexpected parent.
return array();
}
return array(array(
'tid' => $synonym_entity->tid,
));
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* @file
* Enables text and number field types to be source of synonyms.
*/
/**
* Definition of TextSynonymsBehavior class.
*/
class TextSynonymsBehavior extends AbstractSynonymsSynonymsBehavior implements SynonymsSynonymsBehavior, AutocompleteSynonymsBehavior, SelectSynonymsBehavior {
public function extractSynonyms($items, $field, $instance, $entity, $entity_type) {
$synonyms = array();
foreach ($items as $item) {
$synonyms[] = $item['value'];
}
return $synonyms;
}
public function mergeEntityAsSynonym($items, $field, $instance, $synonym_entity, $synonym_entity_type) {
$synonym = entity_label($synonym_entity_type, $synonym_entity);
switch ($field['type']) {
case 'text':
break;
// We add synonyms for numbers only if $synonym is a number.
case 'number_integer':
case 'number_float':
case 'number_decimal':
if (!is_numeric($synonym)) {
return array();
}
break;
}
return array(array(
'value' => $synonym,
));
}
public function synonymItemHash($item, $field, $instance) {
return $item['value'];
}
public function synonymsFind(QueryConditionInterface $condition, $field, $instance) {
if ($field['storage']['type'] != 'field_sql_storage') {
throw new SynonymsSynonymsBehaviorException(t('Not supported storage engine %type in synonymsFind() method.', array(
'%type' => $field['storage']['type'],
)));
}
$table = array_keys($field['storage']['details']['sql'][FIELD_LOAD_CURRENT]);
$table = reset($table);
$column = $field['storage']['details']['sql'][FIELD_LOAD_CURRENT][$table]['value'];
$this->synonymsFindProcessCondition($condition, $column);
$query = db_select($table);
$query->fields($table, array('entity_id'));
$query->addField($table, $column, 'synonym');
return $query->condition($condition)
->condition('entity_type', $instance['entity_type'])
->condition('bundle', $instance['bundle'])
->execute();
}
}