Issue #2227227 by mpdonadio, amateescu, yched, larowlan, swentel, tstoeckler, fago: FieldTypePluginManager cannot instantiate FieldType plugins, good thing TypedDataManager can instantiate just about anything

8.0.x
Alex Pott 2015-03-01 10:28:54 +00:00
parent e40099fd39
commit b0f4b418ca
13 changed files with 189 additions and 26 deletions

View File

@ -382,7 +382,7 @@ services:
parent: default_plugin_manager
plugin.manager.field.field_type:
class: Drupal\Core\Field\FieldTypePluginManager
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@typed_data_manager']
plugin.manager.field.widget:
class: Drupal\Core\Field\WidgetPluginManager
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@plugin.manager.field.field_type']

View File

@ -184,15 +184,6 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
}
}
/**
* Returns the typed data manager.
*
* @return \Drupal\Core\TypedData\TypedDataManager
*/
protected function typedDataManager() {
return \Drupal::typedDataManager();
}
/**
* {@inheritdoc}
*/
@ -378,8 +369,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
if (isset($this->values[$name][$langcode])) {
$value = $this->values[$name][$langcode];
}
$entity_adapter = $this->getTypedData();
$field = \Drupal::typedDataManager()->getPropertyInstance($entity_adapter, $name, $value);
$field = \Drupal::service('plugin.manager.field.field_type')->createFieldItemList($this, $name, $value);
if ($default) {
// $this->defaultLangcode might not be set if we are initializing the
// default language code cache, in which case there is no valid
@ -894,7 +884,8 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// Avoid deep-cloning when we are initializing a translation object, since
// it will represent the same entity, only with a different active language.
if (!$this->translationInitialize) {
// The translation is a different object, and needs its own TypedData object.
// The translation is a different object, and needs its own TypedData
// adapter object.
$this->typedData = NULL;
$definitions = $this->getFieldDefinitions();
foreach ($this->fields as $name => $values) {

View File

@ -515,7 +515,7 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
// without modifying the entity being worked on.
if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) {
$items = $entity->get($this->getName());
return \Drupal::typedDataManager()->getPropertyInstance($items, 0);
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
}
// @todo: Allow setting custom options provider, see
// https://www.drupal.org/node/2002138.

View File

@ -78,7 +78,7 @@ abstract class FieldItemBase extends Map implements FieldItemInterface {
* {@inheritdoc}
*/
public function getFieldDefinition() {
return $this->getParent()->getFieldDefinition();
return $this->definition->getFieldDefinition();
}
/**

View File

@ -40,6 +40,13 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
*/
protected $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
/**
* {@inheritdoc}
*/
protected function createItem($offset = 0, $value = NULL) {
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($this, $offset, $value);
}
/**
* {@inheritdoc}
*/

View File

@ -9,8 +9,10 @@ namespace Drupal\Core\Field;
use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\TypedData\TypedDataManager;
/**
* Plugin manager for 'field type' plugins.
@ -19,6 +21,13 @@ use Drupal\Core\Plugin\DefaultPluginManager;
*/
class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePluginManagerInterface {
/**
* The typed data manager.
*
* @var \Drupal\Core\TypedData\TypedDataManager
*/
protected $typedDataManager;
/**
* Constructs the FieldTypePluginManager object
*
@ -29,11 +38,50 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface
* The module handler.
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
* The typed data manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, TypedDataManager $typed_data_manager) {
parent::__construct('Plugin/Field/FieldType', $namespaces, $module_handler, 'Drupal\Core\Field\FieldItemInterface', 'Drupal\Core\Field\Annotation\FieldType');
$this->alterInfo('field_info');
$this->setCacheBackend($cache_backend, 'field_types_plugins');
$this->typedDataManager = $typed_data_manager;
}
/**
* {@inheritdoc}
*
* Creates a field item, which is not part of an entity or field item list.
*
* @param string $field_type
* The field type, for which a field item should be created.
* @param array $configuration
* The plugin configuration array, i.e. an array with the following keys:
* - field_definition: The field definition object, i.e. an instance of
* Drupal\Core\Field\FieldDefinitionInterface.
*
* @return \Drupal\Core\Field\FieldItemInterface
* The instantiated object.
*/
public function createInstance($field_type, array $configuration = array()) {
$configuration['data_definition'] = $configuration['field_definition']->getItemDefinition();
return $this->typedDataManager->createInstance("field_item:$field_type", $configuration);
}
/**
* {@inheritdoc}
*/
public function createFieldItemList(FieldableEntityInterface $entity, $field_name, $values = NULL) {
// Leverage prototyping of the Typed Data API for fast instantiation.
return $this->typedDataManager->getPropertyInstance($entity->getTypedData(), $field_name, $values);
}
/**
* {@inheritdoc}
*/
public function createFieldItem(FieldItemListInterface $items, $index, $values = NULL) {
// Leverage prototyping of the Typed Data API for fast instantiation.
return $this->typedDataManager->getPropertyInstance($items, $index, $values);
}
/**

View File

@ -8,6 +8,7 @@
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
/**
* Defines an interface for the field type plugin manager.
@ -16,6 +17,45 @@ use Drupal\Component\Plugin\PluginManagerInterface;
*/
interface FieldTypePluginManagerInterface extends PluginManagerInterface {
/**
* Creates a new field item list.
*
* The provided entity is assigned as the parent of the created item list.
* However, it is the responsibility of the caller (usually the parent entity
* itself) to make the parent aware of the field as a new child.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity this field item list will be part of.
* @param string $field_name
* The name of the field.
* @param mixed $values
* (optional) The data value. If set, it has to match one of the supported
* data type format as documented for the data type classes.
*
* @return \Drupal\Core\Field\FieldItemListInterface
* The instantiated object.
*/
public function createFieldItemList(FieldableEntityInterface $entity, $field_name, $values = NULL);
/**
* Creates a new field item as part of a field item list.
*
* The provided item list is assigned as the parent of the created item. It
* However, it is the responsibility of the caller (usually the parent list
* itself) to have the parent aware of the item as a new child.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field item list, for which to create a new item.
* @param int $index
* The list index at which the item is created.
* @param array|null $values
* (optional) The values to assign to the field item properties.
*
* @return \Drupal\Core\Field\FieldItemInterface
* The instantiated object.
*/
public function createFieldItem(FieldItemListInterface $items, $index, $values = NULL);
/**
* Returns the default field-level settings for a field type.
*

View File

@ -78,4 +78,14 @@ class FieldItemDataDefinition extends DataDefinition implements ComplexDataDefin
return $this->fieldDefinition->getFieldStorageDefinition()->getMainPropertyName();
}
/**
* Gets the field item's field definition.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface
* The field definition for this field item.
*/
public function getFieldDefinition() {
return $this->fieldDefinition;
}
}

View File

@ -630,7 +630,7 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI
// without modifying the entity being worked on.
if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) {
$items = $entity->get($this->getName());
return \Drupal::typedDataManager()->getPropertyInstance($items, 0);
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
}
// @todo: Allow setting custom options provider, see
// https://www.drupal.org/node/2002138.

View File

@ -7,6 +7,10 @@
namespace Drupal\field\Tests;
use Drupal\Component\Utility\String;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity_test\Entity\EntityTest;
/**
* Tests the field type manager.
*
@ -26,4 +30,63 @@ class FieldTypePluginManagerTest extends FieldUnitTestBase {
}
}
/**
* Tests creation of field item instances.
*/
public function testCreateInstance() {
/** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
foreach (array('test_field', 'shape', 'hidden_test_field') as $type) {
$definition = $field_type_manager->getDefinition($type);
$class = $definition['class'];
$field_name = 'field_' . $type;
$field_definition = BaseFieldDefinition::create($type);
$configuration = array(
'field_definition' => $field_definition,
'name' => $field_name,
'parent' => NULL,
);
$instance = $field_type_manager->createInstance($type, $configuration);
$this->assertTrue($instance instanceof $class, String::format('Created a @class instance', array('@class' => $class)));
$this->assertEqual($field_name, $instance->getName(), String::format('Instance name is @name', array('@name' => $field_name)));
}
}
/**
* Tests creation of field item instances.
*/
public function testCreateInstanceWithConfig() {
/** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$type = 'test_field';
$definition = $field_type_manager->getDefinition($type);
$class = $definition['class'];
$field_name = 'field_' . $type;
$field_definition = BaseFieldDefinition::create($type)
->setLabel('Jenny')
->setDefaultValue(8675309);
$configuration = array(
'field_definition' => $field_definition,
'name' => $field_name,
'parent' => NULL,
);
$entity = EntityTest::create();
$instance = $field_type_manager->createInstance($type, $configuration);
$this->assertTrue($instance instanceof $class, String::format('Created a @class instance', array('@class' => $class)));
$this->assertEqual($field_name, $instance->getName(), String::format('Instance name is @name', array('@name' => $field_name)));
$this->assertEqual($instance->getFieldDefinition()->getLabel(), 'Jenny', 'Instance label is Jenny');
$this->assertEqual($instance->getFieldDefinition()->getDefaultValue($entity), [['value' => 8675309]], 'Instance default_value is 8675309');
}
}

View File

@ -229,9 +229,9 @@ class ContentEntityBaseUnitTest extends UnitTestCase {
->disableOriginalConstructor()
->getMockForAbstractClass();
$this->typedDataManager->expects($this->any())
->method('getPropertyInstance')
->with($this->entity->getTypedData(), 'revision_id', NULL)
$this->fieldTypePluginManager->expects($this->any())
->method('createFieldItemList')
->with($this->entity, 'revision_id', NULL)
->will($this->returnValue($field_item_list));
$this->fieldDefinitions['revision_id']->getItemDefinition()->setClass(get_class($field_item));

View File

@ -164,11 +164,6 @@ class EntityAdapterUnitTest extends UnitTestCase {
->method('getValidationConstraintManager')
->willReturn($validation_constraint_manager);
$this->fieldItemList = $this->getMock('\Drupal\Core\Field\FieldItemListInterface');
$this->typedDataManager->expects($this->any())
->method('getPropertyInstance')
->willReturn($this->fieldItemList);
$not_specified = new Language(array('id' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'locked' => TRUE));
$this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
$this->languageManager->expects($this->any())
@ -190,6 +185,11 @@ class EntityAdapterUnitTest extends UnitTestCase {
->method('getDefaultFieldSettings')
->will($this->returnValue(array()));
$this->fieldItemList = $this->getMock('\Drupal\Core\Field\FieldItemListInterface');
$this->fieldTypePluginManager->expects($this->any())
->method('createFieldItemList')
->willReturn($this->fieldItemList);
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('uuid', $this->uuid);

View File

@ -40,10 +40,14 @@ abstract class BaseFieldDefinitionTestBase extends UnitTestCase {
->method('moduleExists')
->with($module_name)
->will($this->returnValue(TRUE));
$typed_data_manager = $this->getMockBuilder('\Drupal\Core\TypedData\TypedDataManager')
->disableOriginalConstructor()
->getMock();
$plugin_manager = new FieldTypePluginManager(
$namespaces,
$this->getMock('Drupal\Core\Cache\CacheBackendInterface'),
$module_handler
$module_handler,
$typed_data_manager
);
$container = new ContainerBuilder();