cbr-1.0.0/src/Plugin/Field/FieldType/CBRTaxonomyReferenceField.php

src/Plugin/Field/FieldType/CBRTaxonomyReferenceField.php
<?php

namespace Drupal\cbr\Plugin\Field\FieldType;

use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Form\FormStateInterface;
use Drupal\field\Entity\FieldConfig;

/**
 * Defines the 'cbr_taxonomy_reference' entity field type.
 *
 * @FieldType(
 *   id = "cbr_taxonomy_reference",
 *   label = @Translation("CBR Taxonomy reference"),
 *   description = @Translation("An entity field containing an entity reference."),
 *   category = @Translation("Case Based Reasoning"),
 *   default_widget = "cbr_entity_reference_autocomplete",
 *   default_formatter = "cbr_entity_reference_label",
 *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
 * )
 */
class CBRTaxonomyReferenceField extends EntityReferenceItem implements CBRFieldInterface
{
    /**
     * {@inheritdoc}
     */
    public function fieldSettingsForm(array $form, FormStateInterface $form_state): array
    {
        $form = parent::fieldSettingsForm($form, $form_state) + CBRFieldHelper::cbrFieldSettingsForm($form, $form_state);
        /** @var FieldConfig $field_config */
        $field_config = $form_state->getFormObject()->getEntity();
        $form['cbr_settings']['similarity_function'] = [
            '#type' => 'select',
            '#title' => t('Similarity function'),
            '#description' => t('Select the similarity function to use for this field. <br> 
                • "Jaccard" - Use jaccard if you reference multiple taxonomy terms. <br>
                • "Hierarchy distance" - Use hierarchy distance if your terms are organized in a hierarchy.'),
            '#options' => [
                'jaccard' => t('Jaccard'),
                'hierarchy_distance' => t('Hierarchy distance'),
            ],
            '#default_value' => $field_config->getThirdPartySetting('cbr', 'similarity_function', 'jaccard'),
            '#required' => true
        ];
        $form['#entity_builders'][] = [$this, 'saveCBRFieldTaxonomySettings'];
        return $form;
    }

    /**
     * Entity builder callback to save the similarity function.
     * @param $entity_type The entity type.
     * @param FieldConfig $field_config The field config.
     * @param $form The form array.
     * @param FormStateInterface $form_state The form state.
     */
    public static function saveCBRFieldTaxonomySettings($entity_type, FieldConfig $field_config, &$form, FormStateInterface $form_state)
    {
        $field_config->setThirdPartySetting('cbr', 'similarity_function',   $form_state->getValue(['settings', 'cbr_settings', 'similarity_function']));
    }

    /**
     * {@inheritdoc}
     */
    public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data): array
    {
        $element['target_type'] = [
            '#type' => 'select',
            '#title' => t('Type of item to reference'),
            '#default_value' => 'taxonomy_term',
            '#options' => [
                'taxonomy_term' => t('Taxonomy term'),
            ],
            '#required' => TRUE,
            '#disabled' => TRUE,
            '#size' => 1,
        ];
        return $element;
    }


    /**
     * {@inheritdoc}
     */
    public static function getPreconfiguredOptions(): array
    {
        $options = [];
        return $options;
    }

    /**
     * {@inheritdoc}
     */
    public function calculateSimilarity($tids1, $tids2, FieldConfig $field_config): float
    {
        $similarity_function = $field_config->getThirdPartySetting('cbr', 'similarity_function', 'jaccard');
        switch ($similarity_function) {
            case 'jaccard':
                return $this->calculateJaccardSimilarity($tids1, $tids2);
            case 'hierarchy_distance':
                return $this->calculateHierarchyDistanceSimilarity($tids1, $tids2);
            default:
                die("Unknown similarity function: " . $similarity_function);
                return 0;   // should never happen    
        }
    }


    public function summerize(array $values): array
    {
        $merged = [];
        foreach ($values as $value) {
            $merged = array_merge($merged, $value);
        }
        return array_unique($merged);
    }

    public function getValueForSimilarityCalculation(FieldConfig $field_config): array
    {
        $tids = [];
        foreach ($this->parent as $field) {
            $tids[] = $field->getValue()['target_id'];
        }
        return $tids;
    }

    private function calculateJaccardSimilarity($tids1, $tids2): float
    {
        //return 0, if no referenced entities
        if (empty($tids1)) {
            return 0;
        }

        //return 0, if no referenced entities
        if (empty($tids2)) {
            return 0;
        }

        //calculate jaccard similarity
        $tids1 = array_unique($tids1);
        $tids2 = array_unique($tids2);

        $intersection = array_intersect($tids1, $tids2);
        $union = array_unique(array_merge($tids1, $tids2));
        if (empty($union)) {
            return 0;
        }
        return count($intersection) / count($union);
    }


    private function calculateHierarchyDistanceSimilarity($tids1, $tids2): float
    {
        //return 0, if no referenced entities
        if (empty($tids1)) {
            return 0;
        }

        //return 0, if no referenced entities
        if (empty($tids2)) {
            return 0;
        }

        //compare each elemet of tids1 with each element of tids2
        $hierarchy_distance = 0;
        foreach ($tids1 as $tid1) {
            foreach ($tids2 as $tid2) {
                $hierarchy_distance += $this->getHierarchyDistance($tid1, $tid2);
            }
        }
        return $hierarchy_distance / (count($tids1) * count($tids2));
    }


    private function getHierarchyDistance($tid1, $tid2): float
    {

        if ($tid1 == $tid2) {
            return 1;
        }

        //get all parents of term 1
        $parents1 = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadAllParents($tid1);
        $parents1 = array_keys($parents1);

        //get all parents of term 2
        $parents2 = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadAllParents($tid2);
        $parents2 = array_keys($parents2);

        if (empty($parents1)) {
            return count($parents2);
        }
        if (empty($parents2)) {
            return count($parents1);
        }
        //Select the last element of the parents array
        //This is the root of the tree
        $term1 = end($parents1);
        $term2 = end($parents2);

        //While both terms are the same, keep going down the tree
        while ($term1 == $term2) {
            array_pop($parents1);
            array_pop($parents2);
            $term1 = end($parents1);
            $term2 = end($parents2);
        }
        //We found the Lowest Common Ancestor (LCA)! 
        //The distance is the number of edges between the two terms
        $distance = count($parents1) + count($parents2);

        return 1 / ($distance + 1);
    }
}

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

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