/** * Validates the form and its domain object. * * @param FormInterface $form A FormInterface instance */ public function validate(FormInterface $form) { if ($form->isRoot()) { $mapping = array(); $forms = array(); $this->buildFormPathMapping($form, $mapping); $this->buildDataPathMapping($form, $mapping); $this->buildNamePathMapping($form, $forms); $this->resolveMappingPlaceholders($mapping, $forms); // Validate the form in group "Default" // Validation of the data in the custom group is done by validateData(), // which is constrained by the Execute constraint if ($form->hasAttribute('validation_constraint')) { $violations = $this->validator->validateValue($form->getData(), $form->getAttribute('validation_constraint'), self::getFormValidationGroups($form)); if ($violations) { foreach ($violations as $violation) { $propertyPath = new PropertyPath($violation->getPropertyPath()); $template = $violation->getMessageTemplate(); $parameters = $violation->getMessageParameters(); $error = new FormError($template, $parameters); $child = $form; foreach ($propertyPath->getElements() as $element) { $children = $child->getChildren(); if (!isset($children[$element])) { if ($form->isSynchronized()) { $form->addError($error); } break; } $child = $children[$element]; } if ($child->isSynchronized()) { $child->addError($error); } } } } elseif (count($violations = $this->validator->validate($form))) { foreach ($violations as $violation) { $propertyPath = $violation->getPropertyPath(); $template = $violation->getMessageTemplate(); $parameters = $violation->getMessageParameters(); $error = new FormError($template, $parameters); foreach ($mapping as $mappedPath => $child) { if (preg_match($mappedPath, $propertyPath)) { if ($child->isSynchronized()) { $child->addError($error); } continue 2; } } if ($form->isSynchronized()) { $form->addError($error); } } } } }
public function transform($entity) { if (null === $entity) { return null; } if (!$this->unitOfWork->isInIdentityMap($entity)) { throw new FormException('Entities passed to the choice field must be managed'); } if ($this->property) { $propertyPath = new PropertyPath($this->property); return $propertyPath->getValue($entity); } return current($this->unitOfWork->getEntityIdentifier($entity)); }
/** * Initializes the choices and returns them * * The choices are generated from the models. If the models have a * composite identifier, the choices are indexed using ascending integers. * Otherwise the identifiers are used as indices. * * If the models were passed in the "choices" option, this method * does not have any significant overhead. Otherwise, if a query object * was passed in the "query" option, this query is now used and executed. * In the last case, all models for the underlying class are fetched. * * If the option "property" was passed, the property path in that option * is used as option values. Otherwise this method tries to convert * objects to strings using __toString(). * * @return array An array of choices */ protected function load() { parent::load(); if ($this->choices) { $models = $this->choices; } else { $models = $this->query->find(); } $this->choices = array(); $this->models = array(); foreach ($models as $key => $model) { if ($this->propertyPath) { // If the property option was given, use it $value = $this->propertyPath->getValue($model); } else { // Otherwise expect a __toString() method in the model $value = (string) $model; } if (count($this->identifier) > 1) { // When the identifier consists of multiple field, use // naturally ordered keys to refer to the choices $this->choices[$key] = $value; $this->models[$key] = $model; } else { // When the identifier is a single field, index choices by // model ID for performance reasons $id = current($this->getIdentifierValues($model)); $this->choices[$id] = $value; $this->models[$id] = $model; } } }
/** * Converts entities into choices with support for groups. * * The choices are generated from the entities. If the entities have a * composite identifier, the choices are indexed using ascending integers. * Otherwise the identifiers are used as indices. * * If the option "property" was passed, the property path in that option * is used as option values. Otherwise this method tries to convert * objects to strings using __toString(). * * @param array $entities An array of entities * @param string $group A group name */ private function loadEntities($entities, $group = null) { foreach ($entities as $key => $entity) { if (is_array($entity)) { // Entities are in named groups $this->loadEntities($entity, $key); continue; } if ($this->propertyPath) { // If the property option was given, use it $value = $this->propertyPath->getValue($entity); } else { $value = (string) $entity; } if (count($this->identifier) > 1) { // When the identifier consists of multiple field, use // naturally ordered keys to refer to the choices $id = $key; } else { // When the identifier is a single field, index choices by // entity ID for performance reasons $id = current($this->getIdentifierValues($entity)); } if (null === $group) { // Flat list of choices $this->choices[$id] = $value; } else { // Nested choices $this->choices[$group][$id] = $value; } $this->entities[$id] = $entity; } }
public function validate($value, Constraint $constraint) { // Aplica la ruta de la propiedad si es especificada if ($constraint->propertyPath) { $propertyPath = new PropertyPath($constraint->propertyPath); $value = $propertyPath->getValue($value); } // Chequea que el valor no esta en el array foreach ($this->collectionValues as $aValue) { if (spl_object_hash($value) == spl_object_hash($aValue)) { $this->context->addViolation($constraint->message, array()); } } //if(in_array($value, $this->collectionValues)) // $this->context->addViolation($constraint->message, array()); // Agrega el valor en el array para los siguientes items de la validacion $this->collectionValues[] = $value; }
/** * Creates a new violation path from a string. * * @param string $violationPath The property path of a {@link ConstraintViolation} * object. */ public function __construct($violationPath) { $path = new PropertyPath($violationPath); $elements = $path->getElements(); $positions = $path->getPositions(); $data = false; for ($i = 0, $l = count($elements); $i < $l; ++$i) { if (!$data) { // The element "data" has not yet been passed if ('children' === $elements[$i] && $path->isProperty($i)) { // Skip element "children" ++$i; // Next element must exist and must be an index // Otherwise consider this the end of the path if ($i >= $l || !$path->isIndex($i)) { break; } $this->elements[] = $elements[$i]; $this->positions[] = $positions[$i]; $this->isIndex[] = true; $this->mapsForm[] = true; } elseif ('data' === $elements[$i] && $path->isProperty($i)) { // Skip element "data" ++$i; // End of path if ($i >= $l) { break; } $this->elements[] = $elements[$i]; $this->positions[] = $positions[$i]; $this->isIndex[] = $path->isIndex($i); $this->mapsForm[] = false; $data = true; } else { // Neither "children" nor "data" property found // Consider this the end of the path break; } } else { // Already after the "data" element // Pick everything as is $this->elements[] = $elements[$i]; $this->positions[] = $positions[$i]; $this->isIndex[] = $path->isIndex($i); $this->mapsForm[] = false; } } $this->length = count($this->elements); $this->pathAsString = $violationPath; $this->resizeString(); }
private function extractLabels($choices, array &$labels) { foreach ($choices as $i => $choice) { if (is_array($choice) || $choice instanceof \Traversable) { $labels[$i] = array(); $this->extractLabels($choice, $labels[$i]); } elseif ($this->labelPath) { $labels[$i] = $this->labelPath->getValue($choice); } elseif (method_exists($choice, '__toString')) { $labels[$i] = (string) $choice; } else { throw new StringCastException('A "__toString()" method was not found on the objects of type "' . get_class($choice) . '" passed to the choice field. To read a custom getter instead, set the argument $labelPath to the desired property path.'); } } }
/** * Renders a pagerfanta. * * @param PagerfantaInterface $pagerfanta The pagerfanta. * @param string $viewName The view name. * @param array $options An array of options (optional). * * @return string The pagerfanta rendered. */ public function renderPagerfanta(PagerfantaInterface $pagerfanta, $viewName = 'default', array $options = array()) { $options = array_replace(array('routeName' => null, 'routeParams' => array(), 'pageParameter' => 'page'), $options); $router = $this->container->get('router'); $request = $this->container->get('request'); if (null === $options['routeName']) { $options['routeName'] = $request->attributes->get('_route'); $options['routeParams'] = $request->query->all(); if ($options['routeName'] === '_internal') { throw new \Exception('PagerfantaBundle can not guess the route when used in a subrequest'); } foreach ($router->getRouteCollection()->get($options['routeName'])->compile()->getVariables() as $variable) { $options['routeParams'][$variable] = $request->attributes->get($variable); } } $routeName = $options['routeName']; $routeParams = $options['routeParams']; $pagePropertyPath = new PropertyPath($options['pageParameter']); $routeGenerator = function ($page) use($router, $routeName, $routeParams, $pagePropertyPath) { $pagePropertyPath->setValue($routeParams, $page); return $router->generate($routeName, $routeParams); }; return $this->container->get('white_october_pagerfanta.view_factory')->get($viewName)->render($pagerfanta, $routeGenerator, $options); }
/** * @param \Symfony\Component\HttpFoundation\Request $request * @return \Symfony\Component\HttpFoundation\Response */ public function setObjectFieldValueAction(Request $request) { $field = $request->get('field'); $code = $request->get('code'); $objectId = $request->get('objectId'); $value = $request->get('value'); $context = $request->get('context'); $admin = $this->pool->getInstance($code); // alter should be done by using a post method if ($request->getMethod() != 'POST') { return new Response(json_encode(array('status' => 'KO', 'message' => 'Expected a POST Request')), 200, array('Content-Type' => 'application/json')); } $object = $admin->getObject($objectId); if (!$object) { return new Response(json_encode(array('status' => 'KO', 'message' => 'Object does not exist')), 200, array('Content-Type' => 'application/json')); } // check user permission if (false === $admin->isGranted('EDIT', $object)) { return new Response(json_encode(array('status' => 'KO', 'message' => 'Invalid permissions')), 200, array('Content-Type' => 'application/json')); } if ($context == 'list') { $fieldDescription = $admin->getListFieldDescription($field); } else { return new Response(json_encode(array('status' => 'KO', 'message' => 'Invalid context')), 200, array('Content-Type' => 'application/json')); } if (!$fieldDescription) { return new Response(json_encode(array('status' => 'KO', 'message' => 'The field does not exist')), 200, array('Content-Type' => 'application/json')); } if (!$fieldDescription->getOption('editable')) { return new Response(json_encode(array('status' => 'KO', 'message' => 'The field cannot be edit, editable option must be set to true')), 200, array('Content-Type' => 'application/json')); } // TODO : call the validator component ... $propertyPath = new PropertyPath($field); $propertyPath->setValue($object, $value); $admin->update($object); // render the widget // todo : fix this, the twig environment variable is not set inside the extension ... $extension = $this->twig->getExtension('sonata_admin'); $extension->initRuntime($this->twig); $content = $extension->renderListElement($object, $fieldDescription); return new Response(json_encode(array('status' => 'OK', 'content' => $content)), 200, array('Content-Type' => 'application/json')); }
/** * @param FormInterface $root * @param string $propertyPath */ public function __construct(FormInterface $root, $propertyPath) { parent::__construct($propertyPath); $this->root = $root; }
public function testGetParent_noParent() { $propertyPath = new PropertyPath('path'); $this->assertNull($propertyPath->getParent()); }
private function groupEntities($entities, $groupBy) { $grouped = array(); foreach ($entities as $entity) { // Get group name from property path try { $path = new PropertyPath($groupBy); $group = (string) $path->getValue($entity); } catch (UnexpectedTypeException $e) { // PropertyPath cannot traverse entity $group = null; } if (empty($group)) { $grouped[] = $entity; } else { $grouped[$group][] = $entity; } } return $grouped; }
public function onBindNormData(FilterDataEvent $event) { $originalData = $event->getForm()->getNormData(); // If we are not allowed to change anything, return immediately if (!$this->allowAdd && !$this->allowDelete) { // Don't set to the snapshot as then we are switching from the // original object to its copy, which might break things $event->setData($originalData); return; } $form = $event->getForm(); $data = $event->getData(); $childPropertyPath = null; $parentData = null; $addMethod = null; $removeMethod = null; $propertyPath = null; $plural = null; if ($form->hasParent() && $form->getAttribute('property_path')) { $propertyPath = new PropertyPath($form->getAttribute('property_path')); $childPropertyPath = $propertyPath; $parentData = $form->getParent()->getClientData(); $lastElement = $propertyPath->getElement($propertyPath->getLength() - 1); // If the property path contains more than one element, the parent // data is the object at the parent property path if ($propertyPath->getLength() > 1) { $parentData = $propertyPath->getParent()->getValue($parentData); // Property path relative to $parentData $childPropertyPath = new PropertyPath($lastElement); } // The plural form is the last element of the property path $plural = ucfirst($lastElement); } if (null === $data) { $data = array(); } if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { throw new UnexpectedTypeException($data, 'array or (\\Traversable and \\ArrayAccess)'); } if (null !== $originalData && !is_array($originalData) && !($originalData instanceof \Traversable && $originalData instanceof \ArrayAccess)) { throw new UnexpectedTypeException($originalData, 'array or (\\Traversable and \\ArrayAccess)'); } // Check if the parent has matching methods to add/remove items if ($this->mergeStrategy & self::MERGE_INTO_PARENT && is_object($parentData)) { $reflClass = new \ReflectionClass($parentData); $addMethodNeeded = $this->allowAdd && !$this->addMethod; $removeMethodNeeded = $this->allowDelete && !$this->removeMethod; // Any of the two methods is required, but not yet known if ($addMethodNeeded || $removeMethodNeeded) { $singulars = (array) FormUtil::singularify($plural); foreach ($singulars as $singular) { // Try to find adder, but don't override preconfigured one if ($addMethodNeeded) { $addMethod = 'add' . $singular; // False alert if (!$this->isAccessible($reflClass, $addMethod, 1)) { $addMethod = null; } } // Try to find remover, but don't override preconfigured one if ($removeMethodNeeded) { $removeMethod = 'remove' . $singular; // False alert if (!$this->isAccessible($reflClass, $removeMethod, 1)) { $removeMethod = null; } } // Found all that we need. Abort search. if ((!$addMethodNeeded || $addMethod) && (!$removeMethodNeeded || $removeMethod)) { break; } // False alert $addMethod = null; $removeMethod = null; } } // Set preconfigured adder if ($this->allowAdd && $this->addMethod) { $addMethod = $this->addMethod; if (!$this->isAccessible($reflClass, $addMethod, 1)) { throw new FormException(sprintf('The public method "%s" could not be found on class %s', $addMethod, $reflClass->getName())); } } // Set preconfigured remover if ($this->allowDelete && $this->removeMethod) { $removeMethod = $this->removeMethod; if (!$this->isAccessible($reflClass, $removeMethod, 1)) { throw new FormException(sprintf('The public method "%s" could not be found on class %s', $removeMethod, $reflClass->getName())); } } } // Calculate delta between $data and the snapshot created in PRE_BIND $itemsToDelete = array(); $itemsToAdd = is_object($data) ? clone $data : $data; if ($this->dataSnapshot) { foreach ($this->dataSnapshot as $originalItem) { foreach ($data as $key => $item) { if ($item === $originalItem) { // Item found, next original item unset($itemsToAdd[$key]); continue 2; } } // Item not found, remember for deletion foreach ($originalData as $key => $item) { if ($item === $originalItem) { $itemsToDelete[$key] = $item; continue 2; } } } } if ($addMethod || $removeMethod) { // If methods to add and to remove exist, call them now, if allowed if ($removeMethod) { foreach ($itemsToDelete as $item) { $parentData->{$removeMethod}($item); } } if ($addMethod) { foreach ($itemsToAdd as $item) { $parentData->{$addMethod}($item); } } $event->setData($childPropertyPath->getValue($parentData)); } elseif ($this->mergeStrategy & self::MERGE_NORMAL) { if (!$originalData) { // No original data was set. Set it if allowed if ($this->allowAdd) { $originalData = $data; } } else { // Original data is an array-like structure // Add and remove items in the original variable if ($this->allowDelete) { foreach ($itemsToDelete as $key => $item) { unset($originalData[$key]); } } if ($this->allowAdd) { foreach ($itemsToAdd as $key => $item) { if (!isset($originalData[$key])) { $originalData[$key] = $item; } else { $originalData[] = $item; } } } } $event->setData($originalData); } }
protected function renderChoiceWidgetOptions(FormView $view, $blockName, array $variables = array()) { $options = array(); $valuePropertyPath = new PropertyPath('value'); $labelPropertyPath = new PropertyPath('label'); foreach ($variables['options'] as $groupLabel => $choice) { if (is_array($choice) || $choice instanceof \Traversable) { $options = array_merge($options, $this->renderChoiceWidgetOptions($view, $blockName, array_merge($variables, array('options' => $choice)))); } else { $option = new FormRepresentations\Option(); $option->attributes['value'] = $valuePropertyPath->getValue($choice); if ($this->isSelectedChoice($choice, $variables['value'])) { $option['attributes']['selected'] = 'selected'; } $option->value = $labelPropertyPath->getValue($choice); $options[] = $option; } } return $options; }
protected function getValue($object, $field) { $path = new PropertyPath($field); return $path->getValue($object); }
public function normalizeEntity($entity, $doNotExpandRelationsAndCollections = false) { // Make sure that the used is authorized to see this resource $canSee = $this->getConfigurationEntity()['can_see']; if (false === $canSee($this->securityContext, $entity)) { throw new AccessDeniedException(); } $entityRepresentationClass = $this->getConfigurationEntity()['representation_class']; $entityRepresentation = new $entityRepresentationClass(); $entityRepresentation->addLink($this->atomLinkFactory->create('self', $this->getUrlGenerator()->generateEntityUrl($entity))); $getEntityValue = function ($value) use($entity) { if ($value instanceof \Closure) { return $value($entity); } $propertyPath = new PropertyPath($value); return $propertyPath->getValue($entity); }; // Properties $normalizeAttributes = $this->getConfigurationEntity()['normalize_attributes']; foreach ($normalizeAttributes as $key => $value) { $entityRepresentation->setAttribute($key, $getEntityValue($value)); } // Elements $normalizeElements = $this->getConfigurationEntity()['normalize_elements']; foreach ($normalizeElements as $key => $value) { $entityRepresentation->setElement($key, $getEntityValue($value)); } // Entity collections foreach ($this->getConfigurationEntityCollections() as $entityCollectionRel => $configurationEntityCollection) { if (isset($configurationEntityCollection['can_see']) && !$configurationEntityCollection['can_see']($this->securityContext, $entity)) { continue; } if ($doNotExpandRelationsAndCollections || !in_array($entityCollectionRel, $this->getConfigurationEntity()['expanded_collections'])) { $entityRepresentation->addLink($this->atomLinkFactory->create($entityCollectionRel, $this->getUrlGenerator()->generateEntityCollectionUrl($entity, $entityCollectionRel))); } else { $entityRelationSearchFormDescription = $this->createEntityCollectionSearchFormDescription($entity, $entityCollectionRel); $entityRelationPager = $this->getEntityCollectionPager($entity, $entityRelationSearchFormDescription->getData(), $entityCollectionRel); $entityRelationRepresentation = $this->normalizeEntityCollection($entity, $entityCollectionRel, $entityRelationPager, $entityRelationSearchFormDescription); $entityRelationRepresentation->rel = $this->atomLinkFactory->getRel($entityCollectionRel); $entityRepresentation->addCollection($entityRelationRepresentation); } } // Entity relations foreach ($this->getConfigurationEntityRelations() as $entityRelationRel => $entityRelationConfiguration) { $entityRelation = $this->getEntityRelation($entity, $entityRelationRel); if (null === $entityRelation) { continue; } $entityRelationClassName = ClassUtils::getRealClass(get_class($entityRelation)); $entityRelationResource = $this->container->get($entityRelationConfiguration['resources'][$entityRelationClassName]); $canSeeEntityRelation = $entityRelationResource->getConfigurationEntity()['can_see']; if (!$canSeeEntityRelation($this->securityContext, $entityRelation) || $doNotExpandRelationsAndCollections || !in_array($entityRelationRel, $this->getConfigurationEntity()['expanded_relations'])) { $entityRepresentation->addLink($this->atomLinkFactory->create($entityRelationRel, $entityRelationResource->getUrlGenerator()->generateEntityUrl($entityRelation))); } else { $entityRelationRepresentation = $entityRelationResource->normalizeEntity($entityRelation, true); $entityRelationRepresentation->rel = $this->atomLinkFactory->getRel($entityRelationRel); $entityRepresentation->addRelation($entityRelationRepresentation); } } return $entityRepresentation; }
/** * Build the form FieldDescription collection * * @return void */ protected function buildForm() { if ($this->form) { return; } // append parent object if any // todo : clean the way the Admin class can retrieve set the object if ($this->isChild() && $this->getParentAssociationMapping()) { $parent = $this->getParent()->getObject($this->request->get($this->getParent()->getIdParameter())); $propertyPath = new PropertyPath($this->getParentAssociationMapping()); $object = $this->getSubject(); $propertyPath->setValue($object, $parent); } $this->form = $this->getFormBuilder()->getForm(); }
/** * @dataProvider provideCustomDataErrorTests */ public function testCustomDataErrorMapping($target, $mapFrom, $mapTo, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) { $violation = $this->getConstraintViolation($violationPath); $parent = $this->getForm('parent', null, null, array($mapFrom => $mapTo)); $child = $this->getForm($childName, $childPath); $grandChild = $this->getForm($grandChildName, $grandChildPath); $parent->add($child); $child->add($grandChild); // Add a field mapped to the first element of $mapFrom // to try to distract the algorithm // Only add it if we expect the error to come up on a different // level than LEVEL_0, because in this case the error would // (correctly) be mapped to the distraction field if ($target !== self::LEVEL_0) { $mapFromPath = new PropertyPath($mapFrom); $mapFromPrefix = $mapFromPath->isIndex(0) ? '['.$mapFromPath->getElement(0).']' : $mapFromPath->getElement(0); $distraction = $this->getForm('distraction', $mapFromPrefix); $parent->add($distraction); } $this->mapper->mapViolation($violation, $parent); if ($target !== self::LEVEL_0) { $this->assertFalse($distraction->hasErrors(), 'distraction should not have an error, but has one'); } if (self::LEVEL_0 === $target) { $this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName() . ' should have an error, but has none'); $this->assertFalse($child->hasErrors(), $childName . ' should not have an error, but has one'); $this->assertFalse($grandChild->hasErrors(), $grandChildName . ' should not have an error, but has one'); } elseif (self::LEVEL_1 === $target) { $this->assertFalse($parent->hasErrors(), $parent->getName() . ' should not have an error, but has one'); $this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName . ' should have an error, but has none'); $this->assertFalse($grandChild->hasErrors(), $grandChildName . ' should not have an error, but has one'); } else { $this->assertFalse($parent->hasErrors(), $parent->getName() . ' should not have an error, but has one'); $this->assertFalse($child->hasErrors(), $childName . ' should not have an error, but has one'); $this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName. ' should have an error, but has none'); } }
/** * @expectedException \OutOfBoundsException */ public function testIsIndexDoesNotAcceptNegativeIndices() { $propertyPath = new PropertyPath('grandpa.parent[child]'); $propertyPath->isIndex(-1); }
/** * @expectedException Symfony\Component\Form\Exception\InvalidPropertyException */ public function testMapFormToDataFailsIfOnlyRemoverFound() { $car = $this->getMock(__CLASS__ . '_CarOnlyRemover'); $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth')); $axesAfter = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third')); $path = new PropertyPath('axes'); $car->expects($this->any())->method('getAxes')->will($this->returnValue($axesBefore)); $path->setValue($car, $axesAfter); }
public function testToString() { $path = new PropertyPath('reference.traversable[index].property'); $this->assertEquals('reference.traversable[index].property', $path->__toString()); }