diff --git a/core/includes/entity.inc b/core/includes/entity.inc index 463dcd0..71743e3 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -203,7 +203,13 @@ function entity_delete_multiple($entity_type, array $ids) { } /** - * Constructs a new entity object, without permanently saving it. + * Constructs a new entity object with default values, without saving it. + * + * Returns an entity object with default values applied. If you need an empty + * entity object use \Drupal\Core\Entity\EntityManager::createInstance() + * instead. + * + * @see Drupal\Core\Entity\EntityManager::createInstance() * * @param string $entity_type * The type of the entity. @@ -212,13 +218,10 @@ function entity_delete_multiple($entity_type, array $ids) { * entity type has bundles, the bundle key has to be specified. * * @return \Drupal\Core\Entity\EntityInterface - * A new entity object. + * A new entity object with default values applied. */ function entity_create($entity_type, array $values = array()) { - return \Drupal::entityManager() - ->getStorage($entity_type) - ->create($values); -} + return \Drupal::entityManager()->createEntity($entity_type, $values);} /** * Returns the label of an entity. diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 16de8fb..0bcbcad 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -153,16 +153,33 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans $this->langcodeKey = $this->getEntityType()->getKey('langcode'); $this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode'); - foreach ($values as $key => $value) { - // If the key matches an existing property set the value to the property - // to set properties like isDefaultRevision. - // @todo: Should this be converted somehow? - if (property_exists($this, $key) && isset($value[LanguageInterface::LANGCODE_DEFAULT])) { - $this->$key = $value[LanguageInterface::LANGCODE_DEFAULT]; + // This is weird, any bright ideas anyone? + if (!empty($values['isDefaultRevision'])) { + $this->isDefaultRevision($values['isDefaultRevision'][Language::LANGCODE_DEFAULT]); + unset($values['isDefaultRevision']); + } + + // Initialize language information of the entity. + $values_langcode = Language::LANGCODE_NOT_SPECIFIED; + if (!empty($values['langcode'])) { + if (is_array($values['langcode'])) { + if (isset($values['langcode'][0])) { + $values_langcode = $values['langcode'][0]['value']; + } + else { + $values_langcode = reset($values['langcode']); + if (!is_string($values_langcode)) { + throw new \RuntimeException("Unsupported data structure for language code"); + } + } + } + else { + $values_langcode = $values['langcode']; } } + $this->language = isset($this->languages[$values_langcode]) ? $this->languages[$values_langcode] : new Language(array('id' => $values_langcode)); + $this->defaultLangcode = $values_langcode; - $this->values = $values; foreach ($this->getEntityType()->getKeys() as $key => $field_name) { if (isset($this->values[$field_name])) { if (is_array($this->values[$field_name]) && isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) { @@ -190,6 +207,23 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans } } } + + $this->getFieldDefinitions(); + + // Currently each stored value has its own internal language structure. + // Ensure the passed-in values meet the required structure and set them to + // the plain values array of the entity. The plain values array will be used + // whenever properties accessed. + // @todo Change the raw values array structure to hold field values per + // language instead languages per field. + foreach ($values as $field => $value) { + // If the value doesn't meet the required raw values structure and is a + // defined field, wrap it. + if ((!is_array($value) || !isset($value[Language::LANGCODE_DEFAULT])) && isset($this->fieldDefinitions[$field])) { + $value = array(Language::LANGCODE_DEFAULT => $value); + } + $this->values[$field] = $value; + } } /** diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index a5c3d0f..4486b8b 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -18,6 +18,7 @@ use Drupal\Core\Link; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Defines a base entity class. @@ -96,6 +97,13 @@ protected function uuidGenerator() { /** * {@inheritdoc} */ + public static function createInstance(ContainerInterface $container, array $values, $entity_type, $bundle = FALSE, $translations = array()) { + return new static($values, $entity_type, $bundle, $translations); + } + + /** + * {@inheritdoc} + */ public function id() { return isset($this->id) ? $this->id : NULL; } diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index 7b5ebc7..a381ce4 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -9,6 +9,7 @@ use Drupal\Core\Access\AccessibleInterface; use Drupal\Core\Cache\CacheableDependencyInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Defines a common interface for all entity objects. @@ -18,6 +19,25 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface { /** + * Creates an entity object based on its values. + * + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * The service container this object should use. + * @param array $values + * An array of values to set, keyed by property name. + * @param string $entity_type + * The type of the entity to create. + * @param string|FALSE $bundle + * The bundle of the entity. + * @param array $translations + * The language codes of the available translations of the entity. + * + * @return \Drupal\Core\Entity\EntityInterface + * An empty new entity object. + */ + public static function createInstance(ContainerInterface $container, array $values, $entity_type, $bundle = FALSE, $translations = array()); + + /** * Returns the entity UUID (Universally Unique Identifier). * * The UUID is guaranteed to be unique and can be used to identify an entity diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index ae6009d..4c600cd 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -260,6 +260,68 @@ public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) { } /** + * Instantiates an entity object based on its values. + * + * If you want to create a new entity, use + * \Drupal\Core\Entity\EntityManager::create() instead. + * + * @see \Drupal\Core\Entity\EntityManager::create() + * + * @param string $entity_type + * The type of the entity. + * @param array $values + * The values to create an entity instance with. + * @param string|false $bundle + * The bundle of the entity. + * @param array $translations + * The language codes of the available translations of the entity. + * + * @throws \InvalidArgumentException + * If mandatory arguments are missing. + * + * @return \Drupal\Core\Entity\EntityInterface + * An empty new entity object. + */ + public function createInstance($entity_type, array $values = array(), $bundle = FALSE, array $translations = array()) { + $definition = $this->getDefinition($entity_type); + if (!$definition) { + throw new \InvalidArgumentException(sprintf('The %s entity type does not exist.', $entity_type)); + } + // Check if this entity type has bundles and if so if a bundle is defined. + $bundle_key = $definition->getKey('bundle'); + if ($bundle_key) { + if (empty($bundle)) { + throw new \InvalidArgumentException(format_string('Missing bundle for entity type @type', array('@type' => $entity_type))); + } + // Make sure the bundle is part of the values too. + $values[$bundle_key] = $bundle; + } + $class = $definition->getClass(); + return $class::createInstance(\Drupal::getContainer(), $values, $entity_type, $bundle, $translations); + } + + /** + * Constructs an entity object for creating a new entity. + * + * Note that for the permanent creation of the new entity, the returned entity + * object needs to be saved first. + * + * @see \Drupal\Core\Entity\EntityManager::createInstance() + * + * @param string $entity_type + * The type of the entity. + * @param array $values + * An array of values to set, keyed by property name. If the entity type has + * bundles the bundle key has to be specified. + * + * @return \Drupal\Core\Entity\EntityInterface + * A new entity object with default values applied. + */ + public function createEntity($entity_type, array $values) { + return $this->getStorage($entity_type)->create($values); + } + + /** * {@inheritdoc} */ public function hasHandler($entity_type, $handler_type) { diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index 14fdb0b..571c81f 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Language\LanguageInterface; use Drupal\rest\LinkManager\LinkManagerInterface; use Symfony\Component\Serializer\Exception\UnexpectedValueException; @@ -135,23 +136,8 @@ public function denormalize($data, $class, $format = NULL, array $context = arra $typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context); $entity_type = $this->entityManager->getDefinition($typed_data_ids['entity_type']); $langcode_key = $entity_type->getKey('langcode'); - $values = array(); - - // Figure out the language to use. - if (isset($data[$langcode_key])) { - $values[$langcode_key] = $data[$langcode_key][0]['value']; - // Remove the langcode so it does not get iterated over below. - unset($data[$langcode_key]); - } - - if ($entity_type->hasKey('bundle')) { - $bundle_key = $entity_type->getKey('bundle'); - $values[$bundle_key] = $typed_data_ids['bundle']; - // Unset the bundle key from data, if it's there. - unset($data[$bundle_key]); - } - - $entity = $this->entityManager->getStorage($typed_data_ids['entity_type'])->create($values); + $langcode = isset($data[$langcode_key]) ? $data[$langcode_key] : LanguageInterface::LANGCODE_NOT_SPECIFIED; + $entity = $this->entityManager->createInstance($typed_data_ids['entity_type'], array('langcode' => $langcode), $typed_data_ids['bundle']); // Remove links from data array. unset($data['_links']); diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php index f993024..548e698 100644 --- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php @@ -69,7 +69,7 @@ public function denormalize($data, $class, $format = NULL, array $context = arra $path = 'temporary://' . drupal_basename($data['uri'][0]['value']); $data['uri'] = file_unmanaged_save_data($file_data, $path); - return $this->entityManager->getStorage('file')->create($data); + return \Drupal::entityManager()->createInstance('file', $data); } } diff --git a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php index 0e62fce..2225e9c 100644 --- a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php +++ b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php @@ -486,7 +486,7 @@ public function testUserHooks() { 'mail' => 'test@example.com', 'created' => REQUEST_TIME, 'status' => 1, - 'language' => 'en', + 'langcode' => 'en', )); $this->assertHookMessageOrder(array( diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 75bbbdf..c2bbc47 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -26,6 +26,7 @@ use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Stores UI related temporary settings. @@ -169,6 +170,16 @@ public function __construct(ViewEntityInterface $storage) { } /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, array $values, $entity_type, $bundle = FALSE, $translations = array()) { + // This class shouldn't be used like an entity and thus shouldn't be + // instantiated using this helper function. Therefore we break with the + // interface and just throw an error. + throw new \Exception('ViewUI cannot be handled as entity. Use the normal constructor to create an instance instead.'); + } + + /** * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::get(). */ public function get($property_name, $langcode = NULL) {