I ran into a trouble of understanding how exactly data is processed in DataTransformers of Symfony.
I have a just a password form. Just one field. This field belongs to the User entity which has to constraints defined in .yml file.
Software\Bundle\Entity\User:
password:
- NotBlank: ~
- Length:
min: 6
max: 155
The validation works fine, as supposed to. The problem arise when password must be encoded automatically from the field. So,
$builder->add('password', 'password', [
'label' => 'word.password'
]);
$builder->get('password')
->addModelTransformer(new EncodePasswordTransformer());
And the transformer itself:
class EncodePasswordTransformer implements DataTransformerInterface
{
public function transform($value)
{
return $value;
}
public function reverseTransform($value)
{
// encode password
return PasswordHash::createHash($value);
}
}
So here's what's happening:
The form should contain 6 to 155 characters but $form->isValid() is always true, because the PasswordHash::createHash($value) encodes the password to 32 characters. What I was expecting is:
Form validates raw password, if more than 6 chars then go to $form->isValid() true and then encode the password after it's validated.
I know I can just encode the password manually while the form is valid without using DataTransformer but I was hoping for a bit more elegant way.
Am I wrong?
You can't, according to the documents.
Symfony's form library uses the validator service internally to validate the underlying object after values have been submitted.
So you're not actually validating form, but the object underneath which has no "notion" of the plain password.
A not so elegant solution would be to include a plain password field on your user and not persist it. However you probably won't be able to validate your existing user objects (e.g: in an update form), since their plain password fields will be null. To get around that you could create a custom validator that checks the validity of the $plainPassword field only if the user is not new. You could check that by using doctrine's UnitOfWork or by checking if the id of the user is null.
I suggest you also take a look at FOSUserBundle it has (or had) a similar approach to the plain password field and might have what you're looking for.
Related
I have implemented function to check my password length in register api call like..
if($password){
$this->validatePassword($password);
}
from other api call..
public function validatePassword($password)
{
if (strlen($password) < 6)
{
throw new PasswordLength('Password must be minimum 6 characters long!');
}
}
and in my postman I am not returning an empty field but I keep getting error
Integrity constraint violation: 1048 Column 'password' cannot be null
In symfony, the plainPassword field is purely used to carry the users 'new' password into a method that would encode it and set it to password.
After the password field is populated, then the plainPassword field is blanked so that no plain text credentials are saved to the db. This is done by calling the implemented eraseCredentials method (found in the required UserInterface). Or, you can make the plainPassword method non-persistent in the user class.
Have a look here at the docs to see how to go about it all.
The docs show an extract from what should be a user manager utility class. Use this to set and update passwords as well as creating new users.
Theres a more complete explination here where the whole process is explained from end-to-end.
Solved!
if($password){
$this->validatePassword($password);
}
$user->setPassword($password);
I'm using ZF2 and mysql, but the question is platform-independent. I have a data transfer object Organization that gets hydrated from an html form. OrganizationMapper has a save method that (1) gets Organization as an argument and (2) fills a couple of database tables one after another.
Suppose the 1st table gets filled ok, but the 2nd doesn't because one of the properties of Organization isn't set (not null constraint on a column). The user gets an error, but the 1st table is already filled. If he attempts to submit the form again, but this time with all html fields filled, all the tables get filled ok, but the 1st has a previous unused row.
How could I avoid this situation?
I thought of checking for empty values with if's in the mapper's save method, but it doesn't seem elegant. I know about the InputFilter validations in ZF2, but these check the user input in the form, they don't check things when the php code communicates with the database.
Any help?
The best way is to validate all the data before you start writing it to the database.
I didn't use ZF2 and this solution is actually framework-dependent, so you need to check the ZF2 docs. For example, in Yii, you just define validation rules for every field of the model, so you can ensure that your Organization contains all the data before you start saving it to the database, probably something similar is possible in Zend.
Note, that validation doesn't mean just to check for empty values, you may need to verify different things, like: "email is correct email like xxx#yyy.com", "name is not empty", "name length is more than 3 chars", "name length is less than 1000 chars" and so on.
For Yii it roughly looks like this:
class Organization extends ActiveRecord {
...
// here we define the validation rules
public function rules() {
return [
// name is required
['name', 'required'],
// check min / max length
['name', 'string', 'min' => 3, 'max' => 12],
// check if email is valid
['email', 'email']
];
}
}
Now you can do $organization->validate() to make sure everything is correct (also when you do $organization->save() the rules will be checked before saving to the database).
And one more solution to protect from the inconsistent data is to use transactions. In the case you write to multiple tables, you anyway need them, even if you validated everything. Unexpected things happen, so it is better to protect your saving code like this (pseudo-code):
$transaction->start();
try {
$table1->writeSomeData();
$table2->writeMoreData();
$transaction->commit();
} (catch Exception $e) {
$transaction->rollback();
}
Again, check your framework documentation, it probably supports this in some way.
This may be a very simple question about ZF2, but I can't get my head around it. So please bear with me.
Suppose I create a user registration form. It has 2 inputs: username and password. In the model, I create a class User that has $username and $password variables, and the setters + getters for the two variables.
My question is how to pass what a user writes into the HTML inputs to the corresponding setters? Obviously, it has to do with the $_POST array. But how is it done internally in ZF2? What should I use to pass the username input to the actual $username variable?
You will need to use a hydrator to populate the model's data. An example would be Zend\Stdlib\Hydrator\ClassMethods which accepts an array (such as the post data) and calls the setters of the target object.
$hydrator = new \Zend\Stdlib\Hydrator\ClassMethods;
$user = new \User\Model\User;
$data = [
'username' => 'foo',
'password' => 'bar',
];
$hydrator->hydrate($data, $user);
echo $user->getUsername(); // foo
Keep in mind however that you will need to ensure that you sanitize all user supplied data (e.g hash the password) as well as validate the form data is correct (e.g. ensure a minimum complexity of the password, or to ensure valid e-mail address for the username).
The Zend\Form component is designed to integrate all these requirements as well as allow you to construct the forms HTML output. You would attach the hydrator to the form and once is has been validated you can retrieve a constructed entity populated will the user supplied data using $form->getData().
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
// hydration occurs internally and returns our user populated
$user = $form->getData();
if ($user instanceof User) {
echo $user->getUsername();
}
}
}
The ZF2 tutorial gives a detailed explanation of the process, if you have not already created the example project I highly recommend doing so.
If your form is posting, the values will be stored within the post with the input ids as the keys. You can use a couple of ways to access them from there.
$this->getRequest()
Will get everything, then you can use ->getParams() to get all the post parameters or even ->getParam('username') to get just the ones you need.
I have a helper method that returns a captcha image url & stores a session of the key:
function captcha(){
$builder = new CaptchaBuilder;
$builder->build();
Session::put('phrase', $builder->getPhrase());
return $builder->inline();
}
The user then writes the captcha and submits the form and my controller grabs all and validates it:
'captcha' => 'required|same:'.Session::get('phrase')
The problem is no-matter what it always says they phrase & the textbox submission are not the same...
I can give more information if needed, also if this is not the best way to do it please give me suggestions, I am just learning Laravel4.
Edit for some output info:
If I return the values from the controller:
return "Session:".Session::get('phrase')." - Input:".$input['captcha'];
It returns: Session:5zij5 - Input:5zij5
According to Laravel's docs, same refers to another input.
I'd recommend using a custom validation rule to compare the session and the input.
I'm currently building a form manager in PHP to validate large forms.
I'm wondering what is the best structure for that, because the number of fields will be different each time.
I'm already filtering the field to validate by using a prefix (ex : 'user_name', will be validated, but 'name' no).
My real problem is for the validation : I must check the type of the field (mail, zipcode, phone...)
AND check that the value for this type of field is valid.
I thought that I could use HTML5 Custom data" (ex: data-fieldtype="zipcode"), but i didn't know that the server can't get this attribute...
What's the way to go ?
I could use 2 inputs per field, one for the value and one for the type, but it looks really stupid !
Thanks if you can help.
EDIT :
Your answers are all interesting, i don't know which is best.
I will probably make a mix between your solutions, depending of the kind of form.
Thanks a lot.
Methinks, this shouldn't be played via the Browser without further thought: A malicious user would be able to manipulate a "INT ONLY" field into being freetext, and your application would suddenly have to deal with freetext in a field, that is thought to be validated as an integer (und thus e.g. safe for SQL).
You have two approaches:
Have your form validation structure stored in the DB, and submit a single hidden field, that carries the ID of the validation structure. On receiving the request, your script would request the structure from the DB, unserialize it, and work on it.
If you really need to go through the browser, serialize your validation structure, base64-encode it and use it as a single hidden field. For the reasons stated above, it is mandatory to authenticate this value, either hashing (concatenate with another string only known to the server, hash it, send the hash as a second hidden field, on next request verify the hash is correct) or by encryption (encrypt the serialized data before the browser roundtrip, decrypt afterwards, key known only to the server)
Both would make your prefix-trick unnecessary, increasing readability and maintainability.
If no framework is used, you can use an array of field => options.
$rules = [
'user_name' => 'required',
'user_email' => 'email required',
// ...
];
And then feed them to some validator class, where rules are methods and they're being called inside validate method dynamically:
class Validator {
public function __construct($data) { $this->data = $data; }
private function required($field) {}
private function email($email) {}
// etc
/** #method bool validate(array $rules) */
public function validate($rules) {}
}
Never ever validate on client-side only. Always validate serverside.
Usually you will have a base class for controller, and every other controller extends it.
Aa good approach is to have en every view controller (or the bootsrtap) a method check_params().
It should 1) get a copy or $_REQUEST, check every parameter needed, 2) delete $_REQUEST, 3) write back the checked and validated params.
abstract class controller_base {
public function __construct() { ...; $this->check_param();...}
protected final function check_param() {
foreach ($this->param_list() AS $name => $type) {...}
}
abstract public function param_list();
}
class controller_login extends controller_base {
public function param_list() {
return array('name' => 'string', 'password' => 'string');
}
}
The idea is that this way you
only use params that has been sanitized
you autmaticly delete every param not needed
you have a list in every controller that states the used params.