What am I trying to do?
I have three fields (1 hidden, an id) and the user must complete one of the other two in order to pass validation.
So the user should fail validation if both fields are empty, but pass if one is completed.
1 2 3
A 0 B True
A B 0 True
A 0 0 False
I'm using CakePHP v2.1.3 so have access to the new validation rule enhancements.
The problem
I can't seem to find a reliable way to check both fields at the same time. I have so far tried looking at $this->data from the model and have found that validation is only passing a single instance of the data at a time. So there doesn't seem to be a way to compare the fields.
What I have so far
/**
* Custom validation to see if either of the two fields are set, if neither are, then we fail, if at least one is, we pass
* #param array $check
* #return boolean
*/
public function checkAttributes($check){
var_dump($check);
var_dump($this->data);
echo "<hr>";
// Check for an id value picked from a list
if(#is_numeric($check['attribute_value_id']) && isset($this->data['AdvertAttributeValue']['attribute_value_id'])){
return true;
}
// Check for a date value selected
if(#is_array($check['attribute_value_text']) && isset($this->data['AdvertAttributeValue']['attribute_value_text'])){
return true;
}
// Check for a text value
if(#is_string($check['attribute_value_text']) && isset($this->data['AdvertAttributeValue']['attribute_value_text'])){
return true;
}
return false;
}
This doesn't seem to do the trick as I think it can't check $this->data because the instance of it doesn't contain all the relevant fields.
The data
I should also mention that I am passing a large numeric array in. So these fields appear multiple times on the page, currently 12 dimensions. So accessing them directly through $this->data will be hard as they are not named dimensions, but are $this->data['Model'][<num>]['field'] = value
Validation
public $validate = array(
'attribute_value_id'=>array(
'notempty'=>array(
'rule'=>'checkAttributes',
'message'=>'Please select a value for your attribute',
'required'=>true,
),
),
'attribute_value_text'=>array(
'notempty'=>array(
'rule'=>'checkAttributes',
'message'=>'You must enter text for this attribute',
'required'=>true,
),
)
);
Data dump
Here I'll show the output of the var_dump() above. I have two validation rules in my Model, one for attribute_value_id and also one for attribute_value_text
// An id field selected from a list
array // $check
'attribute_value_id' => string '1' (length=1)
array // $this->data
'AdvertAttributeValue' =>
array
'attribute_value_id' => string '1' (length=1)
'id' => string '' (length=0)
// A text field
// Validating first time around on the id field
array // $check
'attribute_value_id' => string '' (length=0)
array // $this->data
'AdvertAttributeValue' =>
array
'attribute_value_id' => string '' (length=0)
'id' => string '' (length=0)
'attribute_value_text' => string '50' (length=2)
// Validating second time around on the text field
array // $check
'attribute_value_text' => string '50' (length=2)
array // $this->data
'AdvertAttributeValue' =>
array
'attribute_value_id' => string '' (length=0)
'id' => string '' (length=0)
'attribute_value_text' => string '50' (length=2)
// A date field
array // $check
'attribute_value_id' => string '' (length=0)
array // $this->data
'AdvertAttributeValue' =>
array
'attribute_value_id' => string '' (length=0)
'id' => string '' (length=0)
'attribute_value_text' =>
array
'month' => string '06' (length=2)
'day' => string '28' (length=2)
'year' => string '2012' (length=4)
The saveAll() method just calls saveMany() or saveAssociated() as appropriate. These methods will, by default, attempt to validate all of the data before saving any of it (via a call to validateMany()). However, as you can see in the source, that function validates each item individually, so the validation code won't have access to other records.
As I understand it, you need to cross-validate between multiple records before you save them. Although I've never done that, it sounds like a case for validation in the controller. You can make calls to Model::validate() or Model::validateAll() to ensure internal consistency of records. Then your controller can also implement whatever logic is necessary for your cross-record validation. Once that's done, you can make the save call with validation disabled:
$this->myModel->saveAll($this->request->data, array('validate'=>false));
Note that before you do any of this, you'll have to set your data to the model:
$this->myModel->set($this->request->data);
I realize this puts a lot of extra code in the controller that should ideally be in the model. I suppose it's possible that it could be done via one of the model callbacks, but I'm not sure how.
You can make use of Model::$data or Model::beforeValidate().
Related
How can I Iterate over this ridiculously tedious array?
array (size=6)
0 =>
array (size=1)
'Question' =>
array (size=2)
'id' => string 'q_1' (length=3)
'question_desc' => string 'Is this correct?)' (length=15)
1 =>
array (size=1)
'Question' =>
array (size=2)
'id' => string 'q_10' (length=4)
'question_desc' => string 'Do you weigh less than 45 kilograms OR more than 160 kilograms.' (length=63)
This is a var_dump from a Session data! I need to get the question_desc field from each 'Question' array object.
This array has a purpose to its structure, but I understand your frustration as I shared it before I rtfm-ed!
$flattened_data = array();
foreach($your_main_array as $question)
{
foreach($question['Question'] as $question_param)
{
if($question_param == 'question_desc')
{
$flattened_data[] = $question_param;
// if you want to be really cool you can do this instead
// this will list the array with the question id as the key.
// $flattened_data[$question[id]] = $question_param;
}
}
}
// now flattened data has only what you require
return $flattened_data;
Cakes data form makes a lot more sense once you understand its ORM and how it uses model relations. Its actually a powerful tool for managing your data, but before you need all of that power it does seem like an encumbrance for simple tasks.
This should be a simple thing to solve...
I have got these estatements when a user logs in:
$query = $conn->prepare("SELECT * FROM admins WHERE username = :user AND password = :pass");
$query->bindValue(":user", $user);
$query->bindValue(":pass", md5($pass));
$query->execute();
Now if the user exists, I return the user information from the database, but if not, I return false instead...
if($query->rowCount() > 0) {
$admininfo = $query->fetchAll(PDO::FETCH_ASSOC);
} else {
return false;
}
But when I var_dump the variable $admininfo I get an array with a number key before the actual array... like this:
array (size=1)
0 => <---- I DONT KNOW WHERE THIS COME FROM. I USED PDO::FETCH_ASSOC
array (size=9)
'id' => string '1' (length=1)
'username' => string 'admin' (length=5)
'password' => string '21232f297a57a5a743894a0e4a801fc3' (length=32)
'permissionid' => string '1' (length=1)
'name' => string 'Administrador' (length=13)
'lastname' => string 'Softing' (length=7)
'phonenumber' => null
'cellphonenumber' => null
'info' => null
I will put this information inside the SESSION array, so I want to access it by $_SESSION["admininfo"]["username"] or $_SESSION["admininfo"]["phonenumber"]
but I have to put something like this instead: $_SESSION["admininfo"][0]["phonenumber"].
How can I remove that 0 from the keys? Thank you!
According to the documentation for fetchAll, it will return an array of elements. This is because it is getting all of the rows in the table that match your criteria. In your case, you are only getting back one row, located at index 0. If you know you only want one result, use fetch instead of fetchAll. It will get rid of the 0.
You're using fetchAll. This will give you an array of DB rows -- of all the rows that were matched.
Use fetch in a while loop to go through individual rows one by one.
I'm having a strange thing in cakephp. So I try to explain it first.
I'm having:
two tables (staff and staffgroups)
a groupleader per group which is a staff
a virtual field, which CONCATs first and last name
the whish to order the list by lastname
Staff/model
public $virtualFields = array(
[...] ,
'fullnameForList' => 'CONCAT(Staff.lastname, ", ", Staff.firstname)',
[...]
function in Staffgroup/controller
public function getGroupLeaderListArray(){
$this->loadModel('Staff');
$loc_list = $this->Staff->find("list",
array("fields" => array("id", 'fullnameForList')),
array("order" => array("Staff.lastname ASC" ))
);
$this->set('GroupLeaderList',$loc_list);
}
outputs of the list
When I'm having the function getGroupLeaderListArray as it is above, i get the content of the virtualfield fullnameForList, but its not in order. eg. Lastname, Firstname
When I'm putting the array order before fields, it is shown in order but the wrong field. eg. Firstname Lastname
how do I get the list with the virtual field and correct order. I don't know further and I'm gaining weight and loosing hairs because of this problem. help apreciated big time!
cheers endo
You are using invalid array structures here. you need to pay attention on how arrays are working in cake. One array with named keys (you have multiple arrays here) as second param for find(), for example.
find($type, $options);
So its:
$locList = $this->Staff->find("list",
array(
'fields' => array('id', 'fullnameForList'),
'order' => array('Staff.lastname ASC')
)
);
I have this $data array: (built on a shell, not a form)
(debugged here)
array(
(int) 0 => array(
(int) 0 => 's511013t',
(int) 1 => 'id3422',
(int) 2 => '1'
),
(int) 1 => array(
(int) 0 => 's511013t',
(int) 1 => 'id3637',
(int) 2 => '1'
)
)
And using saveMany :
$this->Dir->saveMany($data, array( 'validate' => 'false', 'fieldList' => array('name','dir_dataname', 'project_id')));
Saving fails with no error.
I'm not sure if my $data array is well formatted, (I'm confused whether it should have another level) I built it from sql selects, etc. However it does contain all info I need saved, single model.
I'm running all this from a Shell and it does work to save a single record provided the field names everytime:
// this works
$this->Dir->save(array('name' => $data[0][0], 'project_id' => $data[0][2], 'dir_dataname' => $data[0][1]));
Already read Saving your data, and I'd really like to use saveMany and a fieldList due to my custom $data format. (I wouldn't like to have to insert field names on my $data).
(no sql_dump to show since is pretty cumbersome to get it from a shell task)
I've spent all evening trying to figure it out, can you point me in the right direction, Please?
IMHO, the keys in each arrays are not valid fields in your database table. They should represent the same name as your table field.
When you build the array from sql, the output should look like these - an associative array:
array(
(int) 0 => array(
(string) name => 's511013t',
(string) dir_dataname => 'id3422',
(string) project_id => '1'
),
(int) 1 => array(
(string) name => 's511013t',
(string) dir_dataname => 'id3637',
(string) project_id => '1'
)
)
Cake2.0 Docs
$this->Dir->saveMany($data);
You can get the log via
debug($this->Dir->getDataSource()->getLog());
It looks as if you are using fieldList incorrectly. fieldList is a list of fields that are to be whitelisted for saving to the database, not a "mapping" like you are using.
You need to specify field => value pairs in the array for each record, not numerical indexes. I may be wrong, but I've never seen that and it doesn't look to be that way according to docs.
I have two tables,Forms and Attributes. I'm tring to retrieve the last inserted id from the Forms table and insert it to the form_id column of the Attributes table, along with the other field columns.
Earlier I retrieved the form Id from the Forms table and used it to update the value of the Form name column. It worked fine.The code for that is given below:
function saveFormName($data)
{
$this->data['Form']['formname']=$data['Form']['formname'];
$this->data['Form']['id']=$this->find('all', array(
'fields' => array('Form.id'),
'order' => 'Form.id DESC'
));
$this->id=$this->data['Form']['id'][0];
$this->saveField('name',$this->data['Form']['formname']);
}
But When I tried to do it in a similar way for updating the attributes table,the row is not saved in the database,since the value of $this->data['Form']['id'][0] is an 'Array'.
Even in the saveFormName function, the value of $this->data['Form']['id'][0] is an 'Array',but the form name gets updated correctly. Someone explain me the concept.
function saveFieldEntries($data)
{
$this->data['Form']['id']=$this->find('all', array(
'fields' => array('Form.id'),
'order' => 'Form.id DESC'
));
$this->data['Attribute']['form_id'] = $this->data['Form']['id'][0];
$this->data['Attribute']['label']= 'Label';
$this->data['Attribute']['size']='20';
$this->data['Attribute']['type']=$data['Attribute']['type'];
$this->data['Attribute']['sequence_no'] = $data['Attribute']['sequence_no'];
$this->Attribute->save($this->data);
}
EDIT:
Ok, here is the corresponding code in the controller.
function insertFormName()
{
$this->data['Form']['formname']=$this->params['form']['formname'];
$this->Form->saveFormName($this->data);
}
function insertFieldEntry()
{
$this->data['Attribute']['type']=$this->params['form']['type'];
$this->data['Attribute']['sequence_no'] = $this->params['form']['sequence_no'];
$this->Form->saveFieldEntries($this->data);
}
The parameters form name,type and sequence no are passed to the controller from the corresponding view file.
$this->data['Form']['id'][0] holds an array because find('all') returns an array.
So if you need first ID from this array, you need to pick it properly in function saveFieldEntries:
...
$this->data['Attribute']['form_id'] = $this->data['Form']['id'][0]['Form']['id'];
...