On my site I have my register page, where a user inputs his information to register to my site.
When he does the I process the user inputted information. Now when processing it I'm using arrays to facilitate the process, and not have me write the same code over and over. For example, here is how I gather the user information and check it using a method in my validation class.
$data = array(
$_POST['firstname'] => "First Name",
$_POST['lastname'] => "Last Name"
);
foreach($data as $userInput => $fieldName) {
$validation = new Validation();
$result = $validation->checkEmpty($userInput, $fieldName);
}
Now this all works for me on my page, I'm able to check to see if the user left something empty on the register with the method "checkEmpty", which also returns an array of the fields left empty. I didn't show the method because it's not part of my problem. Now, here's my question, I want to also check the length of the fields. What would be a good way for me to do this without rewriting things over and over? Would I have to do another array?
I'm thinking something like this, maybe?
$data2 = array(
$_POST['firstname'] => 30,
$_POST['lastname'] => 35
),
foreach($data as $userInput => $limit) {
$result = $validation->checkLength($userInput, $limit);
}
But where I get stumped is, if one the inputted fields is too long, how do return which one it was if in the array I passed through I don't have the field name to which it belongs? I thought of a multi-dimensional array, but I'm not sure if that will work.
I would structure the the array completely differently, something like:
$config = array(
'firstname' => array('notEmpty' = true,
'limit' => 30),
'lastname' => array('notEmpty' = true,
'limit' => 35)
);
Then I would create a method validate that looks like this:
public function validate($config, $data) {
$error = array();
foreach($data as $field => $value) {
$c = $config[$field];
if(array_key_exists('notEmpty', $c)) {
if(!$this->checkEmpty($value)) {
$this->addError($error, $field, 'Must not be empty');
}
}
if(array_key_exists('limit', $c)) {
if(!$this->checkLength($value, $c['limit'])) {
$this->addError($error, $field, 'Must not be longer than' . $c['limit'] . ' characters');
}
}
/*...*/
}
return $error;
}
private function addError(&$error, $field, $msg) {
if(!array_key_exists($field, $error)) {
$error[$field] = array();
}
$error[$field][] = $msg;
}
Then you just call:
$validation = new Validation();
$errors = $validation->validate($config, $_POST);
and $errors will contain all error messages per field. You just need to to loop over them and print them next to the field.
Of course this code can (and should be!) improved. E.g. dynamic lookup of validation methods).
That said, I highly recommend to have a look at and use a ready made validation classes such as Zend_Validate
I would think using the field names as keys for your array may be a better approach. Then yeah, use a multi-dimensional array something like this.
$data = array(
'First Name' => array($_POST['firstname'], 30),
'Last Name' => array($_POST['lastname'], 35)
)
You gloss over the validation class, but I think its important. For one it should be used statically if it doesn't have any state. Theres no reason to be constructing it every single loop iteration like that!
Validation::checkEmpty($x);
AS for the length checking, id suggest you keep fields in Validation something like
protected $lengths = array( 'firstname' => 35, 'lastname' => 30, 'default' => 100);
and call it with
Validation::checkLength($userInput, $fieldName);
with a body like
protected static function checkLength($subject, $fieldType = 'default')
{
return strlen($subject) < self::$lengthTypes($fieldType);
//or self::$fields['lengthLimit'] if you are going to structure it like Felix suggest
}
You don't need me to tell you that a function lengthChecker($subject, $limit) is pretty pointless.
EDIT - restructuring like Felix suggested isn't a bad idea depending on how far you want tot ake this. His looks more extensible. If you are going to implement the addError you probably don't want the static design.
Related
This might seem like a stupid and trivial question. I am having problem naming functions in PHP. I have two functions that retrieves all the information of a student given its id or name and email.
Since PHP doesn't have function overloading in the same sense as JAVA, I am having difficulty naming the functions.
Here is what I have done. These are the names that I have given them.
get_students_with_id($id) and get_students_with_name_and_email($name, $email)
But the parameters are gonna increase. I need a better and simple solution to name these functions or methods. BTW, they all belong to the same class. So what am I gonna do? Thanks in advance.
In PHP there doesn't exist the concept of method overriding like in JAVA, for example, but you can send default parameters, for example:
get_students($id, $name = null, $email = null)
This means that you don't need to call the function with the three parameters. You can do it by calling it just with one and it will assume it is the id. For example, if you want to have a function working for your example above, you could do something like:
function get_students($id, $name = null, $email) {
if (!empty($id)) {
// Get students by their ids
} else if (!empty($name) && !empty($email)) {
// Get students by their names and emails
}
}
And you can call the function above:
get_students(1); //Will retrieve studen with id 1
get_students(null, "Name", "email#email.com"); //Will retrieve students with name "Name" and email "email#email.com"
A search method could look something like this:
class Student {
public static $columns = ['id', 'name', 'email', 'password', /* ... */];
// Imagine that this method is called with the following array:
// ['name' => 'Joe', 'password' => 'Pa55w0rD']
public static function search(array $queries) {
// We will be appending WHERE clauses to this SQL query
$sql = 'SELECT * FROM students WHERE ';
// Get the column names
$parameters = array_keys($queries);
// Create a parameterized WHERE clause for each column
foreach ($parameters as & $param) {
if ( ! in_array($param, self::$columns)) {
throw "Invalid column";
}
$param = "{$param} = :{$param}";
}
// Squish parameterized WHERE clauses into one
// and append it to the SQL query
$sql .= implode(' AND ', $parameters);
// The query will now look something like this:
// SELECT * FROM students WHERE name = :name AND password = :password
// Prepare the SQL query
$stmt = DB::instance()->prepare($sql);
// Go over the queries and bind the values to the columns
foreach ($queries as $col => $val) {
$stmt->bindValue(":" . $col, $val);
// Internally the query will look something like this:
// SELECT * FROM students WHERE name = 'Joe' AND password = 'Pa55w0rD'
}
// Execute
$result = $stmt->execute();
// ...
}
}
To use the method you would do something like this:
$student = Student::search([
'name' => 'Joe',
'password' => 'Pa55w0rD',
]);
You would want to handle the data in a safer way (making sure the password is hashed, for instance), but the general idea is there.
Why not use get_students($id=0, $name='', $email='') and so on for your other parameters, then have the function do whatever is necessary based on the passed parameters?
If that gets to be too much, pass an array check for keys. So if array('id' => 1) is passed then if (array_key_exists('id', $input)) {...} would catch it and proceed with actual function work, but if other keys/values are passed then a subsequent appropriate elseif would catch it.
Update: I think a format like this might be able to handle most of your use cases, based on some of the comments I read in the question. Not sure what your DB is, so this was done with MySQL in mind.
function get_students($input) {
$where = array();
$allowed_columns = array('id', 'name', 'email');
foreach ($allowed_columns as $key) {
if (!array_key_exists($key, $input)) continue;
$where[] = "`$key` = '" . mysqli_escape_string($input[$key]) . "'";
}
if ($where) {
$query = 'SELECT ... FROM `...` WHERE ' . join(' AND ', $where);
// etc...
} else {
return false;
}
}
I would use a class instead of multiple functions
class Student
{
public static function byName($name)
{
// ...
}
public static function byId($id)
{
// ...
}
}
$student = Student::byName('joe');
This would allow it to be much cleaner and more extendible, as you can put common logic in protected static methods in the class.
If you want to do multiples you can do some chaining which is a little more complicated.
I've mocked up a quick ideone which you can reverse engineer:
http://ideone.com/duafK4
For a project with Laravel 4.1 I have a little UI issue I'd like to solve.
Some inputs make an ajax call to laravel on blur and that works fine. It simply sends it's value. In laravel I then check with the validator.
public function validate() {
if(Request::ajax()) {
$validation = Validator::make(Input::all(), array(
'email' => 'unique:users|required|email',
'username' => 'required'
));
if($validation->fails()) {
return $validation->messages()->toJson();
}
return "";
}
return "";
}
Although this works, the json string also contains fields I have no need to check. To be precise this is the feedback I get:
{"email":["The email field is required."],"username":["The username field is required."]}
But seeing it is on blur I only want the one I'm actually checking in return. So if i'm blurring email I want a return of:
{"email":["The email field is required."]}
Now I know it's obviously because my array contains multiple fields, but I don't feel like writing a complete validation for each possible input I ever make.
My question is: can I somehow only get a return of the post values that are actually posted, even though the value might be null and not get rest of the array back.
Try this (untested, feel free to comment/downvote if it doesn't work) :
// Required rules, these will always be present in the validation
$required = ["email" => "unique:users|required|email", "username" => "required"];
// Optional rules, these will only be used if the fields they verify aren't empty
$optional = ["other_field" => "other_rules"];
// Gets input data as an array excluding the CSRF token
// You can use Input::all() if there isn't one
$input = Input::except('_token');
// Iterates over the input values
foreach ($input as $key => $value) {
// To make field names case-insensitive
$key = strtolower($key);
// If the field exists in the rules, to avoid
// exceptions if an extra field is added
if (in_array($key, $optional)) {
// Append corresponding validation rule to the main validation rules
$required[$key] = $optional[$key];
}
}
// Finally do your validation using these rules
$validation = Validator::make($input, $required);
Add your required fields to the $required array, the key being the field's name in the POST data, and the optional fields in the $optional array - the optional ones will only be used if the field exists in the submitted data.
You can also use Laravel requests in a much cleaner way
public function rules(){
$validation = [];
$input = Request::all();
if (array_key_exists('email', $input)) {
$validation['email'] = 'unique:users|required|email';
}
if (array_key_exists('username', $input)) {
$validation['username'] = 'required|min:6';
}
return $validation;
}
I found it. It's going to be something like this:
if(Request::ajax()) {
$arr = array();
$arr['email'] = 'unique:users|required|email';
$arr['username'] = 'required|min:6';
$checks = array();
foreach($arr as $key => $value) {
if(Input::has($key)) {
$checks[$key] = $value;
}
}
if(count($checks)) {
$validation = Validator::make(Input::all(), $checks);
if($validation->fails()) {
return $validation->messages()->toJson();
}
}
return "ok";
}
return "";
I've most certainly got something very basic wrong here.
Here is the code that is part of my Abstract Class:
private $outarray = null;
public function add_to_array($ahref, $docname, $description) {
$row = array('ahref' => $ahref, 'docname' => $docname, 'description' => $description);
if (!isset($this->outarray)) {
$this->outarray = array();
}
array_push($this->outArray, $row);
}
When I step through the code, though, the outArray remains null. It is never created and never populated.
I'm still green with PHP, but this help doc seems to leave me believing that this is OK to do:
http://www.php.net/manual/en/language.oop5.abstract.php
...particularly where they are declaring the Common method printOut() that performs some action.
I've got 5 elements I am trying to populate outArray with, but each of the 5 times I circle into this function, I come out with outArray being NULL.
Variables are case sensitive. You have in one place $this->outarray and in array_push you have $this->outArray
Ugh.
PHP is case sensitive, but it does not complain about it because it was assuming I had another variable declared on the fly.
Correct way:
public function add_to_array($ahref, $docname, $description) {
$row = array('ahref' => $ahref, 'docname' => $docname, 'description' => $description);
if (!isset($this->outarray)) {
$this->outarray = array();
}
array_push($this->outarray, $row);
}
A have a lot of controllers where I must to save/create new models, it looks like this:
public Controller_Test extends Controller_Template {
if ($post = $this->request->post()) {
$model = ORM::factory('model');
$model->param1 = $post['Param1'];
$model->param2 = $post['Param26'];
$model->param3 = $post['Param31'];
$model->param4 = $post['Param13'];
$model->param5 = $post['Param2'];
$model->param6 = $post['Param35'];
$model->param7 = $post['Param10'];
$model->param8 = $post['Param22'];
$model->param9 = $post['Param3'];
$model->save();
}
}
Is it possible to unify (create a method) thats will save all array?
I know about $model->values($post)->create();, but still can't understand how really same it works, as u can see I have different keys of posted parameteres and this might be considered.
In many examples all the data assignemnts take place in controller, but they're really small, in my case I'll suppose to have a huge controllers with a lot of data assignment strings and it will be a bad style coding I think.
Whatever you do you need to map the key names in your $_POST variable to model property names.
$model_post_map = array(
'param1' => 'Param1',
'param2' => 'Param26',
'param3' => 'Param31',
'param4' => 'Param13',
'param5' => 'Param2',
'param6' => 'Param35',
'param7' => 'Param10',
'param8' => 'Param22',
'param9' => 'Param3',
);
$post_model_map = array_flip($model_post_map);
function rekey($arr, $map) {
$newarr = array();
foreach ($arr as $k => $v) {
if (isset($map[$k])) {
$newarr[$map[$k]] = $v;
}
}
return $newarr;
}
$modeldata = rekey($post, $post_model_map);
$model->values($modeldata);
You should name your form fields the way you do your models to reduce the impedance mismatch.
You should also use the second argument to $model->values() to restrict what a form can change.
I'm currently working on an OO PHP application. I have a class called validation which I would like to use to check all of the data submitted is valid, however I obviously need somewhere to define the rules for each property to be checked. At the moment, I'm using arrays during the construction of a new object. eg:
$this->name = array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter
)
One array for each property.
I would then call a static method from the validation class which would carry out various checks depending on the values defined in each array.
Is there a more efficient way of doing this?
Any advice appreciated.
Thanks.
I know the associative array is used commonly to configure things in PHP (it's called magic container pattern and is considered bad practice, btw), but why don't you create multiple validator classes instead, each of which able to handle one rule? Something like this:
interface IValidator {
public function validate($value);
}
$validators[] = new StringLengthValidator(2, 10);
$validators[] = new NotNollValidator();
$validators[] = new UsernameDoesNotExistValidator();
This has multiple advantages over the implementation using arrays:
You can document them (very important), phpdoc cannot parse comments for array keys.
Your code becomes typo-safe (array('reqiured' => true))
It is fully OO and does not introduce new concepts
It is more readable (although much more verbose)
The implementation of each constraint can be found intuitively (it's not in a 400-line function, but in the proper class)
EDIT: Here is a link to an answer I gave to a different question, but that is mostly applicable to this one as well.
Since using OO it would be cleaner if you used classes for validating properties. E.g.
class StringProperty
{
public $maxLength;
public $minlength;
public $required;
public $value;
function __construct($value,$maxLength,$minLength,$required)
{
$this->value = $value;
$this-> maxLength = $maxLength;
$this-> minLength = $minLength;
$this-> required = $required;
}
function isValidat()
{
// Check if it is valid
}
function getValidationErrorMessage()
{
}
}
$this->name = new StringProperty($namefromparameter,10,2,true);
if(!$this->name->isValid())
{
$validationMessage = $this->name-getValidationErrorMessage();
}
Using a class has the advantage of encapsulating logic inside of it that the array (basically a structure) does not have.
Maybe get inspired by Zend-Framework Validation.
So define a master:
class BaseValidator {
protected $msgs = array();
protected $params = array();
abstract function isValid($value);
public function __CONSTRUCT($_params) {
$this->params = $_params;
}
public function getMessages() {
// returns errors-messages
return $this->msgs;
}
}
And then build your custom validators:
class EmailValidator extends BaseValidator {
public function isValid($val=null) {
// if no value set use the params['value']
if ($val==null) {
$val = $this->params['value'];
}
// validate the value
if (strlen($val) < $this->params['maxlength']) {
$this->msgs[] = 'Length too short';
}
return count($this->msgs) > 0 ? false : true;
}
}
Finally your inital array could become something like:
$this->name = new EmailValidator(
array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter,
),
),
);
validation could then be done like this:
if ($this->name->isValid()) {
echo 'everything fine';
} else {
echo 'Error: '.implode('<br/>', $this->name->getMessages());
}