I'm trying to add a repeat function to my Events/add.
So if I have the same Event from Monday to Friday (or even more days) the user can add everything in a single add, instead than one by one.
I added an input field in Events/add.ctp which is:
$this->Form->input('repeat', array('type' => 'number'));
to compare it in a do - while loop in beforeSave()
if(!empty($this->data[$this->alias]['repeat'])) {
$repeat = $this->data['Event']['repeat'];
$i = 0;
do {
$start = strtotime($this->data[$this->alias]['start'] . ' +1 day');
$end = strtotime($this->data[$this->alias]['end'] . ' +1 day');
$this->data[$this->alias]['start'] = date('Y-m-d H:i:s', $start);
$this->data[$this->alias]['end'] = date('Y-m-d H:i:s', $end);
$this->create();
$this->save($this->data);
//i think here's the problem... this shouldn't be done here right?
$i++;
} while ($repeat >= $i);
}
I ran out of ideas on how to make this possible..
I know that I have to use beforeSave to handle data after the add action, but obviously I cannot save data in before save... any hints?
Definitely don't do it in beforeSave. As you said, is kind of weird having a save inside a beforeSave... Hasn't it give you loop problems even? Because you do a save inside the loop, and $this->data keeps having the same repeat value inside... it will be repeating forever and spaming that poor events table.
No, definitely get that loop out of there.
I think the best way to do this is to add a new function in your model
public function saveRepeating($data) {
if(!empty($data[$this->alias]['repeat'])) {
$repeat = $data['Event']['repeat'];
unset($data['Event']['repeat']); //prevent loops!
$i = 0;
do {
$start = strtotime($data[$this->alias]['start'] . ' +1 day');
$end = strtotime($data[$this->alias]['end'] . ' +1 day');
$data[$this->alias]['start'] = date('Y-m-d H:i:s', $start);
$data[$this->alias]['end'] = date('Y-m-d H:i:s', $end);
$this->create();
$this->save($data);
$i++;
} while ($repeat >= $i);
}
}
And in your controller, call that function instead of save with
$this->Model->saveRepeating($data);
Notice I didn't really change anything inside your loop, just $this->data for $data. But having it as a separate function avoids any weird problems with beforeSave. Oh, and I added an unset to the repeat variable to prevent any loop (delete that unset if you want to save the repeat value to a table, though). You can add a return value to validate that every event got saved, or handle exceptions, etc.
Remember to return the beforeSave as it was before!
Related
Inside a cell I need to access the TreeOptions model.
So I've wrote this :
$this->loadModel( 'TreeOptions' );
$i = $this->TreeOptions->find( 'all' );
But when I do the foreach like this :
foreach( $i as $row )
debug( $row->description );
It only returns the last record of the result.
The only way I've found to make it work as desired is adding the limit clause :
$i = $this->TreeOptions->find( 'all', [ 'limit' => 200 ] );
And then, I can get the whole set of records.
What am I missing ?
Thanks.
Regards.
In your first snippet, the variable $i, is a state where the query has not yet run. See the excerpt from CakePHP 3 Cookbook: Retrieving Data & Results — Using Finders to Load Data:
// Find all the articles.
// At this point the query has not run.
$query = $articles->find('all');
// Iteration will execute the query.
foreach ($query as $row) {
}
// Calling all() will execute the query
// and return the result set.
$results = $query->all();
// Once we have a result set we can get all the rows
$data = $results->toArray();
// Converting the query to an array will execute it.
$results = $query->toArray();
I have a problem, right now Im using this foreach loop on CakePhp on which I want to add all the values which are still not on the table for the respecting user. To give a little more context, the user has a menu. And the admin can select which one to add for the user to use. On the next code I receive a array with the menus which will be added as so:
//This is what comes on the ['UserMenuAccessibility'] array:
Array ( [menu_accessibility_id2] => 2 [menu_accessibility_id3] => 3 [menu_accessibility_id4] => 4 [menu_accessibility_id5] => 5 [menu_accessibility_id8] => 8 )
I get the ids of the menus which want to be added to the table for the user to use. And I use the next code to add the menus to the table if they are not there still:
//I check if the array has something cause it can come with no ids.
if (!(isset($this->request->data['UserMenuAccessibility']))) {
$this->request->data['UserMenuAccessibility'] = array();
}
$UserMenuAccessibility = $this->request->data['UserMenuAccessibility'];
foreach ($UserMenuAccessibility as $key => $value) {
$conditions = array(
'UserMenuAccessibility.menu_accessibility_id' => $value,
'UserMenuAccessibility.users_id' => $id
);
if ($this->User->UserMenuAccessibility->hasAny($conditions)) {
} else {
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
For some reason the array is only saving the last new id which is not on the table and not the rest. For example if I have menu 1 and 2 and add 3 and 4 only 4 gets added to the table. For some reason I cant add all the missing menu ids to the table. Any ideas why this is happening?
Thanks for the help on advance.
It looks like your code will save each item, but each call to save() is overwriting the last entry added as $this->User->UserMenuAccessibility->id is set after the first save and will be used for subsequent saves. Try calling $this->User->UserMenuAccessibility->create() before each save to ensure that the model data is reset and ready to accept new data:-
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
$this->User->UserMenuAccessibility->create();
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
}
In cakephp 2.0 $this->Model->create() create work fine. But if you are using cakephp version 3 or greater then 3. Then follow the below code
$saveData['itemId'] = 1;
$saveData['qty'] = 2;
$saveData['type'] = '0';
$saveData['status'] = 'active';
$saveData = $this->Model->newEntity($saveData);
$this->Model->save($materialmismatch);
In normal case we use patchEntity
$this->Model->patchEntity($saveData, $this->request->data);
It will only save last values of array so you have to use newEntity() with data
In cakephp3, patchEntity() is normally used. However, when using it for inserting-new/updating entries in a foreach loop, I too saw that it only saves the last element of the array.
What worked for me was using patchEntities(), which as explained in the patchEntity() doc, is used for patching multiple entities at once.
So simplifying and going by the original code sample to handle multiple entities, it could be:
$userMenuAccessibilityObject = TableRegistry::get('UserMenuAccessibility');
foreach ($UserMenuAccessibility as $key => $value) {
$userMenuAccessibility = $userMenuAccessibilityObject->get($value);//get original individual entity if exists
$userMenuAccessibilities[] = $userMenuAccessibility;
$dataToPatch = [
'menu_accessibility_id' => $value,
'users_id' => $id
]//store corresponding entity data in array for patching after foreach
$userMenuAccessibilitiesData[] = $dataToPatch;
}
$userMenuAccessibilities = $userMenuAccessibilityObject->patchEntities($userMenuAccessibilities, $userMenuAccessibilities);
if ($userMenuAccessibilityObject->saveMany($requisitions)) {
} else {
$this->Session->setFlash(__('The users could not be saved. Please, try again.'));
}
Note: I haven't made it handle if entity doesn't exist, create a new one and resume. That can be done with a simple if condition.
I have a simple search form with 3 data and now not all data needs to be filled, some may stay empty ...
as they filled :
$this->Praca->find('all',array(
'conditions'=>array(
'kategoria'=>$wyniki['kategoria'][0],
'wojewodztwo'=>$wyniki['wojewodztwo'],
'poziom'=>$wyniki['poziom']
)));
Now, fe. when $wyniki['kategoria'] is empty I must :
$this->Praca->find('all',array(
'conditions'=>array(
'wojewodztwo'=>$wyniki['wojewodztwo'],
'poziom'=>$wyniki['poziom']
)));
To many possibilities, I need to find a SMART way, any idea? :)
try this
$conditions = array();
if(!empty($wyniki['kategoria'][0])){
$conditions = array('kategoria'=>$wyniki['kategoria'][0]);
}
and so on...
........
$this->Praca->find('all', compact('conditions'));
In Fazel way you need to add conditions per each node. But case below you can add conditions as many as you want
$conditions = array();
$i=1;
foreach ($wyniki as $key=>$wyn){
if(!empty($wyniki[$key])){
$conditions[$i++] = array($key=>$wyn); //or array($key=>end($wyn))
}
}
$this->Praca->find('all',array('conditions'=>$conditions));
Is there anyway to have cake do a multi-row insert in a single query without writing raw SQL to do this? The saveMany and saveAssociated options will only save multiple rows in a single transaction, but that transaction contains multiple insert statements so these methods are clearly not a solution to write heavy applications.
Thanks for reading.
Yes
Though it's not a common practice to do so in app-land code, and doing so removes the possibility to use almost any application logic (validation rules, behaviors, events etc.). You can see an example of doing this in the way fixtures are loaded:
$db = ConnectionManager::getDataSource('default');
$table = "stuffs";
$fields = array('id', 'name');
$values = array(
array(1, 'one'),
array(2, 'two'),
...
);
$result = $db->insertMulti($table, $fields, $values);
You may also find this repository useful (either directly or as a basis for your code) which loads fixture files into your app database - using multi-inserts.
Yes, Big_Data is good idea for inserting bulk. But as AD7six note, it still use basic value quoting and does not return insert ids. And base on your ideas, i wrote small script to inserting bulk in a single query, using default CakePHP quoting and returning ids of inserted records.
$count = count($records);
$dbSource = $this->getDataSource();
$table = $dbSource->fullTableName($this->table);
$fields = $dbSource->prepareFields($this, array('fields' => array_keys($records[0])));
$values = array();
foreach ($records as $index => $record) {
if (!is_array($record) || !$record) {
return null;
}
foreach ($record as $column => $value) {
$values[$index][$column] = $dbSource->value($value, $this->getColumnType($column));
}
$values[$index] = '(' . implode(',', $values[$index]) . ')';
}
$query = 'INSERT INTO %s (%s) VALUES %s;';
$query = sprintf($query, $table, implode(',', $fields), implode(',', $values));
if (!$dbSource->execute($query)) {
return false;
}
$lastInsertId = $dbSource->getConnection()->lastInsertId();
$insertIds = array();
for ($i = 0; $i < $count; $i++) {
$insertIds[] = $lastInsertId + $i;
}
return $insertIds;
Someone pointed me towards the Big Data Behavior https://github.com/jmillerdesign/CakePHP_Big_Data
If you are using CakePHP 3.0 you can check the answer to this question: How to use insert in query builder insert multiple records?
If you are using CakePHP 2 you will have to use raw SQL like this:
$sql = "INSERT INTO `people` (`name`,`title`) VALUES ";
foreach($people as $person){
list($name,$title) = $person;
$sql.= "('$name','$title'),";
}
$this->query(substr($sql,0,-1));
Source: Inserting Multiple Rows with CakePHP 3
yes you can use like below
The getDataSource() method is static in CakePHP 2.x, so you should be able to use:
$db = ConnectionManager::getDataSource('default');
$db->rawQuery($some_sql);
here i am posting method to do. you have to create some SQL statement manually to insert multiple row in one time.
Please let me know if i can help you more.
I have I problem that I hope someone can help me with. I thought this code was right, but it will not work. Below is my code, it is a function for my CakePHP 2.2.2 site, the main aim of the code is to produce a menu system from database results. The problem is with my foreach loop, it will not loop. All $Key does is return the value of 2 (three records within the table at this time). So When I display / echo / debug $Menu, the only result I get is the last result stored within the database.
I know the SQL command is right, if that is debuged / echoed then all three results are displayed. The idea of this loop was to get it to count the results, so that I could run a check on a selected field. Where I am going wrong?
function MenuSystem() {
$this->loadModel('Menu');
$MenuSQL = $this->Menu->find('all', array('conditions' => array('active' => true)));
foreach ($MenuSQL as $Key=>$Value) {
$MenuSystem = $MenuSQL[$Key];
$this->Menu = $MenuSystem;
}
}
Many Thanks,
Glenn.
UPDATE :::
Below is my function, now my foreach loop now works, don't know what I was doing wrong, but I know think its working. You can see the print_r command that I am using for testing, if I use that, then all links from my database are printed / echoed on the screen and all works. But if I try and call the $this->Menu from another controller, then only the last record is echoed on the screen. I have moved the $this->Menu outside of the foreach loop, but it made no difference, with it inside the loop or outside, it still only echoes the last record and not all three. So what I am doing wrong?
function MenuSystem() {
$this->loadModel('Menu');
$SiteBase = '/projects/cake/';
$MenuSQL = $this->Menu->find('all', array('conditions' => array('active' => true)));
foreach ($MenuSQL as $key => $Value) {
$MenuAccessLevel = $MenuSQL[$key]['Menu']['roles_id'];
if ($MenuAccessLevel == 1) {
$Path = $MenuSQL[$key]['Menu']['path'];
$Title = $MenuSQL[$key]['Menu']['title'];
$MenuSys = "<a href=\" " . $SiteBase . $Path . " \">" . $Title ."";
} else {
print ("Admin");
}
//print_r($MenuSys);
} //End of Foreach Loop
$this->Menu = $MenuSys;
} //End of function MenuSystem
So When I display / echo / debug $Menu, the only result I get is the last result stored within the database.
You're setting the value of $this->Menu within the foreach, so when the foreach is complete it will take the last value iterated over.
If you want to find the number of records matching a condition, try:
$menuCount = $this->Menu->find('count', array(
'conditions'=>array('active'=>true)
));
$this->set(compact('menuCount'));
Edit: also, by setting the value of $this->Menu within the foreach, you're overwriting the Menu model variable. This is not a good idea.
Edit2: to get the counts of rows as grouped by some value, try:
$this->Menu->virtualFields = array('count' => 'count(*)');
$counts = $this->Menu->find('all', array(
'group'=>'Role',
'fields'=>array('Role', 'count'),
));
This generates SQL to have the results grouped by the Role column. Returned fields are the name of the role, and the number of rows having that value.
If you wanted to do it with a foreach loop instead, it might look like:
$menus = $this->Menu->find('all', array('fields'=>array('id', 'Role')));
$counts = array('user'=>0, 'admin'=>0);
foreach ($menus as $menu) {
$role = $menu['Menu']['Role'];
$counts[$role] += 1;
}