cbr-1.0.0/src/Plugin/Field/FieldType/CBRNodeReferenceField.php
src/Plugin/Field/FieldType/CBRNodeReferenceField.php
<?php
namespace Drupal\cbr\Plugin\Field\FieldType;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\Entity\Node;
/**
* Defines the 'cbr_node_reference' entity field type.
*
* @FieldType(
* id = "cbr_node_reference",
* label = @Translation("CBR Node 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 CBRNodeReferenceField extends EntityReferenceItem implements CBRFieldInterface
{
protected MessengerInterface $messenger;
protected CacheBackendInterface $cache;
/**
* {@inheritdoc}
*/
public function __construct(ComplexDataDefinitionInterface $definition, $name, TypedDataInterface $parent)
{
parent::__construct($definition, $name, $parent);
$this->messenger = \Drupal::messenger();
$this->cache = \Drupal::cache();
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state): array
{
return parent::fieldSettingsForm($form, $form_state) + CBRFieldHelper::cbrFieldSettingsForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data)
{
$element['target_type'] = [
'#type' => 'select',
'#title' => t('Type of item to reference'),
'#default_value' => 'node',
'#options' => [
'node' => t('Node'),
],
'#required' => TRUE,
'#disabled' => TRUE,
'#size' => 1,
];
return $element;
}
/**
* {@inheritdoc}
*/
public static function getPreconfiguredOptions()
{
$options = [];
return $options;
}
/**
* {@inheritdoc}
*/
public function calculateSimilarity($nids1, $nids2, FieldConfig $field_config): float
{
if ($this->getFieldDefinition()->getTargetEntityTypeId() != 'node')
return 0;
//return 0, if no referenced entities
if (empty($nids1)) {
return 0;
}
//return 0, if no referenced entities
if (empty($nids2)) {
return 0;
}
$entity = Node::load($nids1[0]);
$summerized_field_values = $this->loadSummarizedFieldValues($nids1, $field_config, $entity->bundle());
$summerizied_field_values2 = $this->loadSummarizedFieldValues($nids2, $field_config, $entity->bundle());
//loop all fields
$similarity = 0;
$sum_weight = 0;
foreach ($entity->getFields() as $field_item) {
if ($field_item->first() !== null && $field_item->first() instanceof CBRFieldInterface) {
$field_config = $field_item->getFieldDefinition()->getConfig($entity->bundle());
$weight = $field_config->getConfig($entity->bundle())->getThirdPartySetting('cbr', 'weight', 1);
if (isset($summerized_field_values[$field_item->getName()]) && isset($summerizied_field_values2[$field_item->getName()])) {
$value1 = $summerized_field_values[$field_item->getName()];
$value2 = $summerizied_field_values2[$field_item->getName()];
$similarity += $field_item->first()->calculateSimilarity($value1, $value2, $field_config) * $weight;
$sum_weight += $weight;
}
}
}
if ($sum_weight > 0) {
$similarity = $similarity / $sum_weight;
} else {
$similarity = 0;
}
return $similarity;
}
//Get all field values for all referenced nodes and summarize them
private function loadSummarizedFieldValues(array $nids, FieldConfig $field_config, string $bundle): array
{
$summerized_field_values = [];
sort($nids);
//check if already in cache
$cache_key = 'cbr_node_reference_summerizied_field_values_' . implode('_', $nids);
if ($cached_item = $this->cache->get($cache_key)) {
$summerized_field_values = $cached_item->data;
} else {
$referenced_entities = Node::loadMultiple($nids);
foreach ($referenced_entities as $entity) {
foreach ($entity->getFields(false) as $field_item) {
//skip entitys which are not the same type
if ($bundle != $entity->bundle()) {
//show message
$this->messenger->addMessage($this->t('Skipping entity @entity_type @entity_id, because it is not of the same type as the first entity.', [
'@entity_type' => $entity->bundle(),
'@entity_id' => $entity->id(),
]), MessengerInterface::TYPE_WARNING);
continue;
}
/* @var FieldConfig $field_config */
$field_config = $field_item->getFieldDefinition()->getConfig($entity->bundle());
if ($field_item->first() !== null && $field_item->first() instanceof CBRFieldInterface) {
$summerized_field_values[$field_item->getName()][] = $field_item->first()->getValueForSimilarityCalculation($field_config);
}
}
}
//Summerize with the method implemented in the field type
foreach ($summerized_field_values as $field_name => $field_values) {
foreach ($referenced_entities as $nid => $entity) {
if ($referenced_entities[$nid]->get($field_name)->first() !== null && is_array($summerized_field_values[$field_name])) {
$summerized_field_values[$field_name] = $referenced_entities[$nid]->get($field_name)->first()->summerize($field_values);
}
}
}
//generate cachetags - these tags are automatically invalidated when the referenced entity is changed
$tags = array_map(function ($nid) {
return 'node:' . $nid;
}, $nids);
//Save in cache
$this->cache->set($cache_key, $summerized_field_values, CacheBackendInterface::CACHE_PERMANENT, $tags);
}
return $summerized_field_values;
}
/**
* {@inheritdoc}
*/
public function getValueForSimilarityCalculation(FieldConfig $field_config): array
{
$nids = [];
foreach ($this->parent as $field) {
$nids[] = $field->getValue()['target_id'];
}
return $nids;
}
//Will never be used
public function summerize(array $fields): mixed
{
return 0;
}
}
