-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Context problem with Identical validator and UniqueObject validator #196
Comments
Hi, For the context, here is what I'm doing in my controller: public function put(array $params)
{
if (!($user = $this->userService->getById($params['user_id']))) {
throw new NotFoundException();
}
$data = $this->validateIncomingData(UserInputFilter::class, [], $user);
$this->hydrateObject(UserHydrator::class, $user, $data);
$this->userService->update($user);
// ...
} As you can see I give the full user object as a context. Then my input filter overrides the "isValid" method: namespace Account\InputFilter;
use Account\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use DoctrineModule\Validator\NoObjectExists;
use DoctrineModule\Validator\UniqueObject;
use Zend\Filter\FilterPluginManager;
use Zend\Filter\StringToLower;
use Zend\Filter\StringTrim;
use Zend\Filter\StripTags;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterInterface;
use Zend\Validator\EmailAddress;
use Zend\Validator\StringLength;
use Zend\Validator\ValidatorChain;
/**
* Validate and filter the data of a user
*
* @author Michaël Gallego <[email protected]>
* @licence MIT
*/
class UserInputFilter extends InputFilter
{
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* @param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->add([
'name' => 'email',
'required' => true,
'validators' => [
[
'name' => EmailAddress::class,
'options' => [
'messages' => [
EmailAddress::INVALID_FORMAT => 'Email address is invalid'
]
]
],
[
'name' => StringLength::class,
'options' => ['max' => 255]
],
[
'name' => NoObjectExists::class,
'options' => [
'object_repository' => $entityManager->getRepository(User::class),
'fields' => ['email'],
'messages' => [
NoObjectExists::ERROR_OBJECT_FOUND => 'User with this email address already exists'
]
]
]
],
'filters' => [
['name' => StringTrim::class],
['name' => StringToLower::class]
]
]);
}
/**
* @param mixed $context
* @return bool
*/
public function isValid($context = null)
{
// If the context is an object, we do additional things as this is an update
if ($context instanceof User) {
$this->data['id'] = $context->getId();
$this->prepareInputFilterForUpdate($context);
}
return parent::isValid($context);
}
/**
* @param User $user
*/
private function prepareInputFilterForUpdate(User $user)
{
/** @var \Zend\InputFilter\InputInterface $passwordInput */
$passwordInput = $this->inputs['password'];
$passwordInput->setRequired(false);
// For the email, we need to copy all the existing validators, but remove the "NoObjectExists" to replace
// it by a "UniqueObject" filter
/** @var \Zend\InputFilter\InputInterface $emailInput */
$emailInput = $this->inputs['email'];
$validatorChain = $emailInput->getValidatorChain();
$newValidatorChain = new ValidatorChain();
foreach ($validatorChain->getValidators() as $validator) {
$instance = $validator['instance'];
if ($instance instanceof NoObjectExists) {
continue;
}
$newValidatorChain->attach($instance);
}
// Finally, attach the new one
$newValidatorChain->attachByName(UniqueObject::class, [
'object_repository' => $this->entityManager->getRepository(User::class),
'object_manager' => $this->entityManager,
'use_context' => true,
'fields' => ['email'],
'messages' => [
UniqueObject::ERROR_OBJECT_NOT_UNIQUE => 'User with this email address already exists'
]
]);
$emailInput->setValidatorChain($newValidatorChain);
}
} I admit this is a bit hacky, but until input filter supports a better way to context, this is the best one. |
Ok it's good for some king of context but how doing this with Identical validator. Both email and confirm_email need to check data from the request and not for the entity. So my problem is to merge context "user id" with default context data retrieve from the request :/. |
Hi, how do you get entity manager injected into you filter class? |
Hello, I have one problem when I use Identical validator and UniqueObject validator. In fact for UniqueObject we have to use a context with the id of the object but there is an other context for identical validator (by default data of the InputFilter)
In controller :
With this (we assume that email and confirm_email are equals) Identical validator is not valid because I force the context with user id and when I don't use the context Identical validator is valid because the default context is passed but UniqueObject throw exception because the context id is missing.
I think it's really really bad to have id in request content for security and in my case it will retrieve with the access token.
ValidateIncomingData retrieve by itself the request content we can't merge id context with request content.
Maybe it will be great to add an other parameter to the plugin ValidateIncomingData like "merge_with_default_context" by default to false and when we have this both validator use like this :
And ValidateIncomingData merge request content with ['id' => $user->getId()] but maybe we have an other problem in validation where data are filtered in the inputFilter ...
@bakura10 @Ocramius what do you think ? Maybe an other idea ?
The text was updated successfully, but these errors were encountered: