diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 2245ad7..baa10f9 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity; +use Drupal\Component\Utility\Unicode; use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Component\Utility\String; use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException; @@ -334,6 +335,18 @@ public function getEntityType() { * {@inheritdoc} */ public function preSave(EntityStorageInterface $storage) { + // Check if this is an entity bundle. + if ($this->getEntityType()->getBundleOf()) { + // Throw an exception if the bundle ID is longer than 32 characters. + if (Unicode::strlen($this->id()) > EntityTypeInterface::BUNDLE_MAX_LENGTH) { + throw new EntityMalformedException(String::format( + 'Attempt to create a bundle with an ID longer than @max characters: %ID.', array( + '@max' => EntityTypeInterface::BUNDLE_MAX_LENGTH, + '%ID' => $this->id(), + ) + )); + } + } } /** diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php index 93f6062..56a96ab 100644 --- a/core/lib/Drupal/Core/Entity/EntityType.php +++ b/core/lib/Drupal/Core/Entity/EntityType.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity; +use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; /** @@ -182,8 +183,20 @@ class EntityType implements EntityTypeInterface { * * @param array $definition * An array of values from the annotation. + * @throws EntityMalformedException + * When attempting to instantiate an entity type with too long ID. */ public function __construct($definition) { + // Throw an exception if the entity type ID is longer than 32 characters. + if (Unicode::strlen($definition['id'] > static::ID_MAX_LENGTH)) { + throw new EntityMalformedException(String::format( + 'Attempt to create an entity type with an ID longer than @max characters: %ID.', array( + '@max' => static::ID_MAX_LENGTH, + '%ID' => $definition['id'], + ) + )); + } + foreach ($definition as $property => $value) { $this->{$property} = $value; } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php index 17d9555..411c0b9 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php @@ -18,6 +18,12 @@ interface EntityTypeInterface { /** + * The maximum length of ID and bundle name, in characters. + */ + const ID_MAX_LENGTH = 32; + const BUNDLE_MAX_LENGTH = 32; + + /** * Gets any arbitrary property. * * @param string $property diff --git a/core/modules/block/custom_block/custom_block.install b/core/modules/block/custom_block/custom_block.install index 3474559..6c27dd9 100644 --- a/core/modules/block/custom_block/custom_block.install +++ b/core/modules/block/custom_block/custom_block.install @@ -5,6 +5,8 @@ * Install, update and uninstall functions for the custom block module. */ +use Drupal\Core\Entity\EntityTypeInterface; + /** * Implements hook_schema(). */ @@ -44,7 +46,7 @@ function custom_block_schema() { 'type' => array( 'description' => 'The type of this custom block.', 'type' => 'varchar', - 'length' => 32, + 'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH, 'not null' => TRUE, 'default' => '', ), @@ -64,7 +66,7 @@ function custom_block_schema() { ), 'primary key' => array('id'), 'indexes' => array( - 'block_custom_type' => array(array('type', 4)), + 'block_custom_type' => array('type'), ), 'unique keys' => array( 'revision_id' => array('revision_id'), @@ -161,7 +163,7 @@ function custom_block_schema_0() { 'type' => array( 'description' => 'The type of this custom block.', 'type' => 'varchar', - 'length' => 32, + 'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH, 'not null' => TRUE, 'default' => '', ), @@ -181,7 +183,7 @@ function custom_block_schema_0() { ), 'primary key' => array('id'), 'indexes' => array( - 'block_custom_type' => array(array('type', 4)), + 'block_custom_type' => array('type'), ), 'unique keys' => array( 'revision_id' => array('revision_id'), diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php index 5758788..a2f3008 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php @@ -8,6 +8,7 @@ namespace Drupal\custom_block; use Drupal\Core\Entity\EntityFormController; +use Drupal\Core\Entity\EntityTypeInterface; /** * Base form controller for category edit forms. @@ -36,6 +37,7 @@ public function form(array $form, array &$form_state) { '#machine_name' => array( 'exists' => 'custom_block_type_load', ), + '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH, '#disabled' => !$block_type->isNew(), ); diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php index 68414d3..49ba3fc 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php @@ -182,7 +182,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['type'] = FieldDefinition::create('entity_reference') ->setLabel(t('Block type')) ->setDescription(t('The block type.')) - ->setSetting('target_type', 'custom_block_type'); + ->setSetting('target_type', 'custom_block_type') + ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH); $fields['log'] = FieldDefinition::create('string') ->setLabel(t('Revision log message')) diff --git a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php index 5ba7f5e..2101d93 100644 --- a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php +++ b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php @@ -7,6 +7,7 @@ namespace Drupal\comment\Tests\Entity; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityType; use Drupal\Tests\UnitTestCase; /** @@ -74,10 +75,16 @@ public function testLocks() { $comment->expects($this->any()) ->method('getThread') ->will($this->returnValue('')); - $comment->expects($this->at(0)) + + $entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $comment->expects($this->any()) + ->method('getEntityType') + ->will($this->returnValue($entity_type)); + $comment->expects($this->at(1)) ->method('get') ->with('status') ->will($this->returnValue((object) array('value' => NULL))); + $storage = $this->getMock('Drupal\comment\CommentStorageInterface'); $comment->preSave($storage); $comment->postSave($storage); diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install index e474d69..2ff37ea 100644 --- a/core/modules/content_translation/content_translation.install +++ b/core/modules/content_translation/content_translation.install @@ -5,6 +5,7 @@ * Installation functions for Content Translation module. */ +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Language\Language; use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; @@ -17,7 +18,7 @@ function content_translation_schema() { 'fields' => array( 'entity_type' => array( 'type' => 'varchar', - 'length' => 128, + 'length' => EntityTypeInterface::ID_MAX_LENGTH, 'not null' => TRUE, 'default' => '', 'description' => 'The entity type this translation relates to', diff --git a/core/modules/node/lib/Drupal/node/NodeTypeFormController.php b/core/modules/node/lib/Drupal/node/NodeTypeFormController.php index 7d6bd72..63f36f9 100644 --- a/core/modules/node/lib/Drupal/node/NodeTypeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeTypeFormController.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityFormController; use Drupal\Component\Utility\String; +use Drupal\Core\Entity\EntityTypeInterface; /** * Form controller for node type forms. @@ -45,7 +46,7 @@ public function form(array $form, array &$form_state) { $form['type'] = array( '#type' => 'machine_name', '#default_value' => $type->id(), - '#maxlength' => 32, + '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH, '#disabled' => $type->isLocked(), '#machine_name' => array( 'exists' => 'node_type_load', diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php index 4550acb..14ec9c1 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php @@ -121,7 +121,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['vid'] = FieldDefinition::create('entity_reference') ->setLabel(t('Vocabulary')) ->setDescription(t('The vocabulary to which the term is assigned.')) - ->setSetting('target_type', 'taxonomy_vocabulary'); + ->setSetting('target_type', 'taxonomy_vocabulary') + ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH); $fields['langcode'] = FieldDefinition::create('language') ->setLabel(t('Language code')) diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php index 3474bfb..c446c28 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php @@ -8,6 +8,7 @@ namespace Drupal\taxonomy; use Drupal\Core\Entity\EntityFormController; +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Language\Language; /** @@ -37,7 +38,7 @@ public function form(array $form, array &$form_state) { $form['vid'] = array( '#type' => 'machine_name', '#default_value' => $vocabulary->id(), - '#maxlength' => 255, + '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH, '#machine_name' => array( 'exists' => 'taxonomy_vocabulary_load', 'source' => array('name'), diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install index f76d350..a3f7e12 100644 --- a/core/modules/taxonomy/taxonomy.install +++ b/core/modules/taxonomy/taxonomy.install @@ -5,6 +5,8 @@ * Install, update and uninstall functions for the taxonomy module. */ +use Drupal\Core\Entity\EntityTypeInterface; + /** * Implements hook_schema(). */ @@ -26,7 +28,7 @@ function taxonomy_schema() { ), 'vid' => array( 'type' => 'varchar', - 'length' => 255, + 'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH, 'not null' => TRUE, 'default' => '', 'description' => 'The ID of the vocabulary to which the term is assigned.', @@ -75,8 +77,8 @@ function taxonomy_schema() { 'uuid' => array('uuid'), ), 'indexes' => array( - 'taxonomy_tree' => array(array('vid', 64), 'weight', 'name'), - 'vid_name' => array(array('vid', 64), 'name'), + 'taxonomy_tree' => array('vid', 'weight', 'name'), + 'vid_name' => array('vid', 'name'), 'name' => array('name'), ), ); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php index d26ccd1..db3e5d3 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php @@ -11,7 +11,7 @@ use Drupal\Tests\UnitTestCase; /** - * Tests the \Drupal\Core\Entity\EntityType class. + * @coversDefaultClass \Drupal\Core\Entity\EntityType * * @group Drupal * @group Entity @@ -38,6 +38,9 @@ public static function getInfo() { * @return \Drupal\Core\Entity\EntityTypeInterface */ protected function setUpEntityType($definition) { + $definition += array( + 'id' => 'example_entity_type', + ); return new EntityType($definition); } @@ -191,6 +194,26 @@ public function testGetViewBuilderClass() { } /** + * @covers ::__construct + */ + public function testIdExceedsMaxLength() { + $id = $this->randomName(33); + $message = 'Attempt to create an entity type with an ID longer than 32 characters: ' . $id; + $this->setExpectedException('Drupal\Core\Entity\EntityMalformedException', $message); + $this->setUpEntityType(array('id' => $id)); + } + + + /** + * @covers ::id + */ + public function testId() { + $id = $this->randomName(32); + $entity_type = $this->setUpEntityType(array('id' => $id)); + $this->assertEquals($id, $entity_type->id()); + } + + /** * Gets a mock controller class name. * * @return string