CakePHP - a code sample seems strange to me, what am i missing? - php

Attached code taken from cakephp bakery, where someone uploaded a sample about custom validation rules.
class Contact extends AppModel
{
var $name = 'Contact';
var $validate = array(
'email' => array(
'identicalFieldValues' => array(
'rule' => array('identicalFieldValues', 'confirm_email' ),
'message' => 'Please re-enter your password twice so that the values match'
)
)
);
function identicalFieldValues( $field=array(), $compare_field=null )
{
foreach( $field as $key => $value ){
$v1 = $value;
$v2 = $this->data[$this->name][ $compare_field ];
if($v1 !== $v2) {
return FALSE;
} else {
continue;
}
}
return TRUE;
}
}
In the code, the guy used a foreach to access an array member which he had its name already!
As far as I understand - it's a waste of resources and a bad(even strange) practice.
One more thing about the code:
I don't understand the usage of the continue there. it's a single field array, isn't it? the comparison should happen once and the loop will be over.
Please enlighten me.

In the code, the guy used a foreach to access an array member which he had its name already! As far as I understand - it's a waste of resources and a bad(even strange) practice.
The first parameter is always an array on one key and its value, the second parameter comes from the call to that function, in a block named as the key... So, all you need is to send the key and no need to iterate
The code uses foreach to iterate through $field, which is an array of one key value pair. It all starts when the validation routine invokes identicalFieldValues, passing it two values - $field, which would be an array that looks like:
array (
[email] => 'user entered value 1'
)
The second parameter $compare_field would be set to the string confirm_email.
In this particular case, it doesn't look like it makes a lot of sense to use foreach since your array only has one key-value pair. But you must write code this way because CakePHP will pass an array to the method.
I believe the reason why CakePHP does this is because an array is the only way to pass both the field name and its value. While in this case the field name (email) is irrelevant, you might need in other cases.
What you are seeing here is one of the caveats of using frameworks. Most of the time, they simplify your code. But sometimes you have to write code that you wouldn't write normally just so the framework is happy.
One more thing about the code: I don't understand the usage of the continue there. it's a single field array, isn't it? the comparison should happen once and the loop will be over. Please enlighten me.
Indeed. And since there are no statements in the foreach loop following continue, the whole else block could also be omitted.
A simplified version of this would be:
function identicalFieldValues($field=array(), $compare_field=null)
{
foreach ($field as $field) {
$compare = $this->data[$this->name][$compare_field];
if ($field !== $compare) {
return FALSE;
}
}
return TRUE;
}
And I agree with you, the loop only goes through one iteration when validating the email field. regardless of the field. You still need the foreach because you are getting an array though.

Related

Merge Laravel unique validations with array generic validations

I found a code snippet that solved the problem I had with uniqueness validation, when dealing with arrays:
$rules = [ // this ones are ok for all
'staff.*.name' => 'required|max:128',
'staff.*.description' => 'max:512',
'staff.*.anothervalue' => 'string',
];
// here loop through the staff array to add the ignore
foreach($request->staff as $key => $staff) {
if ( array_key_exists('id', $staff) && $staff['id'] ) { // if have an id, means an update, so add the id to ignore
$rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users,id,'.$staff['id']]);
} else { // just check if the email it's not unique
$rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users']);
}
}
Now I need to go a step further and avoid duplication of email values inside the same array (not in database), so I found the "distinct" rule in Laravel documentation. Also, there is the need for making the email values required only if some another value is present in the same array entry. I see that there is a "required_with" rule that comes handy in those situations. However, I can't find the way to combine the rules generated with the foreach loop with those that use the "wildcard" array notation.
Your suggestions will be appreciated.
~~You can mix and match.~~
Edit: Turns out you cannot.
So you would need to overwrite the rule each time, or set all the rules as per staff.*.email when keyed individually; or make some custom validation logic that throws a BadRequestHttpException/ValidationException or similar.
e.g.:
foreach ...
$rules['staff.'.$key.'.email' => 'required_with:staff.*.id|email|distinct|unique:users']);

Set multi-dimensional array through function parameters

I'm working on a simple session manager for my framework. Im trying to setup a more user friendly structure for the session data. Essentially my sessions are stored like this:
$app_name = "Some_App_Name";
$component = "notifications";
$key = "errors";
$value = "There was some error";
$_SESSION[$app_name][$component][$key] = $value;
The problem I am facing is creating this structure through parameters within the session class. I have a set method which should ideally set a session value. The $app_name as listed above is by default added to the session class through the constructor, but I need to find a simple way of taking the parameters passed in within the method and then creating the rest. A simple example:
// Where keys could be: $key1 = notifications, $key2 => "notices"
public static function set($key1,$key2,$value) {
$_SESSION[self::$app_name][$key1][$key2] = $value;
}
The above would work if I always have 4 parameters but in some cases I might only have 2 parameters. I could pass 2 parameters (both being an array) but I'm looking for a more streamlined approach (if such an approach exists).
With the creating of the structure and setting values I also need a similiar way of verifying if the value or last key exists:
// Where keys could be: $key1 = notifications, $key2 => "errors"
public static function exists($key1,$key2) {
if(isset($_SESSION[self::$app_name][$key1][$key2])) {
return true;
}
Any suggestions would greatly be appreciated.
$params = array(
"key1" => "value1",
"key2" => "value2",
"value" => "value"
);
public static function set($params = NULL) //default null if no value is passed
{
if (!self::exists($params)) return false;
$_SESSION[self::$app_name][$params["key1"]][$params["key2"]] = $value;
return true;
}
public static function exists($params = NULL)
{
if(isset($_SESSION[self::$app_name][$params["key1"]][$params["key2"]]))
{
return true;
}
return false;
}
In the light of assisting other members wanting to do something similiar, I want to strongly advise you against using this concept as off the bat it sounds like a good idea but your true issue comes with the management of the array itself. Working straight with the $_SESSION superglobal really is the more powerful option on the basis that:
Even with a parameter in place for say server and component ($_SESSION ['somename']['auth']), what happens when you want to access content from that level from another instance of the object? Say I have another session object instance for $_SESSION ['somename']['errors'] but need to access properties from $_SESSION ['somename']['auth'] but within scope my base within the session array is incorrect.
Adding properties is fine $this->session->add("key","name") but what if you want to append to that array (where name is actually an array and not just a value), or vise versa. Or checking for occurances if $_SESSION['somename']['auth']['key']['name'] actually has another key or value within it?
All and all having worked with this the last couple of days I can definately say that it might not be impossible to write a "fully functional" session manager class but at the end of the day for simplicity it's better to rather just work with the session array directly as it's less code and less issues as you go.

Database logging in Zend Framework 2: wrong "extra" column name

I want to save log entries to my MySQL database from Zend Framework 2. I am using Zend\Log\Logger with a Zend\Log\Writer\Db writer. By supplying the writer with an array, one can choose which columns to save what data to (e.g. timestamp into a "log_date" column) and which data to save. Here is what I am doing:
$logger = new Zend\Log\Logger();
$mapping = array(
'timestamp' => 'timestamp_column',
'priority' => 'priority_column',
'message' => 'message_column',
'extra' => 'extra_column'
);
$logger->addWriter(new Zend\Log\Writer\Db($dbAdapter, 'table_name', $mapping));
$logger->err('some message', array('some extra information'));
The problem I am facing is that the array of column names and their values contain an incorrect column name for the "extra" column. Based on the array above, it should be inserting the value "some extra information" into the "extra_column" column. The problem is that the Zend\Log\Writer\Db class is using the letter "e" as the name of the extra column. This comes from the first letter of "extra_column" in my array above. For some reason, it is taking the first letter of "extra_column" and using it as the column name instead of the entire value.
I took a look at the source code. The mapEventIntoColumn method is being used to get the column names and values as an array. I copied in the relevant part of the method below.
// Example:
// $event = array('extra' => array(0 => 'some extra information'));
// $columnMap = array('extra' => 'extra_column');
// Return: array('e' => 'some extra information')
// Expected (without looking at the code below): array('extra_column' => 'some extra information')
protected function mapEventIntoColumn(array $event, array $columnMap = null) {
$data = array();
foreach ($event as $name => $value) {
if (is_array($value)) {
foreach ($value as $key => $subvalue) {
if (isset($columnMap[$name][$key])) {
$data[$columnMap[$name][$key]] = $subvalue;
}
}
}
}
return $data;
}
The $event parameter is an array containing the same keys as my $mapping array in my first code snippet and the values for the log message. The $columnMap parameter is the $mapping array from my first code snippet (array values are column names).
What actually seems to happen is that because I am passing in extra information as an array (this is required), the inner foreach loop is executed. Here, $key is 0 (the index) so it is actually doing like this: $columnMap['extra'][0]. This gives the letter "e" (the first letter in "extra_column"), which is used as the column name, where it should be the entire column name instead.
I tried to supply my own key in the extra array when calling the log method, but the same happens. The official documentation shows no examples of usage of the extra parameter. I want to insert information that can help me debug errors into my table, so I would like to use it.
Is this a bug or am I missing something? It seems really strange to me! I hope I explained it well enough - it is quite tricky!
Since Daniel M has not yet posted his comment as an answer, I will refer you to his comment which solved the problem.

PHP Optimizing a Very Long Switch Case Statement

Please have a look to below code
function GetAreaName($AreaCode)
{
switch ($AreaCode)
{
case 201: return 'New Jersey';
case 202: return 'Washington';
// this goes on till
case 999: return '';
}
}
Let's say if the AreaCode is 998 then it would have to go through so many cases!
How could we optimize this function? (No using databases.)
I'm thinking to build an array and do a binary search over it? But this means every time the function is called the array will be rebuild? How do we build the array once, cache it and re-use every time this function is called?
Why not just use a hash table?
class Area {
private $areaCodes = array(
201 => 'New Jersey',
202 => 'Washington',
// this goes on till
999 => '';
);
function getStateByAreaCode ($areaCode) {
if (array_key_exists($areaCode, $this->areaCodes)) {
return $this->areaCodes[$areaCode];
} else {
return false;
}
}
}
Call it like this:
$area = new Area();
$city = $area->getStateByAreaCode(303);
Just save your class in a file and include it when you need it.
You Asked How to Prevent the Array From Being Created Every Request:
By putting this in a class you at least keep it clean. It technically still gets created each request, but unless your array is enormous (WAY bigger than the area codes in the U.S.) it shouldn't pose a performance issue. If you are worried about building the array every time you have a request, then take a look at a code optimizer like APC or the Zend Optimizer. This essentially takes the byte code that PHP generates at run time and caches it.
Sounds like you should just store it in your database.
But if you can't do that, either abstract it into a config file of some kind and store it in some kind of persisted object, or just use a static variable:
function foo($key) {
static $cache = array(1 => 'abc', 2 => 'def', 3 => 'ghi');
if (array_key_exists($key, $cache)) {
return $cache[$key];
} else {
//Somehow signal an error (throw an exception, return boolean false, or something)
}
}
In the above, $cache would only exist once. (If you knew that the values would never be null, you could use isset instead of array_key_exists.)
This isn't very flexible though since changing the data requires you to edit your code. You typically want your data and your code to be decoupled.
That could mean storing it in some kind of file (json, xml, php, whatever), and loading it into some kind of structure that you only create once. You would then pass that object or array around wherever it was needed. (Or, if you wanted to be hacky, you could use a static class. I suggest against this though.)
Switch condition is evaluated once only:
In a switch statement, the condition is evaluated only once and the result is compared to each case statement. In an elseif statement, the condition is evaluated again. If your condition is more complicated than a simple compare and/or is in a tight loop, a switch may be faster. ➫➫➫
There is no optimization required. However, read:
In PHP what's faster, big Switch statement, or Array key lookup
If you want to build a config file, you can consider something like:
$areas = array
(
1 => 'abc',
2 => 'def',
..
);
Then simply compare:
if (!isset($areas[$some_code]))
{
// do something
}
else
{
// ok
}
Try below pseudo code
$areas = array('201' => 'New Jersey',
'202' => 'Washington',
......
........
'999' => '');
function GetAreaName($AreaCode)
{
if(isset($areas[$AreaCode])) {
return $areas[$AreaCode];
} else {
// do something
}
}

Update database field with array in CodeIgniter

Again I find myself at the mercy of the stackoverflow community!
I've gone over to use CodeIgniter for my PHP projects and it's been a breeze so far, hoever I've gotten stuck trying to update a database field with some post data.
My array is the usual: array(name => value, name => value, name => value);
which again is populated from the submitted $_POST data.
Similarly to the array, I have a database table with 2 fields: setting and value, where the names under setting corresponds to the array keys and value to the array keys' value.
(Did I explain that right?)
Nonetheless, I've been trying for a little while now to get this to work as it should, but, I'm really just waving my hands around in the dark.
I hope some of you bright minds out there can help me with this annoying issue!
Edit:
Thanks to everyone who replied! I managed to produce the result that I wanted with the following code:
foreach ($form_data as $key => $val)
{
$this->db->where ('setting', $key);
$this->db->set ('value', $val);
$this->db->update ('recruitment');
}
Now, I tried following up with this by adding:
if ($this->db->affected_rows() >= '1') { return true; }
return false;
To my model, and
if ($this->RecruitmentSettings->Update($form_data) == TRUE)
{
redirect('recruitment/success');
}
to my controller, but it's not working as expected at all. Any ideas what I'm doing wrong?
There are a lot of questions here. Do you already have values in the database and you want to update them? Or do you want to put in new data every time? The answer depends on that.
What you want is the insert_batch() or update_batch() methods of the active record class (if that's what you're using for the db).
foreach($post_array as $key => $value)
{
$settings[] = array(
'setting' => $key,
'value' => $value
);
}
$this->db->insert_batch('your_db_table', $settings);
OR, for updates:
$this->db->update_batch('your_db_table', $settings, 'setting');
You could do a query to check for settings and do insert_batch or update_batch depending on if there are results. If you wanted to insert every time, you could delete the rows in the table before you do the insert. I wouldn't do that without a transaction, however.
So you want to store the array data in the database? You could do this
Model
foreach ($data as $key => $item)
{
$this->db->set ('setting', $key);
$this->db->set ('value', $item);
$this->db->insert ('table_name');
}
return ($this->db->affected_rows() > 0);
Controller
if ($this->RecruitmentSettings->Update($form_data))
{
redirect('recruitment/success');
}
else
{
echo "error";
}

Categories