Issue #2015687 by smiletrl, amateescu, Berdir, fago: Convert field type to FieldType plugin for taxonomy module.
parent
65be82a128
commit
c2f76e09d4
|
@ -8,80 +8,14 @@
|
||||||
namespace Drupal\Core\Field;
|
namespace Drupal\Core\Field;
|
||||||
|
|
||||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||||
use Drupal\Core\TypedData\DataDefinition;
|
|
||||||
use Drupal\field\FieldInstanceInterface;
|
|
||||||
use Drupal\field\FieldInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A common base class for configurable entity reference fields.
|
* A common base class for configurable entity reference fields.
|
||||||
*
|
*
|
||||||
* Extends the Core 'entity_reference' entity field item with properties for
|
* Extends the Core 'entity_reference' entity field item with common methods
|
||||||
* revision ids, labels (for autocreate) and access.
|
* used in general configurable entity reference field.
|
||||||
*
|
|
||||||
* Required settings (below the definition's 'settings' key) are:
|
|
||||||
* - target_type: The entity type to reference.
|
|
||||||
*/
|
*/
|
||||||
class ConfigEntityReferenceItemBase extends EntityReferenceItem implements ConfigFieldItemInterface {
|
class ConfigEntityReferenceItemBase extends EntityReferenceItem {
|
||||||
|
|
||||||
/**
|
|
||||||
* Definitions of the contained properties.
|
|
||||||
*
|
|
||||||
* @see ConfigurableEntityReferenceItem::getPropertyDefinitions()
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
static $propertyDefinitions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyDefinitions() {
|
|
||||||
$settings = $this->definition->getSettings();
|
|
||||||
$target_type = $settings['target_type'];
|
|
||||||
|
|
||||||
// Definitions vary by entity type and bundle, so key them accordingly.
|
|
||||||
$key = $target_type . ':';
|
|
||||||
$key .= isset($settings['target_bundle']) ? $settings['target_bundle'] : '';
|
|
||||||
|
|
||||||
if (!isset(static::$propertyDefinitions[$key])) {
|
|
||||||
// Call the parent to define the target_id and entity properties.
|
|
||||||
parent::getPropertyDefinitions();
|
|
||||||
|
|
||||||
// Only add the revision ID property if the target entity type supports
|
|
||||||
// revisions.
|
|
||||||
$target_type_info = \Drupal::entityManager()->getDefinition($target_type);
|
|
||||||
if (!empty($target_type_info['entity_keys']['revision']) && !empty($target_type_info['revision_table'])) {
|
|
||||||
static::$propertyDefinitions[$key]['revision_id'] = DataDefinition::create('integer')
|
|
||||||
->setLabel(t('Revision ID'))
|
|
||||||
->setConstraints(array('Range' => array('min' => 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static::$propertyDefinitions[$key]['label'] = DataDefinition::create('string')
|
|
||||||
->setLabel(t('Label (auto-create)'))
|
|
||||||
->setComputed(TRUE);
|
|
||||||
|
|
||||||
static::$propertyDefinitions[$key]['access'] = DataDefinition::create('boolean')
|
|
||||||
->setLabel(t('Access'))
|
|
||||||
->setComputed(TRUE);
|
|
||||||
}
|
|
||||||
return static::$propertyDefinitions[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
|
|
||||||
* since we cannot extend it.
|
|
||||||
*/
|
|
||||||
public static function schema(FieldInterface $field) {
|
|
||||||
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->type);
|
|
||||||
$module = $definition['provider'];
|
|
||||||
module_load_install($module);
|
|
||||||
$callback = "{$module}_field_schema";
|
|
||||||
if (function_exists($callback)) {
|
|
||||||
return $callback($field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
|
@ -101,75 +35,14 @@ class ConfigEntityReferenceItemBase extends EntityReferenceItem implements Confi
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
|
|
||||||
* since we cannot extend it.
|
|
||||||
*/
|
*/
|
||||||
public function settingsForm(array $form, array &$form_state, $has_data) {
|
public function preSave() {
|
||||||
if ($callback = $this->getLegacyCallback('settings_form')) {
|
$entity = $this->get('entity')->getValue();
|
||||||
$instance = $this->getFieldDefinition();
|
$target_id = $this->get('target_id')->getValue();
|
||||||
if (!($instance instanceof FieldInstanceInterface)) {
|
|
||||||
throw new \UnexpectedValueException('ConfigEntityReferenceItemBase::settingsForm() called for a field whose definition is not a field instance.');
|
|
||||||
}
|
|
||||||
// hook_field_settings_form() used to receive the $instance (not actually
|
|
||||||
// needed), and the value of field_has_data().
|
|
||||||
return $callback($instance->getField(), $instance, $has_data);
|
|
||||||
}
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (!$target_id && !empty($entity) && $entity->isNew()) {
|
||||||
* {@inheritdoc}
|
$entity->save();
|
||||||
*
|
$this->set('target_id', $entity->id());
|
||||||
* Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
|
|
||||||
* since we cannot extend it.
|
|
||||||
*/
|
|
||||||
public function instanceSettingsForm(array $form, array &$form_state) {
|
|
||||||
if ($callback = $this->getLegacyCallback('instance_settings_form')) {
|
|
||||||
$instance = $this->getFieldDefinition();
|
|
||||||
if (!($instance instanceof FieldInstanceInterface)) {
|
|
||||||
throw new \UnexpectedValueException('ConfigEntityReferenceItemBase::instanceSettingsForm() called for a field whose definition is not a field instance.');
|
|
||||||
}
|
|
||||||
return $callback($instance->getField(), $instance, $form_state);
|
|
||||||
}
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns options provided via the legacy callback hook_options_list().
|
|
||||||
*
|
|
||||||
* @todo: Convert all legacy callback implementations to methods.
|
|
||||||
*
|
|
||||||
* @see \Drupal\Core\TypedData\AllowedValuesInterface
|
|
||||||
*/
|
|
||||||
public function getSettableOptions() {
|
|
||||||
$definition = $this->getPluginDefinition();
|
|
||||||
$callback = "{$definition['provider']}_options_list";
|
|
||||||
if (function_exists($callback)) {
|
|
||||||
// We are at the field item level, so we need to go two levels up to get
|
|
||||||
// to the entity object.
|
|
||||||
return $callback($this->getFieldDefinition(), $this->getEntity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the legacy callback for a given field type "hook".
|
|
||||||
*
|
|
||||||
* Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
|
|
||||||
* since we cannot extend it.
|
|
||||||
*
|
|
||||||
* @param string $hook
|
|
||||||
* The name of the hook, e.g. 'settings_form', 'is_empty'.
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* The name of the legacy callback, or NULL if it does not exist.
|
|
||||||
*/
|
|
||||||
protected function getLegacyCallback($hook) {
|
|
||||||
$definition = $this->getPluginDefinition();
|
|
||||||
$module = $definition['provider'];
|
|
||||||
$callback = "{$module}_field_{$hook}";
|
|
||||||
if (function_exists($callback)) {
|
|
||||||
return $callback;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,4 +211,11 @@ abstract class FieldItemBase extends Map implements FieldItemInterface {
|
||||||
*/
|
*/
|
||||||
public function deleteRevision() { }
|
public function deleteRevision() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getMainPropertyName() {
|
||||||
|
return 'value';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,4 +136,20 @@ interface FieldItemInterface extends ComplexDataInterface {
|
||||||
*/
|
*/
|
||||||
public function deleteRevision();
|
public function deleteRevision();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the main property, if any.
|
||||||
|
*
|
||||||
|
* Some field items consist mainly of one main property, e.g. the value of a
|
||||||
|
* text field or the @code target_id @endcode of an entity reference. If the
|
||||||
|
* field item has no main property, the method returns NULL.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
* The name of the value property, or NULL if there is none.
|
||||||
|
*
|
||||||
|
* @todo: Move this to ComplexDataInterface once we improved Typed data to do
|
||||||
|
* not enforce having all methods on the data objects.
|
||||||
|
* https://drupal.org/node/2002134
|
||||||
|
*/
|
||||||
|
public function getMainPropertyName();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,4 +141,12 @@ class EntityReferenceItem extends FieldItemBase {
|
||||||
}
|
}
|
||||||
parent::onChange($property_name);
|
parent::onChange($property_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getMainPropertyName() {
|
||||||
|
return 'target_id';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,11 @@ use Drupal\Core\Session\AccountInterface;
|
||||||
* as structured options arrays that can be used in an Options widget such as a
|
* as structured options arrays that can be used in an Options widget such as a
|
||||||
* select box or checkboxes.
|
* select box or checkboxes.
|
||||||
*
|
*
|
||||||
|
* Note that this interface is mostly applicable for primitive data values, but
|
||||||
|
* can be used on complex data structures if a (primitive) main property is
|
||||||
|
* specified. In that case, the allowed values and options apply to the main
|
||||||
|
* property only.
|
||||||
|
*
|
||||||
* @see \Drupal\options\Plugin\Field\FieldWidget\OptionsWidgetBase
|
* @see \Drupal\options\Plugin\Field\FieldWidget\OptionsWidgetBase
|
||||||
*/
|
*/
|
||||||
interface AllowedValuesInterface {
|
interface AllowedValuesInterface {
|
||||||
|
|
|
@ -362,7 +362,7 @@ class TypedDataManager extends DefaultPluginManager {
|
||||||
$class = $type_definition['class'];
|
$class = $type_definition['class'];
|
||||||
}
|
}
|
||||||
// Check if the class provides allowed values.
|
// Check if the class provides allowed values.
|
||||||
if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) {
|
if (is_subclass_of($class,'Drupal\Core\TypedData\AllowedValuesInterface')) {
|
||||||
$constraints[] = $validation_manager->create('AllowedValues', array());
|
$constraints[] = $validation_manager->create('AllowedValues', array());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
||||||
|
|
||||||
use Drupal\Core\TypedData\AllowedValuesInterface;
|
use Drupal\Core\TypedData\AllowedValuesInterface;
|
||||||
|
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\Constraints\ChoiceValidator;
|
use Symfony\Component\Validator\Constraints\ChoiceValidator;
|
||||||
|
|
||||||
|
@ -20,14 +21,32 @@ class AllowedValuesConstraintValidator extends ChoiceValidator {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function validate($value, Constraint $constraint) {
|
public function validate($value, Constraint $constraint) {
|
||||||
|
$typed_data = $this->context->getMetadata()->getTypedData();
|
||||||
|
|
||||||
|
if ($typed_data instanceof AllowedValuesInterface) {
|
||||||
|
$account = \Drupal::currentUser();
|
||||||
|
$allowed_values = $typed_data->getSettableValues($account);
|
||||||
|
$constraint->choices = $allowed_values;
|
||||||
|
|
||||||
|
// If the data is complex, we have to validate its main property.
|
||||||
|
if ($typed_data instanceof ComplexDataInterface) {
|
||||||
|
$name = $typed_data->getMainPropertyName();
|
||||||
|
if (!isset($name)) {
|
||||||
|
throw new \LogicException('Cannot validate allowed values for complex data without a main property.');
|
||||||
|
}
|
||||||
|
$value = $typed_data->get($name)->getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parent implementation ignores values that are not set, but makes
|
||||||
|
// sure some choices are available firstly. However, we want to support
|
||||||
|
// empty choices for undefined values, e.g. if a term reference field
|
||||||
|
// points to an empty vocabulary.
|
||||||
if (!isset($value)) {
|
if (!isset($value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) {
|
|
||||||
$account = \Drupal::currentUser();
|
parent::validate($value, $constraint);
|
||||||
$allowed_values = $this->context->getMetadata()->getTypedData()->getSettableValues($account);
|
|
||||||
$constraint->choices = $allowed_values;
|
|
||||||
}
|
|
||||||
return parent::validate($value, $constraint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
namespace Drupal\entity_reference;
|
namespace Drupal\entity_reference;
|
||||||
|
|
||||||
|
use Drupal\Core\TypedData\DataDefinition;
|
||||||
use Drupal\field\FieldInterface;
|
use Drupal\field\FieldInterface;
|
||||||
use Drupal\Core\Field\ConfigEntityReferenceItemBase;
|
use Drupal\Core\Field\ConfigEntityReferenceItemBase;
|
||||||
use Drupal\Core\Field\ConfigFieldItemInterface;
|
use Drupal\Core\Field\ConfigFieldItemInterface;
|
||||||
|
@ -17,11 +18,51 @@ use Drupal\Core\Field\ConfigFieldItemInterface;
|
||||||
* Replaces the Core 'entity_reference' entity field type implementation, this
|
* Replaces the Core 'entity_reference' entity field type implementation, this
|
||||||
* supports configurable fields, auto-creation of referenced entities and more.
|
* supports configurable fields, auto-creation of referenced entities and more.
|
||||||
*
|
*
|
||||||
|
* Required settings (below the definition's 'settings' key) are:
|
||||||
|
* - target_type: The entity type to reference.
|
||||||
|
*
|
||||||
* @see entity_reference_field_info_alter().
|
* @see entity_reference_field_info_alter().
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class ConfigurableEntityReferenceItem extends ConfigEntityReferenceItemBase implements ConfigFieldItemInterface {
|
class ConfigurableEntityReferenceItem extends ConfigEntityReferenceItemBase implements ConfigFieldItemInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definitions of the contained properties.
|
||||||
|
*
|
||||||
|
* @see ConfigurableEntityReferenceItem::getPropertyDefinitions()
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
static $propertyDefinitions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPropertyDefinitions() {
|
||||||
|
$settings = $this->definition->getSettings();
|
||||||
|
$target_type = $settings['target_type'];
|
||||||
|
|
||||||
|
// Definitions vary by entity type and bundle, so key them accordingly.
|
||||||
|
$key = $target_type . ':';
|
||||||
|
$key .= isset($settings['target_bundle']) ? $settings['target_bundle'] : '';
|
||||||
|
|
||||||
|
if (!isset(static::$propertyDefinitions[$key])) {
|
||||||
|
// Call the parent to define the target_id and entity properties.
|
||||||
|
parent::getPropertyDefinitions();
|
||||||
|
|
||||||
|
// Only add the revision ID property if the target entity type supports
|
||||||
|
// revisions.
|
||||||
|
$target_type_info = \Drupal::entityManager()->getDefinition($target_type);
|
||||||
|
if (!empty($target_type_info['entity_keys']['revision']) && !empty($target_type_info['revision_table'])) {
|
||||||
|
static::$propertyDefinitions[$key]['revision_id'] = DataDefinition::create('integer')
|
||||||
|
->setLabel(t('Revision ID'))
|
||||||
|
->setConstraints(array('Range' => array('min' => 0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::$propertyDefinitions[$key];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -65,19 +106,6 @@ class ConfigurableEntityReferenceItem extends ConfigEntityReferenceItemBase impl
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function preSave() {
|
|
||||||
$entity = $this->get('entity')->getValue();
|
|
||||||
$target_id = $this->get('target_id')->getValue();
|
|
||||||
|
|
||||||
if (!$target_id && !empty($entity) && $entity->isNew()) {
|
|
||||||
$entity->save();
|
|
||||||
$this->set('target_id', $entity->id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,7 +27,7 @@ abstract class TaxonomyFormatterBase extends FormatterBase {
|
||||||
foreach ($entities_items as $items) {
|
foreach ($entities_items as $items) {
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
// Force the array key to prevent duplicates.
|
// Force the array key to prevent duplicates.
|
||||||
if ($item->target_id !== 0) {
|
if ($item->target_id != NULL) {
|
||||||
$tids[$item->target_id] = $item->target_id;
|
$tids[$item->target_id] = $item->target_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ abstract class TaxonomyFormatterBase extends FormatterBase {
|
||||||
$item->entity = $terms[$item->target_id];
|
$item->entity = $terms[$item->target_id];
|
||||||
}
|
}
|
||||||
// Terms to be created are not in $terms, but are still legitimate.
|
// Terms to be created are not in $terms, but are still legitimate.
|
||||||
elseif ($item->target_id === 0 && isset($item->entity)) {
|
elseif ($item->target_id === NULL && isset($item->entity)) {
|
||||||
// Leave the item in place.
|
// Leave the item in place.
|
||||||
}
|
}
|
||||||
// Otherwise, unset the instance value, since the term does not exist.
|
// Otherwise, unset the instance value, since the term does not exist.
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
|
|
||||||
namespace Drupal\taxonomy\Plugin\Field\FieldType;
|
namespace Drupal\taxonomy\Plugin\Field\FieldType;
|
||||||
|
|
||||||
use Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList;
|
use Drupal\Core\Field\ConfigFieldItemList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a configurable taxonomy_term_reference entity field.
|
* Represents a configurable taxonomy_term_reference entity field item list.
|
||||||
*/
|
*/
|
||||||
class TaxonomyTermReferenceFieldItemList extends LegacyConfigFieldItemList {
|
class TaxonomyTermReferenceFieldItemList extends ConfigFieldItemList {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\taxonomy\Plugin\field\field_type\TaxonomyTermReferenceItem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\taxonomy\Plugin\Field\FieldType;
|
||||||
|
|
||||||
|
use Drupal\Core\Field\ConfigEntityReferenceItemBase;
|
||||||
|
use Drupal\Core\Field\ConfigFieldItemInterface;
|
||||||
|
use Drupal\field\FieldInterface;
|
||||||
|
use Drupal\Core\Session\AccountInterface;
|
||||||
|
use Drupal\Core\TypedData\AllowedValuesInterface;
|
||||||
|
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||||
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin implementation of the 'term_reference' field type.
|
||||||
|
*
|
||||||
|
* @FieldType(
|
||||||
|
* id = "taxonomy_term_reference",
|
||||||
|
* label = @Translation("Term Reference"),
|
||||||
|
* description = @Translation("This field stores a reference to a taxonomy term."),
|
||||||
|
* settings = {
|
||||||
|
* "options_list_callback" = NULL,
|
||||||
|
* "allowed_values" = {
|
||||||
|
* {
|
||||||
|
* "vocabulary" = "",
|
||||||
|
* "parent" = "0"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* instance_settings = { },
|
||||||
|
* default_widget = "options_select",
|
||||||
|
* default_formatter = "taxonomy_term_reference_link",
|
||||||
|
* list_class = "\Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceFieldItemList"
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
class TaxonomyTermReferenceItem extends ConfigEntityReferenceItemBase implements ConfigFieldItemInterface, AllowedValuesInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPossibleValues(AccountInterface $account = NULL) {
|
||||||
|
// Flatten options firstly, because Possible Options may contain group
|
||||||
|
// arrays.
|
||||||
|
$flatten_options = $this->flattenOptions($this->getPossibleOptions($account));
|
||||||
|
return array_keys($flatten_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPossibleOptions(AccountInterface $account = NULL) {
|
||||||
|
return $this->getSettableOptions($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSettableValues(AccountInterface $account = NULL) {
|
||||||
|
// Flatten options firstly, because Settable Options may contain group
|
||||||
|
// arrays.
|
||||||
|
$flatten_options = $this->flattenOptions($this->getSettableOptions($account));
|
||||||
|
return array_keys($flatten_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSettableOptions(AccountInterface $account = NULL) {
|
||||||
|
$instance = $this->getFieldDefinition();
|
||||||
|
$entity = $this->getParent()->getParent();
|
||||||
|
$function = $this->getFieldSetting('options_list_callback') ? $this->getFieldSetting('options_list_callback') : array($this, 'getDefaultOptions');
|
||||||
|
return call_user_func_array($function, array($instance, $entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPropertyDefinitions() {
|
||||||
|
$this->definition['settings']['target_type'] = 'taxonomy_term';
|
||||||
|
return parent::getPropertyDefinitions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function schema(FieldInterface $field) {
|
||||||
|
return array(
|
||||||
|
'columns' => array(
|
||||||
|
'target_id' => array(
|
||||||
|
'type' => 'int',
|
||||||
|
'unsigned' => TRUE,
|
||||||
|
'not null' => FALSE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'indexes' => array(
|
||||||
|
'target_id' => array('target_id'),
|
||||||
|
),
|
||||||
|
'foreign keys' => array(
|
||||||
|
'target_id' => array(
|
||||||
|
'table' => 'taxonomy_term_data',
|
||||||
|
'columns' => array('target_id' => 'tid'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function settingsForm(array $form, array &$form_state, $has_data) {
|
||||||
|
// Get proper values for 'allowed_values_function', which is a core setting.
|
||||||
|
$vocabularies = entity_load_multiple('taxonomy_vocabulary');
|
||||||
|
$options = array();
|
||||||
|
foreach ($vocabularies as $vocabulary) {
|
||||||
|
$options[$vocabulary->id()] = $vocabulary->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $this->getFieldSettings();
|
||||||
|
$element = array();
|
||||||
|
$element['#tree'] = TRUE;
|
||||||
|
|
||||||
|
foreach ($settings['allowed_values'] as $delta => $tree) {
|
||||||
|
$element['allowed_values'][$delta]['vocabulary'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Vocabulary'),
|
||||||
|
'#default_value' => $tree['vocabulary'],
|
||||||
|
'#options' => $options,
|
||||||
|
'#required' => TRUE,
|
||||||
|
'#description' => t('The vocabulary which supplies the options for this field.'),
|
||||||
|
'#disabled' => $has_data,
|
||||||
|
);
|
||||||
|
$element['allowed_values'][$delta]['parent'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => $tree['parent'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function instanceSettingsForm(array $form, array &$form_state) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flattens an array of allowed values.
|
||||||
|
*
|
||||||
|
* @todo Define this function somewhere else, so we don't have to redefine it
|
||||||
|
* when other field type classes, e.g. list option, need it too.
|
||||||
|
* https://drupal.org/node/2138803
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
* A single or multidimensional array.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The flattened array.
|
||||||
|
*/
|
||||||
|
protected function flattenOptions(array $array) {
|
||||||
|
$result = array();
|
||||||
|
array_walk_recursive($array, function($a, $b) use (&$result) { $result[$b] = $a; });
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns default set of valid terms for a taxonomy field.
|
||||||
|
*
|
||||||
|
* Contrib code could make use of field setting's "options_list_callback" to
|
||||||
|
* provide custom options for taxonomy term reference field.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||||
|
* The field definition.
|
||||||
|
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||||
|
* The entity object the field is attached to.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The array of valid terms for this field, keyed by term id.
|
||||||
|
*/
|
||||||
|
public function getDefaultOptions(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
|
||||||
|
$options = array();
|
||||||
|
foreach ($field_definition->getSetting('allowed_values') as $tree) {
|
||||||
|
if ($vocabulary = entity_load('taxonomy_vocabulary', $tree['vocabulary'])) {
|
||||||
|
if ($terms = taxonomy_get_tree($vocabulary->id(), $tree['parent'], NULL, TRUE)) {
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
$options[$term->id()] = str_repeat('-', $term->depth) . $term->label();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -114,7 +114,7 @@ class TaxonomyAutocompleteWidget extends WidgetBase {
|
||||||
'vid' => $vocabulary->id(),
|
'vid' => $vocabulary->id(),
|
||||||
'name' => $value,
|
'name' => $value,
|
||||||
));
|
));
|
||||||
$item = array('target_id' => 0, 'entity' => $term);
|
$item = array('target_id' => NULL, 'entity' => $term);
|
||||||
}
|
}
|
||||||
$items[] = $item;
|
$items[] = $item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,30 +164,6 @@ function taxonomy_schema() {
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_schema().
|
|
||||||
*/
|
|
||||||
function taxonomy_field_schema($field) {
|
|
||||||
return array(
|
|
||||||
'columns' => array(
|
|
||||||
'target_id' => array(
|
|
||||||
'type' => 'int',
|
|
||||||
'unsigned' => TRUE,
|
|
||||||
'not null' => FALSE,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'indexes' => array(
|
|
||||||
'target_id' => array('target_id'),
|
|
||||||
),
|
|
||||||
'foreign keys' => array(
|
|
||||||
'target_id' => array(
|
|
||||||
'table' => 'taxonomy_term_data',
|
|
||||||
'columns' => array('target_id' => 'tid'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_update_dependencies().
|
* Implements hook_update_dependencies().
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,9 +7,6 @@
|
||||||
|
|
||||||
use Drupal\Core\Entity\FieldableDatabaseStorageController;
|
use Drupal\Core\Entity\FieldableDatabaseStorageController;
|
||||||
use Drupal\Core\Entity\EntityInterface;
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
|
||||||
use Drupal\field\FieldInterface;
|
|
||||||
use Drupal\field\FieldInstanceInterface;
|
|
||||||
use Drupal\file\FileInterface;
|
use Drupal\file\FileInterface;
|
||||||
use Drupal\node\Entity\Node;
|
use Drupal\node\Entity\Node;
|
||||||
use Drupal\taxonomy\Entity\Term;
|
use Drupal\taxonomy\Entity\Term;
|
||||||
|
@ -800,39 +797,6 @@ function taxonomy_implode_tags($tags, $vid = NULL) {
|
||||||
return implode(', ', $typed_tags);
|
return implode(', ', $typed_tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_info().
|
|
||||||
*
|
|
||||||
* Field settings:
|
|
||||||
* - allowed_values: a list array of one or more vocabulary trees:
|
|
||||||
* - vocabulary: a vocabulary machine name.
|
|
||||||
* - parent: a term ID of a term whose children are allowed. This should be
|
|
||||||
* '0' if all terms in a vocabulary are allowed. The allowed values do not
|
|
||||||
* include the parent term.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function taxonomy_field_info() {
|
|
||||||
return array(
|
|
||||||
'taxonomy_term_reference' => array(
|
|
||||||
'label' => t('Term reference'),
|
|
||||||
'description' => t('This field stores a reference to a taxonomy term.'),
|
|
||||||
'default_widget' => 'options_select',
|
|
||||||
'default_formatter' => 'taxonomy_term_reference_link',
|
|
||||||
'class' => 'Drupal\taxonomy\Type\TaxonomyTermReferenceItem',
|
|
||||||
'settings' => array(
|
|
||||||
'options_list_callback' => NULL,
|
|
||||||
'allowed_values' => array(
|
|
||||||
array(
|
|
||||||
'vocabulary' => '',
|
|
||||||
'parent' => '0',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'list_class' => '\Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceFieldItemList',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_field_widget_info_alter().
|
* Implements hook_field_widget_info_alter().
|
||||||
*/
|
*/
|
||||||
|
@ -841,105 +805,6 @@ function taxonomy_field_widget_info_alter(&$info) {
|
||||||
$info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
|
$info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_options_list().
|
|
||||||
*/
|
|
||||||
function taxonomy_options_list(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
|
|
||||||
$function = $field_definition->getSetting('options_list_callback') ?: 'taxonomy_allowed_values';
|
|
||||||
return $function($field_definition, $entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_validate().
|
|
||||||
*
|
|
||||||
* Taxonomy field settings allow for either a single vocabulary ID, multiple
|
|
||||||
* vocabulary IDs, or sub-trees of a vocabulary to be specified as allowed
|
|
||||||
* values, although only the first of these is supported via the field UI.
|
|
||||||
* Confirm that terms entered as values meet at least one of these conditions.
|
|
||||||
*
|
|
||||||
* Possible error codes:
|
|
||||||
* - 'taxonomy_term_illegal_value': The value is not part of the list of allowed values.
|
|
||||||
*/
|
|
||||||
function taxonomy_field_validate(EntityInterface $entity = NULL, FieldInterface $field, FieldInstanceInterface $instance, $langcode, $items, &$errors) {
|
|
||||||
// Build an array of existing term IDs so they can be loaded with
|
|
||||||
// entity_load_multiple('taxonomy_term');
|
|
||||||
foreach ($items as $delta => $item) {
|
|
||||||
if (!empty($item['target_id']) && $item['target_id'] != 'autocreate') {
|
|
||||||
$tids[] = $item['target_id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!empty($tids)) {
|
|
||||||
$terms = entity_load_multiple('taxonomy_term', $tids);
|
|
||||||
|
|
||||||
// Check each existing item to ensure it can be found in the
|
|
||||||
// allowed values for this field.
|
|
||||||
foreach ($items as $delta => $item) {
|
|
||||||
$validate = TRUE;
|
|
||||||
if (!empty($item['target_id']) && $item['target_id'] != 'autocreate') {
|
|
||||||
$validate = FALSE;
|
|
||||||
foreach ($instance->getSetting('allowed_values') as $settings) {
|
|
||||||
// If no parent is specified, check if the term is in the vocabulary.
|
|
||||||
if (isset($settings['vocabulary']) && empty($settings['parent'])) {
|
|
||||||
if ($settings['vocabulary'] == $terms[$item['target_id']]->bundle()) {
|
|
||||||
$validate = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If a parent is specified, then to validate it must appear in the
|
|
||||||
// array returned by taxonomy_term_load_parents_all().
|
|
||||||
elseif (!empty($settings['parent'])) {
|
|
||||||
$ancestors = taxonomy_term_load_parents_all($item['target_id']);
|
|
||||||
foreach ($ancestors as $ancestor) {
|
|
||||||
if ($ancestor->id() == $settings['parent']) {
|
|
||||||
$validate = TRUE;
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$validate) {
|
|
||||||
$errors[$instance->getName()][$langcode][$delta][] = array(
|
|
||||||
'error' => 'taxonomy_term_reference_illegal_value',
|
|
||||||
'message' => t('%name: illegal value.', array('%name' => $instance->getLabel())),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_is_empty().
|
|
||||||
*/
|
|
||||||
function taxonomy_field_is_empty($item, $field_type) {
|
|
||||||
return !is_array($item) || (empty($item['target_id']) && empty($item['entity']));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the set of valid terms for a taxonomy field.
|
|
||||||
*
|
|
||||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
|
||||||
* The field definition.
|
|
||||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
|
||||||
* The entity object the field is attached to.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The array of valid terms for this field, keyed by term id.
|
|
||||||
*/
|
|
||||||
function taxonomy_allowed_values(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
|
|
||||||
$options = array();
|
|
||||||
foreach ($field_definition->getSetting('allowed_values') as $tree) {
|
|
||||||
if ($vocabulary = entity_load('taxonomy_vocabulary', $tree['vocabulary'])) {
|
|
||||||
if ($terms = taxonomy_get_tree($vocabulary->id(), $tree['parent'], NULL, TRUE)) {
|
|
||||||
foreach ($terms as $term) {
|
|
||||||
$options[$term->id()] = str_repeat('-', $term->depth) . $term->label();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title callback for term pages.
|
* Title callback for term pages.
|
||||||
*
|
*
|
||||||
|
@ -966,39 +831,6 @@ function taxonomy_autocomplete_validate($element, &$form_state) {
|
||||||
form_set_value($element, $typed_terms, $form_state);
|
form_set_value($element, $typed_terms, $form_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_settings_form().
|
|
||||||
*/
|
|
||||||
function taxonomy_field_settings_form($field, $instance) {
|
|
||||||
// Get proper values for 'allowed_values_function', which is a core setting.
|
|
||||||
$vocabularies = entity_load_multiple('taxonomy_vocabulary');
|
|
||||||
$options = array();
|
|
||||||
foreach ($vocabularies as $vocabulary) {
|
|
||||||
$options[$vocabulary->id()] = $vocabulary->name;
|
|
||||||
}
|
|
||||||
$form['allowed_values'] = array(
|
|
||||||
'#tree' => TRUE,
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($field->getSetting('allowed_values') as $delta => $tree) {
|
|
||||||
$form['allowed_values'][$delta]['vocabulary'] = array(
|
|
||||||
'#type' => 'select',
|
|
||||||
'#title' => t('Vocabulary'),
|
|
||||||
'#default_value' => $tree['vocabulary'],
|
|
||||||
'#options' => $options,
|
|
||||||
'#required' => TRUE,
|
|
||||||
'#description' => t('The vocabulary which supplies the options for this field.'),
|
|
||||||
'#disabled' => $field->hasData(),
|
|
||||||
);
|
|
||||||
$form['allowed_values'][$delta]['parent'] = array(
|
|
||||||
'#type' => 'value',
|
|
||||||
'#value' => $tree['parent'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @defgroup taxonomy_index Taxonomy indexing
|
* @defgroup taxonomy_index Taxonomy indexing
|
||||||
* @{
|
* @{
|
||||||
|
@ -1017,20 +849,6 @@ function taxonomy_field_settings_form($field, $instance) {
|
||||||
* unnecessary writes in SQL.
|
* unnecessary writes in SQL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_presave().
|
|
||||||
*
|
|
||||||
* Create any new terms defined in a freetagging vocabulary.
|
|
||||||
*/
|
|
||||||
function taxonomy_field_presave(EntityInterface $entity, $field, $instance, $langcode, &$items) {
|
|
||||||
foreach ($items as $delta => $item) {
|
|
||||||
if (!$item['target_id'] && isset($item['target_id'])) {
|
|
||||||
$item['entity']->save();
|
|
||||||
$items[$delta]['target_id'] = $item['entity']->id();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_node_insert().
|
* Implements hook_node_insert().
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue