Update to Drupal 8.0-dev-2015-11-17. Commits through da81cd220, Tue Nov 17 15:53:49 2015 +0000, Issue #2617224 by Wim Leers: Move around/fix some documentation.
This commit is contained in:
parent
4afb23bbd3
commit
7784f4c23d
929 changed files with 19798 additions and 5304 deletions
|
@ -10,10 +10,10 @@ namespace Drupal\Core\Entity\Element;
|
|||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Textfield;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
|
||||
/**
|
||||
* Provides an entity autocomplete form element.
|
||||
|
@ -147,7 +147,7 @@ class EntityAutocomplete extends Textfield {
|
|||
'handler_settings' => $element['#selection_settings'],
|
||||
);
|
||||
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
|
||||
$autocreate = (bool) $element['#autocreate'];
|
||||
$autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface;
|
||||
|
||||
$input_values = $element['#tags'] ? Tags::explode($element['#value']) : array($element['#value']);
|
||||
foreach ($input_values as $input) {
|
||||
|
@ -167,13 +167,14 @@ class EntityAutocomplete extends Textfield {
|
|||
// Auto-create item. See an example of how this is handled in
|
||||
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
|
||||
$value[] = array(
|
||||
'entity' => static::createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid'])
|
||||
'entity' => $handler->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the referenced entities are valid, if needed.
|
||||
if ($element['#validate_reference'] && !$autocreate && !empty($value)) {
|
||||
if ($element['#validate_reference'] && !empty($value)) {
|
||||
// Validate existing entities.
|
||||
$ids = array_reduce($value, function ($return, $item) {
|
||||
if (isset($item['target_id'])) {
|
||||
$return[] = $item['target_id'];
|
||||
|
@ -189,6 +190,30 @@ class EntityAutocomplete extends Textfield {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate newly created entities.
|
||||
$new_entities = array_reduce($value, function ($return, $item) {
|
||||
if (isset($item['entity'])) {
|
||||
$return[] = $item['entity'];
|
||||
}
|
||||
return $return;
|
||||
});
|
||||
|
||||
if ($new_entities) {
|
||||
if ($autocreate) {
|
||||
$valid_new_entities = $handler->validateReferenceableNewEntities($new_entities);
|
||||
$invalid_new_entities = array_diff_key($new_entities, $valid_new_entities);
|
||||
}
|
||||
else {
|
||||
// If the selection handler does not support referencing newly
|
||||
// created entities, all of them should be invalidated.
|
||||
$invalid_new_entities = $new_entities;
|
||||
}
|
||||
|
||||
foreach ($invalid_new_entities as $entity) {
|
||||
$form_state->setError($element, t('This entity (%type: %label) cannot be referenced.', array('%type' => $element['#target_type'], '%label' => $entity->label())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use only the last value if the form element does not support multiple
|
||||
|
@ -310,37 +335,4 @@ class EntityAutocomplete extends Textfield {
|
|||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity from a label entered in the autocomplete input.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID.
|
||||
* @param string $bundle
|
||||
* The bundle name.
|
||||
* @param string $label
|
||||
* The entity label.
|
||||
* @param int $uid
|
||||
* The entity owner ID.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected static function createNewEntity($entity_type_id, $bundle, $label, $uid) {
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
|
||||
$entity_type = $entity_manager->getDefinition($entity_type_id);
|
||||
$bundle_key = $entity_type->getKey('bundle');
|
||||
$label_key = $entity_type->getKey('label');
|
||||
|
||||
$entity = $entity_manager->getStorage($entity_type_id)->create(array(
|
||||
$bundle_key => $bundle,
|
||||
$label_key => $label,
|
||||
));
|
||||
|
||||
if ($entity instanceof EntityOwnerInterface) {
|
||||
$entity->setOwnerId($uid);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,11 +72,26 @@ abstract class Entity implements EntityInterface {
|
|||
* Gets the entity manager.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityManagerInterface
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0.
|
||||
* Use \Drupal::entityTypeManager() instead in most cases. If the needed
|
||||
* method is not on \Drupal\Core\Entity\EntityTypeManagerInterface, see the
|
||||
* deprecated \Drupal\Core\Entity\EntityManager to find the
|
||||
* correct interface or service.
|
||||
*/
|
||||
protected function entityManager() {
|
||||
return \Drupal::entityManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity type manager.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected function entityTypeManager() {
|
||||
return \Drupal::entityTypeManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the language manager.
|
||||
*
|
||||
|
@ -158,6 +173,13 @@ abstract class Entity implements EntityInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function urlInfo($rel = 'canonical', array $options = []) {
|
||||
return $this->toUrl($rel, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toUrl($rel = 'canonical', array $options = []) {
|
||||
if ($this->id() === NULL) {
|
||||
throw new EntityMalformedException(sprintf('The "%s" entity cannot have a URI as it does not have an ID', $this->getEntityTypeId()));
|
||||
}
|
||||
|
@ -237,26 +259,33 @@ abstract class Entity implements EntityInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function link($text = NULL, $rel = 'canonical', array $options = []) {
|
||||
if (is_null($text)) {
|
||||
return $this->toLink($text, $rel, $options)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toLink($text = NULL, $rel = 'canonical', array $options = []) {
|
||||
if (!isset($text)) {
|
||||
$text = $this->label();
|
||||
}
|
||||
$url = $this->urlInfo($rel);
|
||||
$url = $this->toUrl($rel);
|
||||
$options += $url->getOptions();
|
||||
$url->setOptions($options);
|
||||
return (new Link($text, $url))->toString();
|
||||
return new Link($text, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function url($rel = 'canonical', $options = array()) {
|
||||
// While self::urlInfo() will throw an exception if the entity is new,
|
||||
// While self::toUrl() will throw an exception if the entity has no id,
|
||||
// the expected result for a URL is always a string.
|
||||
if ($this->isNew() || !$this->hasLinkTemplate($rel)) {
|
||||
if ($this->id() === NULL || !$this->hasLinkTemplate($rel)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$uri = $this->urlInfo($rel);
|
||||
$uri = $this->toUrl($rel);
|
||||
$options += $uri->getOptions();
|
||||
$uri->setOptions($options);
|
||||
return $uri->toString();
|
||||
|
|
|
@ -101,7 +101,29 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
|
|||
public function label();
|
||||
|
||||
/**
|
||||
* Gets the URI elements of the entity.
|
||||
* Gets the URL object for the entity.
|
||||
*
|
||||
* @param string $rel
|
||||
* The link relationship type, for example: canonical or edit-form.
|
||||
* @param array $options
|
||||
* See \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for
|
||||
* the available options.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL object.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
|
||||
* Use toUrl() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::toUrl
|
||||
*/
|
||||
public function urlInfo($rel = 'canonical', array $options = array());
|
||||
|
||||
/**
|
||||
* Gets the URL object for the entity.
|
||||
*
|
||||
* The entity must have an id already. Content entities usually get their IDs
|
||||
* by saving them.
|
||||
*
|
||||
* URI templates might be set in the links array in an annotation, for
|
||||
* example:
|
||||
|
@ -128,8 +150,12 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
|
|||
* the available options.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL object.
|
||||
*
|
||||
* @throws \Drupal\Core\Entity\EntityMalformedException
|
||||
* @throws \Drupal\Core\Entity\Exception\UndefinedLinkTemplateException
|
||||
*/
|
||||
public function urlInfo($rel = 'canonical', array $options = array());
|
||||
public function toUrl($rel = 'canonical', array $options = array());
|
||||
|
||||
/**
|
||||
* Gets the public URL for this entity.
|
||||
|
@ -142,9 +168,36 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
|
|||
*
|
||||
* @return string
|
||||
* The URL for this entity.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
|
||||
* Please use toUrl() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::toUrl
|
||||
*/
|
||||
public function url($rel = 'canonical', $options = array());
|
||||
|
||||
/**
|
||||
* Deprecated way of generating a link to the entity. See toLink().
|
||||
*
|
||||
* @param string|null $text
|
||||
* (optional) The link text for the anchor tag as a translated string.
|
||||
* If NULL, it will use the entity's label. Defaults to NULL.
|
||||
* @param string $rel
|
||||
* (optional) The link relationship type. Defaults to 'canonical'.
|
||||
* @param array $options
|
||||
* See \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for
|
||||
* the available options.
|
||||
*
|
||||
* @return string
|
||||
* An HTML string containing a link to the entity.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
|
||||
* Please use toLink() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::toLink
|
||||
*/
|
||||
public function link($text = NULL, $rel = 'canonical', array $options = []);
|
||||
|
||||
/**
|
||||
* Generates the HTML for a link to this entity.
|
||||
*
|
||||
|
@ -157,10 +210,13 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
|
|||
* See \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for
|
||||
* the available options.
|
||||
*
|
||||
* @return string
|
||||
* An HTML string containing a link to the entity.
|
||||
* @return \Drupal\Core\Link
|
||||
* A Link to the entity.
|
||||
*
|
||||
* @throws \Drupal\Core\Entity\EntityMalformedException
|
||||
* @throws \Drupal\Core\Entity\Exception\UndefinedLinkTemplateException
|
||||
*/
|
||||
public function link($text = NULL, $rel = 'canonical', array $options = []);
|
||||
public function toLink($text = NULL, $rel = 'canonical', array $options = []);
|
||||
|
||||
/**
|
||||
* Indicates if a link template exists for a given key.
|
||||
|
|
|
@ -30,7 +30,7 @@ interface SelectionInterface extends PluginFormInterface {
|
|||
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0);
|
||||
|
||||
/**
|
||||
* Counts entities that are referenceable by a given field.
|
||||
* Counts entities that are referenceable.
|
||||
*
|
||||
* @return int
|
||||
* The number of referenceable entities.
|
||||
|
@ -38,7 +38,7 @@ interface SelectionInterface extends PluginFormInterface {
|
|||
public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS');
|
||||
|
||||
/**
|
||||
* Validates that entities can be referenced by this field.
|
||||
* Validates which existing entities can be referenced.
|
||||
*
|
||||
* @return array
|
||||
* An array of valid entity IDs.
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\EntityReferenceSelection;
|
||||
|
||||
/**
|
||||
* Interface for Selection plugins that support newly created entities.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
|
||||
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface SelectionWithAutocreateInterface {
|
||||
|
||||
/**
|
||||
* Creates a new entity object that can be used as a valid reference.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID.
|
||||
* @param string $bundle
|
||||
* The bundle name.
|
||||
* @param string $label
|
||||
* The entity label.
|
||||
* @param int $uid
|
||||
* The entity owner ID, if the entity type supports it.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* An unsaved entity object.
|
||||
*/
|
||||
public function createNewEntity($entity_type_id, $bundle, $label, $uid);
|
||||
|
||||
/**
|
||||
* Validates which newly created entities can be referenced.
|
||||
*
|
||||
* This method should replicate the logic implemented by
|
||||
* \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface::validateReferenceableEntities(),
|
||||
* but applied to newly created entities that have not been saved yet.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface[] $entities
|
||||
* An array of entities to check.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface[]
|
||||
* The incoming $entities parameter, filtered for valid entities. Array keys
|
||||
* are preserved.
|
||||
*/
|
||||
public function validateReferenceableNewEntities(array $entities);
|
||||
|
||||
}
|
|
@ -97,18 +97,18 @@ class EntityTypeBundleInfo implements EntityTypeBundleInfoInterface {
|
|||
}
|
||||
else {
|
||||
$this->bundleInfo = $this->moduleHandler->invokeAll('entity_bundle_info');
|
||||
// First look for entity types that act as bundles for others, load them
|
||||
// and add them as bundles.
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $type => $entity_type) {
|
||||
if ($entity_type->getBundleOf()) {
|
||||
foreach ($this->entityTypeManager->getStorage($type)->loadMultiple() as $entity) {
|
||||
$this->bundleInfo[$entity_type->getBundleOf()][$entity->id()]['label'] = $entity->label();
|
||||
// First look for entity types that act as bundles for others, load them
|
||||
// and add them as bundles.
|
||||
if ($bundle_entity_type = $entity_type->getBundleEntityType()) {
|
||||
foreach ($this->entityTypeManager->getStorage($bundle_entity_type)->loadMultiple() as $entity) {
|
||||
$this->bundleInfo[$type][$entity->id()]['label'] = $entity->label();
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $type => $entity_type) {
|
||||
// If no bundles are provided, use the entity type name and label.
|
||||
if (!isset($this->bundleInfo[$type])) {
|
||||
// If entity type bundles are not supported and
|
||||
// hook_entity_bundle_info() has not already set up bundle
|
||||
// information, use the entity type name and label.
|
||||
elseif (!isset($this->bundleInfo[$type])) {
|
||||
$this->bundleInfo[$type][$type]['label'] = $entity_type->getLabel();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Component\Utility\Html;
|
|||
use Drupal\Core\Database\Query\AlterableInterface;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
@ -18,6 +19,7 @@ use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
|
|||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +42,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* deriver = "Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver"
|
||||
* )
|
||||
*/
|
||||
class DefaultSelection extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface {
|
||||
class DefaultSelection extends PluginBase implements SelectionInterface, SelectionWithAutocreateInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
|
@ -288,6 +290,38 @@ class DefaultSelection extends PluginBase implements SelectionInterface, Contain
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$bundle_key = $entity_type->getKey('bundle');
|
||||
$label_key = $entity_type->getKey('label');
|
||||
|
||||
$entity = $this->entityManager->getStorage($entity_type_id)->create(array(
|
||||
$bundle_key => $bundle,
|
||||
$label_key => $label,
|
||||
));
|
||||
|
||||
if ($entity instanceof EntityOwnerInterface) {
|
||||
$entity->setOwnerId($uid);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateReferenceableNewEntities(array $entities) {
|
||||
return array_filter($entities, function ($entity) {
|
||||
if (isset($this->configuration['handler_settings']['target_bundles'])) {
|
||||
return in_array($entity->bundle(), $this->configuration['handler_settings']['target_bundles']);
|
||||
}
|
||||
return TRUE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an EntityQuery to get referenceable entities.
|
||||
*
|
||||
|
|
|
@ -26,10 +26,24 @@ class ValidReferenceConstraint extends Constraint {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public $message = 'The referenced entity (%type: %id) does not exist.';
|
||||
public $message = 'This entity (%type: %id) cannot be referenced.';
|
||||
|
||||
/**
|
||||
* Validation message when the target_id is empty.
|
||||
* Violation message when the entity does not exist.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $nonExistingMessage = 'The referenced entity (%type: %id) does not exist.';
|
||||
|
||||
/**
|
||||
* Violation message when a new entity ("autocreate") is invalid.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $invalidAutocreateMessage = 'This entity (%type: %label) cannot be referenced.';
|
||||
|
||||
/**
|
||||
* Violation message when the target_id is empty.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -7,39 +7,142 @@
|
|||
|
||||
namespace Drupal\Core\Entity\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Checks if referenced entities are valid.
|
||||
*/
|
||||
class ValidReferenceConstraintValidator extends ConstraintValidator {
|
||||
class ValidReferenceConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The selection plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
|
||||
*/
|
||||
protected $selectionManager;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a ValidReferenceConstraintValidator object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager
|
||||
* The selection plugin manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(SelectionPluginManagerInterface $selection_manager, EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->selectionManager = $selection_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.entity_reference_selection'),
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
/** @var \Drupal\Core\Field\FieldItemInterface $value */
|
||||
/** @var \Drupal\Core\Field\FieldItemListInterface $value */
|
||||
/** @var ValidReferenceConstraint $constraint */
|
||||
if (!isset($value)) {
|
||||
return;
|
||||
}
|
||||
// We don't use a regular NotNull constraint for the target_id property as
|
||||
// a NULL value is valid if the entity property contains an unsaved entity.
|
||||
// @see \Drupal\Core\TypedData\DataReferenceTargetDefinition::getConstraints
|
||||
if (!$value->isEmpty() && $value->target_id === NULL && !$value->entity->isNew()) {
|
||||
$this->context->addViolation($constraint->nullMessage);
|
||||
|
||||
// Collect new entities and IDs of existing entities across the field items.
|
||||
$new_entities = [];
|
||||
$target_ids = [];
|
||||
foreach ($value as $delta => $item) {
|
||||
$target_id = $item->target_id;
|
||||
// We don't use a regular NotNull constraint for the target_id property as
|
||||
// NULL is allowed if the entity property contains an unsaved entity.
|
||||
// @see \Drupal\Core\TypedData\DataReferenceTargetDefinition::getConstraints()
|
||||
if (!$item->isEmpty() && $target_id === NULL) {
|
||||
if (!$item->entity->isNew()) {
|
||||
$this->context->buildViolation($constraint->nullMessage)
|
||||
->atPath((string) $delta)
|
||||
->addViolation();
|
||||
return;
|
||||
}
|
||||
$new_entities[$delta] = $item->entity;
|
||||
}
|
||||
|
||||
// '0' or NULL are considered valid empty references.
|
||||
if (!empty($target_id)) {
|
||||
$target_ids[$delta] = $target_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Early opt-out if nothing to validate.
|
||||
if (!$new_entities && !$target_ids) {
|
||||
return;
|
||||
}
|
||||
$id = $value->get('target_id')->getValue();
|
||||
// '0' or NULL are considered valid empty references.
|
||||
if (empty($id)) {
|
||||
return;
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler * */
|
||||
$handler = $this->selectionManager->getSelectionHandler($value->getFieldDefinition());
|
||||
$target_type_id = $value->getFieldDefinition()->getSetting('target_type');
|
||||
|
||||
// Add violations on deltas with a new entity that is not valid.
|
||||
if ($new_entities) {
|
||||
if ($handler instanceof SelectionWithAutocreateInterface) {
|
||||
$valid_new_entities = $handler->validateReferenceableNewEntities($new_entities);
|
||||
$invalid_new_entities = array_diff_key($new_entities, $valid_new_entities);
|
||||
}
|
||||
else {
|
||||
// If the selection handler does not support referencing newly created
|
||||
// entities, all of them should be invalidated.
|
||||
$invalid_new_entities = $new_entities;
|
||||
}
|
||||
|
||||
foreach ($invalid_new_entities as $delta => $entity) {
|
||||
$this->context->buildViolation($constraint->invalidAutocreateMessage)
|
||||
->setParameter('%type', $target_type_id)
|
||||
->setParameter('%label', $entity->label())
|
||||
->atPath((string) $delta . '.entity')
|
||||
->setInvalidValue($entity)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
$referenced_entity = $value->get('entity')->getValue();
|
||||
if (!$referenced_entity) {
|
||||
$type = $value->getFieldDefinition()->getSetting('target_type');
|
||||
$this->context->addViolation($constraint->message, array('%type' => $type, '%id' => $id));
|
||||
|
||||
// Add violations on deltas with a target_id that is not valid.
|
||||
if ($target_ids) {
|
||||
$valid_target_ids = $handler->validateReferenceableEntities($target_ids);
|
||||
if ($invalid_target_ids = array_diff($target_ids, $valid_target_ids)) {
|
||||
// For accuracy of the error message, differentiate non-referenceable
|
||||
// and non-existent entities.
|
||||
$target_type = $this->entityTypeManager->getDefinition($target_type_id);
|
||||
$existing_ids = $this->entityTypeManager->getStorage($target_type_id)->getQuery()
|
||||
->condition($target_type->getKey('id'), $invalid_target_ids, 'IN')
|
||||
->execute();
|
||||
foreach ($invalid_target_ids as $delta => $target_id) {
|
||||
$message = in_array($target_id, $existing_ids) ? $constraint->message : $constraint->nonExistingMessage;
|
||||
$this->context->buildViolation($message)
|
||||
->setParameter('%type', $target_type_id)
|
||||
->setParameter('%id', $target_id)
|
||||
->atPath((string) $delta . '.target_id')
|
||||
->setInvalidValue($target_id)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
namespace Drupal\Core\Entity\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
|
@ -24,7 +28,33 @@ use Symfony\Component\Routing\RouteCollection;
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
|
||||
class DefaultHtmlRouteProvider implements EntityRouteProviderInterface, EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new DefaultHtmlRouteProvider.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -71,6 +101,12 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
|
|||
->setOption('parameters', [
|
||||
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
|
||||
]);
|
||||
|
||||
// Entity types with serial IDs can specify this in their route
|
||||
// requirements, improving the matching process.
|
||||
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
|
||||
$route->setRequirement($entity_type_id, '\d+');
|
||||
}
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +138,12 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
|
|||
->setOption('parameters', [
|
||||
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
|
||||
]);
|
||||
|
||||
// Entity types with serial IDs can specify this in their route
|
||||
// requirements, improving the matching process.
|
||||
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
|
||||
$route->setRequirement($entity_type_id, '\d+');
|
||||
}
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
@ -128,8 +170,33 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
|
|||
->setOption('parameters', [
|
||||
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
|
||||
]);
|
||||
|
||||
// Entity types with serial IDs can specify this in their route
|
||||
// requirements, improving the matching process.
|
||||
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
|
||||
$route->setRequirement($entity_type_id, '\d+');
|
||||
}
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the ID key for a given entity type.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* An entity type.
|
||||
*
|
||||
* @return string|null
|
||||
* The type of the ID key for a given entity type, or NULL if the entity
|
||||
* type does not support fields.
|
||||
*/
|
||||
protected function getEntityTypeIdKeyType(EntityTypeInterface $entity_type) {
|
||||
if (!$entity_type->isSubclassOf(FieldableEntityInterface::class)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type->id());
|
||||
return $field_storage_definitions[$entity_type->getKey('id')]->getType();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1916,16 +1916,16 @@ function hook_entity_extra_field_info() {
|
|||
// Visibility of the ordering of the language selector is the same as on the
|
||||
// node/add form.
|
||||
if ($module_language_enabled) {
|
||||
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->type);
|
||||
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id());
|
||||
if ($configuration->isLanguageAlterable()) {
|
||||
$extra['node'][$bundle->type]['form']['language'] = array(
|
||||
$extra['node'][$bundle->id()]['form']['language'] = array(
|
||||
'label' => t('Language'),
|
||||
'description' => $description,
|
||||
'weight' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
$extra['node'][$bundle->type]['display']['language'] = array(
|
||||
$extra['node'][$bundle->id()]['display']['language'] = array(
|
||||
'label' => t('Language'),
|
||||
'description' => $description,
|
||||
'weight' => 0,
|
||||
|
@ -1948,8 +1948,8 @@ function hook_entity_extra_field_info() {
|
|||
function hook_entity_extra_field_info_alter(&$info) {
|
||||
// Force node title to always be at the top of the list by default.
|
||||
foreach (NodeType::loadMultiple() as $bundle) {
|
||||
if (isset($info['node'][$bundle->type]['form']['title'])) {
|
||||
$info['node'][$bundle->type]['form']['title']['weight'] = -20;
|
||||
if (isset($info['node'][$bundle->id()]['form']['title'])) {
|
||||
$info['node'][$bundle->id()]['form']['title']['weight'] = -20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue