I'm getting a little confused with what exactly cakePHPs Sanitize::clean() method should do. Currently when I'm adding a record I'm doing this:
$this->request->data = Sanitize::clean($this->request->data, array('encode' => true, 'remove_html' => true));
However, this still leaves & and \n in my database when they use & and press enter in a textarea. How can I stop this? I thought remove_html => true would have done this?
Do I need to go as far as doing a str_replace()
Also some of the records with the \n 's in them have hundreds of trailing backslashes meaning the break any views they are displayed on.
Could someone point me in the right direction? Thanks
Update as per Nunsers answer
I've now added the following after the clean:
foreach ($this->request->data['Expense'] as &$expense) {
$expense['detail'] = Sanitize::stripWhitespace($expense['detail']);
}
unset($expense);
However, it does remove whitespace but still leaves lots of \n\n\n\n\n\
Heres a debug of $this->request->data:
array(
'submit' => 'Save',
'Expense' => array(
(int) 0 => array(
'date' => array(
'day' => '27',
'month' => '06',
'year' => '2013'
),
'sitename' => 'test',
'detail' => 'test test\n\ntest\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n',
'ExpenseCode' => array(
'ExpenseCode' => '1'
),
'billable' => '1',
'amount' => '1',
'miles' => '',
'total' => '1',
'billable_mileage' => '',
'recorded_global_mileage_rate' => '0.4'
),
(int) 1 => array(
'date' => array(
'day' => '27',
'month' => '06',
'year' => '2013'
),
'sitename' => 'test',
'detail' => '\n\n\n\n\n\n\n\n\n\n\n\n\n\ntest',
'ExpenseCode' => array(
'ExpenseCode' => '4'
),
'billable' => '1',
'amount' => '10',
'miles' => '',
'total' => '10',
'billable_mileage' => '',
'recorded_global_mileage_rate' => '0.4'
)
),
'CashFloat' => array(
'amount' => ''
),
'ExpenseClaim' => array(
'user_id' => '3',
'claim_status_id' => '1'
)
)
I'd like to strip thouse \n's out really as I dont want them break views.
More results
Even when I cut out the cake function and use its code directly inline via :
$expense['detail'] = preg_replace('/\s{2,}/u', ' ', preg_replace('/[\n\r\t]+/', '', $expense['detail']));
I still get the same (debug($expense['detail']) from the loop:
'test 10 spaces before this then pressing enter lots \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
I've also tried just a basic trim() which didnt work at all.
Working solution (apart from the &)
This will remove any number of
\n
from the string
foreach ($this->request->data['Expense'] as &$expense) {
$expense['detail'] = str_replace("\\n", ' ', $expense['detail']);
$expense['detail'] = Sanitize::stripWhitespace($expense['detail']);
}
// Unset referance var from above loop
unset($expense);
Decided to keep the &
And just use html_entity_decode() when echoing it out in a view
Hope that helps someone!
According to the docs, the clean method does not do what you want.
First, for the \n characters, you should call another Sanitize function. Sanitize::clean() has a carriage option, but that only removes the \r characters. So, after (or before) calling the clean method, call stripWhitespaces. Unfortunately, this functions only receives a string, so you need to call it in a loop (or if you know the string you want to sanitize, use it for just that).
$cleanOfSpacesString = Sanitize::stripWhitespace($dirtyString);
That function removes this characters: \r, \n and \t, and replace 2 or more spaces with just one.
And for &, the docs says that remove_html removes html tags and also does a htmlentities($string, ENT_QUOTES, $defaultCharset, true) to the string. So if that htmlentities is not working for you, you'll have to use another function not incuded in the Sanitize class.
If you think this behaviour is something you want to have inside the helper, extend the Sanitize helper to include stripWhiteSpaces inside the clean function and to also replace the htmlentities function with something that works for you. If it's just for this case, add those functions in the controller, after calling Sanitize::clean
Update as per Jason's update to my answer
I found it weird that the stripWhitespaces function didn't work as you wanted. And here is the explanation of why I think it didn't.
You want to remove \n from a string like this
'test test\n\ntest\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
and the preg_replace inside stripWhitespaces is this
preg_replace('/[\n\r\t]+/', '', $str)
That regex will remove newlines, returns and tabs, but on a string like this
'test test
test
' //and a lot more newlines
So the difference here is that you're passing the newlines (maybe the tabs and returns also, I don't know) as strings, real\+n, and so, the preg_replace cake does is not useful for you. I know you found a solution for that (I recommend you add the tab and return match also in there), but I wanted to clarify why cake's function didn't work in case someone else has a similar problem.
Related
print array
array(
'Order' => array(
'id' => '1',
'base_price' => '65',
'min_price' => '95',
)
)
Is it possible to remove the key('Order') when you retrieving data? if Not how can I use array_shift or end in one line and to prevent below error?
I am getting this error Only variables should be passed by reference when I remove the key from array.
$orders = array_shift or end ($this->Order->read(null, $id));
debug($orders);
You want only id from it then following code will help you
$arrOrderId=Set::extract("/Order/id",$data);
here $data is your array from where you want to delete this "Order" key.
You will get following array when you do debug($arrOrderId);
[0]=>1
if you want base_price then write following code
$arrOrderId=Set::extract("/Order/base_price",$data);
You can use Set functions for manipulating arrays:
Set::extract($array, 'Order');
Will output:
array(
'id' => '1',
'base_price' => '65',
'min_price' => '95',
)
If you need to do this on every output, you can override afterFind() method on your model.
Please see the docs:
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::extract
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::classicExtract
I have a form for creating a task, and when creating it, user is asked to select which employees will be assigned to it. There may be just one employee or even up to 10. I allow user to dynamically create those input fields on the go, but the array that i get after the form submition looks like this:
array(
'Event' => array(
'project_id' => '62',
'user_id' => '23',
'user_id2' => '24',
'user_id4' => '28',
'user_id8' => '30',
'hours' => '6',
'minutes' => '0',
'assignment' => '',
'material' => 'safsaf',
'date' => '2013-10-12',
)
)
The problem is I do not know how to iterate over the user_ids.
Is it possible to save the IDs as a list? Or is there any other solution?
Use CakePHP's find('list') to retrieve the $users in an key=>value array, then set the multiple attribute of the input to true:
echo $this->Form->select('Model.field', $users, array('multiple' => true));
$attributes['multiple'] If ‘multiple’ has been set to true for an
input that outputs a select, the select will allow multiple
selections:
A model that I am using with both TranslateBehavior and TreeBehavior is failing to save. I suspect it is something to do with one of these behaviors. Here's the data going in:
array(
'Category' => array(
'id' => '3252',
'parent_id' => null,
'sort_order' => '0',
'name' => array(
'eng' => 'english name',
'fra' => 'french name',
'deu' => 'german name',
'ita' => 'italian name',
'spa' => 'spanish name'
),
)
)
And $this->Category->validationErrors is:
array(
'parent_id' => array(),
'sort_order' => array()
)
There are no validation rules defined in the model. There is no beforeSave() or beforeValidate() method defined.
I am not getting any validation errors displayed on screen, although all fields have the red "error" outline.
Edit - the save operation is not getting as far as Category::beforeSave(). I created that function and it does not get run. It gets as far as Category::beforeValidate().
$validates = $this->Category->validates($this->request->data);
debug($validates);
$saved = $this->Category->saveAll($this->request->data);
debug($saved);
In the above code, $validates is true, $saved is false. Category beforeSave is not called at any point in the above process. The validation seems to fail on the call to saveAll(). I need to use saveAll rather than save to save all translations at once (I am doing this elsewhere with another model with no problems).
So, after a while debugging I have found the problem:
public $hasMany = array(
'Category' => array('className' => 'Category', 'foreignKey' => 'parent_id', 'counterCache' => true),
...
I have no idea why I wrote this - I should have been aware that it was going to cause problems, I think I meant to write...
public $hasMany = array(
'Children' => array('className' => 'Category', 'foreignKey' => 'parent_id', 'counterCache' => true),
...
Anyway, changed it to the latter and these errors have gone.
Maybe it doesn't like the null and zero value of parent_id and sort_order? Also in the database what are their field types set as? Do they allow null values? etc. I'm guessing that as there are no validation rules in the model or parent/App model, then it must be some default validation with cake's lib linking to the database/mysql table itself. So I would check the Categories table structure for the parent_id and sort_order fields.
I have built an invoice system in my new CakePHP site and I am currently trying to build a function that will let me save the re-edited information back into the same records.
Below is the section of code I am working on,
$InvoiceDataVar = $this->data['Invoicedata'];
foreach ($InvoiceDataVar as $DataKey => $DataValue) {
$UpdateWork = $InvoiceDataVar[$DataKey]['workdes'];
debug($UpdateWork);
$this->Invoicedata->updateAll(
array('Invoicedata.workdes' => "'$UpdateWork'"),
array('Invoicedata.invoicejoblists_id' => $InvoiceID)
);
}
This is work $this->data holds,
'Invoicedata' => array(
(int) 0 => array(
'workdes' => 'test new update 1',
'price' => '500.00'
),
(int) 1 => array(
'workdes' => 'test new update 2',
'price' => '501.00'
),
(int) 2 => array(
'workdes' => 'test new update 3',
'price' => '503.00'
),
(int) 3 => array(
'workdes' => 'test new update 4',
'price' => '504.00'
),
(int) 4 => array(
'workdes' => 'test new update 5',
'price' => '505.00'
),
(int) 5 => array(
'workdes' => 'test new update 6',
'price' => '999.99'
)
)
These are of course just test data, but unless I am not understanding the UpdateAll function documentation which I have gone over many times, all this will do is update all the six fields with the last test input. Right now all the fields for the workdes contain, 'test new update 6'.
I should also say that there might not be all ways be six fields, there will always be at lest one, with a max (at this time) of 6 inputs to update.
I was thinking about loading the 'ID of the invoicedata table, which is an auto count number, where as the joblist_id is the same id in all the the fields being updated? This is my current line of thinking....
Any Help is most welcome.
Glenn.
It seems you are working with CakePHP 1.3
To udpate many records at once there is a shortcut way shown in documentation
First you need to include ids of Invoicedata model in the post data
'Invoicedata' => array(
(int) 0 => array(
'id' => 23, // include id of the record
'workdes' => 'test new update 1',
'price' => '500.00'
),
// other records
)
// then call save all function
$this->Invoicedata->saveAll($this->data['Invoicedata']);
Please, check: updateAll documentation you can see, that first parameter is $fields, which fields you wanto to update and their appropriate values, and second parameter is $conditions some restrictions which define scope of rows which will be changed. You have empty $conditions, so, all rows will be updated. Last values ( here you need to check source code of DataSource update method), but think that it produce some think like
UPDATE invoicedatas SET workdes = value1, price = value2, workdes = value3, price = value4 and etc
so last values will be used.
Your data arrays should contain id attribute, to update right records and for this you should use save or saveAll methods of Invoicedata model.
I'm having difficulty understanding the syntax of Hash::extract when dealing with HABTM.
I have data coming back from a find() that looks like this:
array(
(int) 0 => array(
'EventsGroup' => array(
'id' => '34',
'event_id' => '5',
'group_id' => '1'
)
),
(int) 1 => array(
'EventsGroup' => array(
'id' => '29',
'event_id' => '2',
'group_id' => '1'
)
)
)
I'm trying to get to an array that looks like: array(x,y,z) where x,y,z are event_id's.
The Cake documentation's example looks like:
$users = $this->User->find("all");
$results = Hash::extract($users, '{n}.User.id');
Based on that, I tried:
$eventsGroups = $this->EventsGroup->findAllByGroupId($groupid);
$secEvents = Hash::extract($eventsGroups, '{n}.{EventsGroup}.event_id' );
$secEvents2 = Hash::extract($eventsGroups, '{n}.EventsGroup.event_id' );
$secEvents3 = Hash::extract($eventsGroups, '{n}.[text=EventsGroup].event_id);
None of which worked.
I found a way to get what I wanted without using Hash::extract, but I'd like to use it as some of the other methods will be useful to me down the road.
Any help or pointers would be greatly appreciated!
Thanks
Have you tried:
$eventsGroups = $this->EventsGroup->findAllByGroupId($groupid);
$secEvents = Hash::extract($eventsGroups, '{n}.EventsGroup[event_id]' );
As the docs says to retrieve a field you should use a matcher and not a expression. And the brackets ({}) are not needed, they are used to build expressions like '{n}.{s}'...