Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Annotation\Constraint.
*/
namespace Drupal\Core\Validation\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a validation constraint annotation object.
*
* Plugin Namespace: Plugin\Validation\Constraint
*
* For a working example, see
* \Drupal\Core\Validation\Plugin\Validation\Constraint\LengthConstraint
*
* @see \Drupal\Core\Validation\ConstraintManager
* @see \Symfony\Component\Validator\Constraint
* @see hook_validation_constraint_alter()
* @see plugin_api
*
* @Annotation
*/
class Constraint extends Plugin {
/**
* The constraint plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the constraint plugin.
*
* @ingroup plugin_translatable
*
* @var string|\Drupal\Core\Annotation\Translation
*/
public $label;
/**
* An array of DataType plugin IDs for which this constraint applies. Valid
* values are any types registered by the typed data API, or an array of
* multiple type names. For supporting all types, FALSE may be specified. The
* key defaults to an empty array, which indicates no types are supported.
*
* @var string|string[]|false
*
* @see \Drupal\Core\TypedData\Annotation\DataType
*/
public $type = [];
}

View file

@ -0,0 +1,144 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\ConstraintManager.
*/
namespace Drupal\Core\Validation;
use Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\StringTranslation\TranslationWrapper;
/**
* Constraint plugin manager.
*
* Manages validation constraints based upon
* \Symfony\Component\Validator\Constraint, whereas Symfony constraints are
* added in manually during construction. Constraint options are passed on as
* plugin configuration during plugin instantiation.
*
* While core does not prefix constraint plugins, modules have to prefix them
* with the module name in order to avoid any naming conflicts. E.g. a "profile"
* module would have to prefix any constraints with "Profile".
*
* Constraint plugins may specify data types to which support is limited via the
* 'type' key of plugin definitions. See
* \Drupal\Core\Validation\Annotation\Constraint for details.
*
* @see \Drupal\Core\Validation\Annotation\Constraint
*/
class ConstraintManager extends DefaultPluginManager {
/**
* Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct().
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Validation/Constraint', $namespaces, $module_handler, NULL, 'Drupal\Core\Validation\Annotation\Constraint');
$this->alterInfo('validation_constraint');
$this->setCacheBackend($cache_backend, 'validation_constraint_plugins');
}
/**
* {@inheritdoc}
*/
protected function getDiscovery() {
if (!isset($this->discovery)) {
$this->discovery = parent::getDiscovery();
$this->discovery = new StaticDiscoveryDecorator($this->discovery, [$this, 'registerDefinitions']);
}
return $this->discovery;
}
/**
* Creates a validation constraint.
*
* @param string $name
* The name or plugin id of the constraint.
* @param mixed $options
* The options to pass to the constraint class. Required and supported
* options depend on the constraint class.
*
* @return \Symfony\Component\Validator\Constraint
* A validation constraint plugin.
*/
public function create($name, $options) {
if (!is_array($options)) {
// Plugins need an array as configuration, so make sure we have one.
// The constraint classes support passing the options as part of the
// 'value' key also.
$options = isset($options) ? array('value' => $options) : array();
}
return $this->createInstance($name, $options);
}
/**
* Callback for registering definitions for constraints shipped with Symfony.
*
* @see ConstraintManager::__construct()
*/
public function registerDefinitions() {
$this->getDiscovery()->setDefinition('Callback', array(
'label' => new TranslationWrapper('Callback'),
'class' => '\Symfony\Component\Validator\Constraints\Callback',
'type' => FALSE,
));
$this->getDiscovery()->setDefinition('Blank', array(
'label' => new TranslationWrapper('Blank'),
'class' => '\Symfony\Component\Validator\Constraints\Blank',
'type' => FALSE,
));
$this->getDiscovery()->setDefinition('NotBlank', array(
'label' => new TranslationWrapper('Not blank'),
'class' => '\Symfony\Component\Validator\Constraints\NotBlank',
'type' => FALSE,
));
$this->getDiscovery()->setDefinition('Email', array(
'label' => new TranslationWrapper('Email'),
'class' => '\Drupal\Core\Validation\Plugin\Validation\Constraint\EmailConstraint',
'type' => array('string'),
));
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
// Make sure 'type' is set and either an array or FALSE.
if ($definition['type'] !== FALSE && !is_array($definition['type'])) {
$definition['type'] = array($definition['type']);
}
}
/**
* Returns a list of constraints that support the given type.
*
* @param string $type
* The type to filter on.
*
* @return array
* An array of constraint plugin definitions supporting the given type,
* keyed by constraint name (plugin ID).
*/
public function getDefinitionsByType($type) {
$definitions = array();
foreach ($this->getDefinitions() as $plugin_id => $definition) {
if ($definition['type'] === FALSE || in_array($type, $definition['type'])) {
$definitions[$plugin_id] = $definition;
}
}
return $definitions;
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\ConstraintValidatorFactory.
*/
namespace Drupal\Core\Validation;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorFactory as BaseConstraintValidatorFactory;
/**
* Defines a constraint validator factory that works with container injection.
*/
class ConstraintValidatorFactory extends BaseConstraintValidatorFactory {
/**
* Constructs a new ConstraintValidatorFactory.
*
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
*/
public function __construct(ClassResolverInterface $class_resolver) {
$this->classResolver = $class_resolver;
}
/**
* {@inheritdoc}
*/
public function getInstance(Constraint $constraint) {
$class_name = $constraint->validatedBy();
if (!isset($this->validators[$class_name])) {
$this->validators[$class_name] = $this->classResolver->getInstanceFromDefinition($class_name);
}
return $this->validators[$class_name];
}
}

View file

@ -0,0 +1,92 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\DrupalTranslator.
*/
namespace Drupal\Core\Validation;
use Symfony\Component\Translation\TranslatorInterface;
/**
* Translates strings using Drupal's translation system.
*
* This class is used by the Symfony validator to translate violation messages.
*/
class DrupalTranslator implements TranslatorInterface {
/**
* The locale used for translating.
*
* @var string
*/
protected $locale;
/**
* Implements \Symfony\Component\Translation\TranslatorInterface::trans().
*/
public function trans($id, array $parameters = array(), $domain = NULL, $locale = NULL) {
return t($id, $this->processParameters($parameters), $this->getOptions($domain, $locale));
}
/**
* Implements \Symfony\Component\Translation\TranslatorInterface::transChoice().
*/
public function transChoice($id, $number, array $parameters = array(), $domain = NULL, $locale = NULL) {
// Violation messages can separated singular and plural versions by "|".
$ids = explode('|', $id);
if (!isset($ids[1])) {
throw new \InvalidArgumentException(sprintf('The message "%s" cannot be pluralized, because it is missing a plural (e.g. "There is one apple|There are @count apples").', $id));
}
return \Drupal::translation()->formatPlural($number, $ids[0], $ids[1], $this->processParameters($parameters), $this->getOptions($domain, $locale));
}
/**
* Implements \Symfony\Component\Translation\TranslatorInterface::setLocale().
*/
public function setLocale($locale) {
$this->locale = $locale;
}
/**
* Implements \Symfony\Component\Translation\TranslatorInterface::getLocale().
*/
public function getLocale() {
return $this->locale ? $this->locale : \Drupal::languageManager()->getCurrentLanguage()->getId();
}
/**
* Processes the parameters array for use with t().
*/
protected function processParameters(array $parameters) {
$return = array();
foreach ($parameters as $key => $value) {
if (is_object($value)) {
// t() does not work will objects being passed as replacement strings.
}
// Check for symfony replacement patterns in the form "{{ name }}".
elseif (strpos($key, '{{ ') === 0 && strrpos($key, ' }}') == strlen($key) - 3) {
// Transform it into a Drupal pattern using the format %name.
$key = '%' . substr($key, 3, strlen($key) - 6);
$return[$key] = $value;
}
else {
$return[$key] = $value;
}
}
return $return;
}
/**
* Returns options suitable for use with t().
*/
protected function getOptions($domain = NULL, $locale = NULL) {
// We do not support domains, so we ignore this parameter.
// If locale is left NULL, t() will default to the interface language.
$locale = isset($locale) ? $locale : $this->locale;
return array('langcode' => $locale);
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\Choice;
/**
* Checks for the value being allowed.
*
* @Constraint(
* id = "AllowedValues",
* label = @Translation("Allowed values", context = "Validation")
* )
*
* @see \Drupal\Core\TypedData\OptionsProviderInterface
*/
class AllowedValuesConstraint extends Choice {
public $minMessage = 'You must select at least %limit choice.|You must select at least %limit choices.';
public $maxMessage = 'You must select at most %limit choice.|You must select at most %limit choices.';
}

View file

@ -0,0 +1,80 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraintValidator.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\OptionsProviderInterface;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\ChoiceValidator;
/**
* Validates the AllowedValues constraint.
*/
class AllowedValuesConstraintValidator extends ChoiceValidator implements ContainerInjectionInterface {
use TypedDataAwareValidatorTrait;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('current_user'));
}
/**
* Constructs a new AllowedValuesConstraintValidator.
*
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(AccountInterface $current_user) {
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
$typed_data = $this->getTypedData();
if ($typed_data instanceof OptionsProviderInterface) {
$allowed_values = $typed_data->getSettableValues($this->currentUser);
$constraint->choices = $allowed_values;
// If the data is complex, we have to validate its main property.
if ($typed_data instanceof ComplexDataInterface) {
$name = $typed_data->getDataDefinition()->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)) {
return;
}
parent::validate($value, $constraint);
}
}

View file

@ -0,0 +1,65 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Complex data constraint.
*
* Validates properties of complex data structures.
*
* @Constraint(
* id = "ComplexData",
* label = @Translation("Complex data", context = "Validation")
* )
*/
class ComplexDataConstraint extends Constraint {
/**
* An array of constraints for contained properties, keyed by property name.
*
* @var array
*/
public $properties;
/**
* {@inheritdoc}
*/
public function __construct($options = NULL) {
// Allow skipping the 'properties' key in the options.
if (is_array($options) && !array_key_exists('properties', $options)) {
$options = array('properties' => $options);
}
parent::__construct($options);
$constraint_manager = \Drupal::service('validation.constraint');
// Instantiate constraint objects for array definitions.
foreach ($this->properties as &$constraints) {
foreach ($constraints as $id => $options) {
if (!is_object($options)) {
$constraints[$id] = $constraint_manager->create($id, $options);
}
}
}
}
/**
* {@inheritdoc}
*/
public function getDefaultOption() {
return 'properties';
}
/**
* {@inheritdoc}
*/
public function getRequiredOptions() {
return array('properties');
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraintValidator.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates complex data.
*/
class ComplexDataConstraintValidator extends ConstraintValidator {
use TypedDataAwareValidatorTrait;
/**
* {@inheritdoc}
*/
public function validate($data, Constraint $constraint) {
// If un-wrapped data has been passed, fetch the typed data object first.
if (!$data instanceof TypedDataInterface) {
$data = $this->getTypedData();
}
if (!$data instanceof ComplexDataInterface) {
throw new UnexpectedTypeException($data, 'ComplexData');
}
foreach ($constraint->properties as $name => $constraints) {
$this->context->getValidator()
->inContext($this->context)
// Specifically pass along FALSE as $root_call, as we validate the data
// as part of the typed data tree.
->validate($data->get($name), $constraints, NULL, FALSE);
}
}
}

View file

@ -0,0 +1,35 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\CountConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\Count;
/**
* Count constraint.
*
* Overrides the symfony constraint to use Drupal-style replacement patterns.
*
* @Constraint(
* id = "Count",
* label = @Translation("Count", context = "Validation"),
* type = { "list" }
* )
*/
class CountConstraint extends Count {
public $minMessage = 'This collection should contain %limit element or more.|This collection should contain %limit elements or more.';
public $maxMessage = 'This collection should contain %limit element or less.|This collection should contain %limit elements or less.';
public $exactMessage = 'This collection should contain exactly %limit element.|This collection should contain exactly %limit elements.';
/**
* {@inheritdoc}
*/
public function validatedBy() {
return '\Symfony\Component\Validator\Constraints\CountValidator';
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\EmailConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\Email;
/**
* Count constraint.
*
* Overrides the symfony constraint to use the strict setting.
*
* @Constraint(
* id = "Email",
* label = @Translation("Email", context = "Validation")
* )
*/
class EmailConstraint extends Email {
public $strict = TRUE;
/**
* {@inheritdoc}
*/
public function validatedBy() {
return '\Symfony\Component\Validator\Constraints\EmailValidator';
}
}

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\IsNullConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\IsNull;
/**
* Null constraint.
*
* Overrides the symfony constraint to handle empty Typed Data structures.
*
* @Constraint(
* id = "Null",
* label = @Translation("Null", context = "Validation"),
* type = false
* )
*/
class IsNullConstraint extends IsNull { }

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\IsNullConstraintValidator.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\IsNullValidator;
/**
* Null constraint validator.
*
* Overrides the symfony validator to handle empty Typed Data structures.
*/
class IsNullConstraintValidator extends IsNullValidator {
use TypedDataAwareValidatorTrait;
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
$typed_data = $this->getTypedData();
if (($typed_data instanceof ListInterface || $typed_data instanceof ComplexDataInterface) && $typed_data->isEmpty()) {
$value = NULL;
}
parent::validate($value, $constraint);
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\LengthConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\Length;
/**
* Length constraint.
*
* Overrides the symfony constraint to use Drupal-style replacement patterns.
*
* @todo: Move this below the TypedData core component.
*
* @Constraint(
* id = "Length",
* label = @Translation("Length", context = "Validation"),
* type = { "string" }
* )
*/
class LengthConstraint extends Length {
public $maxMessage = 'This value is too long. It should have %limit character or less.|This value is too long. It should have %limit characters or less.';
public $minMessage = 'This value is too short. It should have %limit character or more.|This value is too short. It should have %limit characters or more.';
public $exactMessage = 'This value should have exactly %limit character.|This value should have exactly %limit characters.';
/**
* Overrides Range::validatedBy().
*/
public function validatedBy() {
return '\Symfony\Component\Validator\Constraints\LengthValidator';
}
}

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\NotNull;
/**
* NotNull constraint.
*
* Overrides the symfony constraint to handle empty Typed Data structures.
*
* @Constraint(
* id = "NotNull",
* label = @Translation("NotNull", context = "Validation"),
* type = false
* )
*/
class NotNullConstraint extends NotNull { }

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraintValidator.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\NotNullValidator;
/**
* NotNull constraint validator.
*
* Overrides the symfony validator to handle empty Typed Data structures.
*/
class NotNullConstraintValidator extends NotNullValidator {
use TypedDataAwareValidatorTrait;
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
$typed_data = $this->getTypedData();
if (($typed_data instanceof ListInterface || $typed_data instanceof ComplexDataInterface) && $typed_data->isEmpty()) {
$value = NULL;
}
parent::validate($value, $constraint);
}
}

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Supports validating all primitive types.
*
* @Constraint(
* id = "PrimitiveType",
* label = @Translation("Primitive type", context = "Validation")
* )
*/
class PrimitiveTypeConstraint extends Constraint {
public $message = 'This value should be of the correct primitive type.';
}

View file

@ -0,0 +1,84 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidator.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Core\TypedData\Type\BinaryInterface;
use Drupal\Core\TypedData\Type\BooleanInterface;
use Drupal\Core\TypedData\Type\DateTimeInterface;
use Drupal\Core\TypedData\Type\DurationInterface;
use Drupal\Core\TypedData\Type\FloatInterface;
use Drupal\Core\TypedData\Type\IntegerInterface;
use Drupal\Core\TypedData\Type\StringInterface;
use Drupal\Core\TypedData\Type\UriInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the PrimitiveType constraint.
*/
class PrimitiveTypeConstraintValidator extends ConstraintValidator {
use TypedDataAwareValidatorTrait;
/**
* Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
*/
public function validate($value, Constraint $constraint) {
if (!isset($value)) {
return;
}
$typed_data = $this->getTypedData();
$valid = TRUE;
if ($typed_data instanceof BinaryInterface && !is_resource($value)) {
$valid = FALSE;
}
if ($typed_data instanceof BooleanInterface && !(is_bool($value) || $value === 0 || $value === '0' || $value === 1 || $value == '1')) {
$valid = FALSE;
}
if ($typed_data instanceof FloatInterface && filter_var($value, FILTER_VALIDATE_FLOAT) === FALSE) {
$valid = FALSE;
}
if ($typed_data instanceof IntegerInterface && filter_var($value, FILTER_VALIDATE_INT) === FALSE) {
$valid = FALSE;
}
if ($typed_data instanceof StringInterface && !is_scalar($value)) {
$valid = FALSE;
}
// Ensure that URIs comply with http://tools.ietf.org/html/rfc3986, which
// requires:
// - That it is well formed (parse_url() returns FALSE if not).
// - That it contains a scheme (parse_url(, PHP_URL_SCHEME) returns NULL if
// not).
if ($typed_data instanceof UriInterface && in_array(parse_url($value, PHP_URL_SCHEME), [NULL, FALSE], TRUE)) {
$valid = FALSE;
}
// @todo: Move those to separate constraint validators.
try {
if ($typed_data instanceof DateTimeInterface && $typed_data->getDateTime() && $typed_data->getDateTime()->hasErrors()) {
$valid = FALSE;
}
if ($typed_data instanceof DurationInterface && $typed_data->getDuration() && !($typed_data->getDuration() instanceof \DateInterval)) {
$valid = FALSE;
}
}
catch (\Exception $e) {
// Invalid durations or dates might throw exceptions.
$valid = FALSE;
}
if (!$valid) {
// @todo: Provide a good violation message for each problem.
$this->context->addViolation($constraint->message, array(
'%value' => is_object($value) ? get_class($value) : (is_array($value) ? 'Array' : (string) $value)
));
}
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\RangeConstraint.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\Range;
/**
* Range constraint.
*
* Overrides the symfony constraint to use Drupal-style replacement patterns.
*
* @todo: Move this below the TypedData core component.
*
* @Constraint(
* id = "Range",
* label = @Translation("Range", context = "Validation"),
* type = { "integer", "float" }
* )
*/
class RangeConstraint extends Range {
public $minMessage = 'This value should be %limit or more.';
public $maxMessage = 'This value should be %limit or less.';
/**
* Overrides Range::validatedBy().
*/
public function validatedBy() {
return '\Symfony\Component\Validator\Constraints\RangeValidator';
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator.
*/
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates that a field is unique for the given entity type.
*/
class UniqueFieldValueValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($items, Constraint $constraint) {
if (!$item = $items->first()) {
return;
}
$field_name = $items->getFieldDefinition()->getName();
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $items->getEntity();
$entity_type_id = $entity->getEntityTypeId();
$id_key = $entity->getEntityType()->getKey('id');
$value_taken = (bool) \Drupal::entityQuery($entity_type_id)
// The id could be NULL, so we cast it to 0 in that case.
->condition($id_key, (int) $items->getEntity()->id(), '<>')
->condition($field_name, $item->value)
->range(0, 1)
->count()
->execute();
if ($value_taken) {
$this->context->addViolation($constraint->message, array("%value" => $item->value));
}
}
}