something's wrong with my cakePHP save method - php

i have a method inside a model, to do a simple save (insert to database), first it finds any users which meet certain conditions, and then insert certain info to another table to each of those found users, here's the code:
function save_emails(){
App::import('Model', 'User');
App::import('Model', 'EmailSave');
$this->EmailSave = new EmailSave();
$this->User = new User();
$users = $this->User->find("all", array(
'conditions' => array(
'OR' => array(
array('User.user_type_id' => '4'),
array('User.user_type_id' => '1')
)
),
'fields' => array(
'username',
'email'
),
'recursive' => -1
));
foreach($users as $user){
//pr($user);
$to = $user['User']['email'];
$subject = $email_template['subject'];
$template = //calling a templating method;
$save_send['EmailSave']['created'] = date("Y-m-d H:i:s", time());
$save_send['EmailSave']['modified'] = date("Y-m-d H:i:s", time());
$save_send['EmailSave']['send_to'] = $to;
$save_send['EmailSave']['subject'] = $subject;
$save_send['EmailSave']['message'] = $template;
//pr($save_send);
$this->EmailSave->save($save_send);
//echo $to."<br />".$subject."<br />".$template."<br />".$cron_id."<br />";
}
}
this is pretty much straight forward, i mean i am not using any complicated methods, now when i run the find method towards user :
$users = $this->User->find("all", array(
'conditions' => array(
'OR' => array(
array('User.user_type_id' => '4'),
array('User.user_type_id' => '1')
)
),
'fields' => array(
'username',
'email'
),
'recursive' => -1
));
it returns 2 results, and when i try to print the result using cake's pr method, it would print the right results, but...if i apply the save method, it would only insert one of the result, how is that possible? i have traced the codes line by line and it went through just find, the problem is when i save them to the database, it fails to save all 2 results, please help.

http://book.cakephp.org/view/1031/Saving-Your-Data
Creating or updating is controlled by the model's id field. If $Model->id is set, the record with this primary key is updated. Otherwise a new record is created.
When calling save() in a loop, don't forget to call create().

Related

CakePHP call to undefined method stdClass::read() error

I'm new to CakePHP and I'm still learning the basics, through working in a live project and taking help from the CakePHP documentations when necessary. Currently, I'm having the following problem : I've recently changed my database table name and structure, so I was forced to change my view, controller and model names. After changing names, whenever I run the index.ctp page, I get the following error:
Fatal error: Call to undefined method stdClass::read() in C:\wamp\www\sdb\app\controllers
\home_loan_distributions_details_controller.php on line 32
Previously, my view folder was named home_loan_distributions, now it's renamed to home_loan_distributions_details.
My previous controller name was home_loan_distributions_controller.php and current name is home_loan_distributions_details_controller.php. The codes:
class HomeLoanDistributionsDetailsController extends AppController {
var $name = 'HomeLoanDistributionsDetails';
function index() {
$user = $this->Session->read('User');
$user_role = $user['User']['user_role_id'];
$actions = $this->Session->read('actions');
$display_actions = array();
foreach ($actions as $action) {
array_push($display_actions, $action['pm_controller_actions']['name']);
}
$this->set('display_actions', $display_actions);
$this->set('user_role', $user_role);
$branch_id = 18;
$this->set('branch_id', $branch_id);
$conditions = array('branch_id' => $branch_id);
$this->set('HomeLoanDistributionsDetails', $this->paginate($conditions));
$this->HomeLoanDistributionDetail->Branch->recursive = 0;
$this->set('BranchDetailInformation', $this->HomeLoanDistributionDetail->Branch->read(array('Branch.id', 'Branch.name', 'RegionalOffice.name', 'DistrictOffice.name', 'SubDistrictOffice.name', 'ClusterOffice.name'), $branch_id));
}
My model was previously named home_loan_distribution.php and now it's named home_loan_distribution_detail.php. The codes:
class HomeLoanDistributionDetail extends AppModel {
var $name = 'HomeLoanDistributionDetail';
var $actsAs = array('Logable' => array(
'userModel' => 'User',
'userKey' => 'user_id',
'change' => 'list', // options are 'list' or 'full'
'description_ids' => TRUE // options are TRUE or FALSE
));
var $validate = array(
'entry_date' => array(
'rule' => 'date',
'message' => 'Enter a valid date',
'allowEmpty' => true
),
'branch_id' => array('numeric'),
'customer_id' => array('numeric'),
'loan_amount' => array('numeric'),
'service_charge' => array('numeric'),
'security' => array('numeric'),
'loan_taken_term' => array('numeric'),
'purpose_id' => array('numeric'),
'installment_amount' => array('numeric'),
'installment_service_charge' => array('numeric'),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Branch' => array(
'className' => 'Branch',
'foreignKey' => 'branch_id',
'conditions' => '',
'fields' => 'id,name',
'order' => ''
)
);
function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
$recursive = 0;
$group = $fields = array('branch_id', 'entry_date');
$order = array('entry_date DESC');
$limit = 4;
$this->paginateCount($conditions);
return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'recursive', 'group'));
}
function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
$recursive = 0;
$group = $fields = array('branch_id', 'entry_date');
$order = array('entry_date DESC');
$results = $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group'));
return count($results);
}
}
What my guess is: probably I messed up the naming conventions while renaming everything. The problem is definitely within this line in the controller:
$this->set('BranchDetailInformation',
$this->HomeLoanDistributionDetail->Branch->read(array('Branch.id', 'Branch.name',
'RegionalOffice.name', 'DistrictOffice.name', 'SubDistrictOffice.name', 'ClusterOffice.name'),
$branch_id));
Whenever I comment out this line, I stop getting the above mentioned error message and my view page loads (although that still have some data missing - because I need those Branch related data to be displayed in my view.)
I can't figure out what exactly my problem is, but at least I know where it is. I need someone to pinpoint it.
My CakePHP version is 1.2.5, PHP version - 5.2
There is no function read for model.If you want to find some model data then try with -
$this->set('BranchDetailInformation', $this->HomeLoanDistributionDetail->Branch->find('all', $consitions);
$conditions will be the array of all requirements you want to provide. See the docs for more info.
Apparently, the problem seemed to be related to my 'className' => 'Branch' element of the Branch array used in my model, since the stdClass::read() method is related to classes and not models. But I discovered that the problem was elsewhere. This error was part of that problem, but it itself is not the actual problem.
I figured out this morning that my Model name is HomeLoanDistributionDetail, but my table name is home_loan_distributions_details (because someone else has changed the table name). CakePHP convention requires corresponding table name to be plural and model class name to be singular and CamelCased.
Quoting from the CakePHP Cookbook:
Model class names are singular and CamelCased. Person, BigPerson, and ReallyBigPerson are all examples of conventional model names.
Table names corresponding to CakePHP models are plural and underscored. The underlying tables for the above mentioned models would be people, big_people, and really_big_people, respectively.
Considering the above convention, I just had to rename my model class name from HomeLoanDistributionDetail to HomeLoanDistributionsDetail, in order to match with the table name home_loan_distributions_details. Also, I had to change the model file name from home_loan_distribution_detail.php to home_loan_distributions_detail.php.
After doing that, I stopped getting the error and I was successful in retrieving data from table and viewing it.

Why only last item is saved in database?

I have problem, becouse only last item from loop is saved in database.
Im using CakePhp 2.x
Controller:
for ($x=1; $x <= count($this->request->data['Goodsandoffer'])/3;$x++){
$promID = $this->request->data['Goodsandoffer']['promotionaloffer_id_'.$x];
if($this->request->data['Goodsandoffer']['cenaPromocyjna_'.$x] != ''){
$helperReqestTable3 = array('promotionaloffer_id'=>$this->request->data['Goodsandoffer']['promotionaloffer_id_'.$x],'good_id'=>$this->request->data['Goodsandoffer']['good_id_'.$x],'cenaPromocyjna'=>$this->request->data['Goodsandoffer']['cenaPromocyjna_'.$x]);
$helperReqestTable['Goodsandoffer']=$helperReqestTable3;
debug($helperReqestTable);
$this->Goodsandoffer->save($helperReqestTable);
}
}
Here is how look my debug in loop:
array(
'Goodsandoffer' => array(
'promotionaloffer_id' => '7',
'good_id' => '18',
'cenaPromocyjna' => '1'
)
)
And in next interation:
array(
'Goodsandoffer' => array(
'promotionaloffer_id' => '7',
'good_id' => '19',
'cenaPromocyjna' => '2'
)
)
In database is created only one row with last item.
Model:
class Goodsandoffer extends AppModel {
public $displayField = 'id';
public $belongsTo = array(
'Promotionaloffer' => array(
'className' => 'Promotionaloffer',
'foreignKey' => 'promotionaloffer_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Good' => array(
'className' => 'Good',
'foreignKey' => 'good_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
Have you tried calling $this->Goodsandoffer->create() before you call save? That way you're definitely telling Cake to create a new record each time.
The general process of creating and saving data in Cake is:
$this->Model->create()
$this->Model->set($data_array)
$this->Model->save()
You can also eliminate step 2 above by passing you $data_array to the save() function:
$this->Model->create();
$this->Model->save($data_array);
NOTE: from the manual (if you aren't using create()):
When calling save in a loop, don’t forget to call clear().
Another way you could create new data would be to make sure the primary key for your model is null in the data set you pass into save, although this is a little less obvious and probably best to stick to the create/[set/]save flow:
$data = array('id' => null, 'somefield' => 'foobar');
$this->Model->save($data); // new record created
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

CakePHP return invalid node

I am trying create a webapplication with CakePHP. I am newbie with that but i created a lot lines of code. Now i have to add ACL and now i have a big problem. My foreach loop with if statement call $this->ACL->deny('Group', 'Alias in ARO table') or $this->ACL->allow('Group', 'Alias in ARO table'). Everything is fine and automatic table acos_aros is modified great ... BUT !! i have one alias which generate something like that:
Warning (512): DbAcl::allow() - Invalid node [CORE\Cake\Model\Permission.php, line 182]
I have no idea why. It is 'Podopieczni' when i change to 'Podopieczn' or to 'Podopieczni1' then is working fine without any errors. Anybody can told me why alias 'Podopieczni' generate that error ?
EDIT 1
My bad. I have actually problem with ACO not Aro as said before. When i change alias in ACO table from Podopieczni to Podopieczni1 or something else is working. Alias Podopieczni is generate error.
Here is my code
public function pokaz(){
if ($this->request->is('post')){
$acl['Aro'] = $this->Acl->Aro->find('list', array(
'fields' => array('id', 'alias'),
'conditions' => array(
'parent_id IS NULL'
)
));
$acl['Aco'] = $this->Acl->Aco->find('list', array(
'fields' => array('id', 'alias'),
'conditions' => array(
'parent_id IS NULL'
)
));
foreach($this->request->data['uprawnienia'] AS $pozycja => $uprawnienie){
$t_aco = $acl['Aco'][split('-',$pozycja)[0]];
$t_aro = $acl['Aro'][split('-',$pozycja)[1]];
if($uprawnienie){
//echo 'ALLOW: '.$t_aro.' => '.$t_aco.'<br>';
$this->Acl->Allow($t_aro, $t_aco);
} else {
//echo 'DENY: '.$t_aro.' => '.$t_aco.'<br>';
$this->Acl->Deny($t_aro, $t_aco);
}
}
//$this->redirect(array('controller'=>'admin', 'action' => 'pokaz'));
//$this->render('/admin/pokaz');
//exit;
}
$wynik = $this->Acl->Aco->find('all', array(
'conditions' => array(
'parent_id is null'
)
));
for($a=0; $a<count($wynik); $a++){
$tmp = array();
foreach($wynik[$a]['Aro'] AS $wiersz){
$tmp[$wiersz['foreign_key']] = $wiersz;
}
$wynik[$a]['Aro'] = $tmp;
}
$this->set('wyniki', $wynik);
$this->set('grupy', $this->requestAction('/admin/pobierzGrupy'));
}
SOLVED
In the ACOS table I have two rows with this same alias.
YOU ARE AWESOME noslone. VERY BIG THANKS FOR YOUR TIME AND PATIENCE :)))
That alias does not exist in the aros or acos table.
You should create it first.
For ARO:
$aroData = array(
'id' => NULL,
'model' => 'Group',
'alias' => 'Podopieczni'
);
$this->Acl->Aro->save($aroData);
For ACO:
$acoData = array(
'id' => NULL,
'parent_id' => NULL,//or parent_id?
'alias' => 'Podopieczni'
);
$this->Acl->Aco->save($acoData);
You should read the Documentation, it is explained pretty nice.

how to make chained model auto read the conditions?

I have two models called Batch and User
Batch has the following
public $belongsTo = array(
'Customer' => array(
'className' => 'User',
'foreignKey' => 'customer_id',
'conditions' => array('Customer.group_id' => CUSTOMERS),
'fields' => '',
'order' => '',
),
);
When I do the following:
$customers = $this->Batch->Customer->find('list');
I fully expected to get back just the users whose group_id matches CUSTOMERS. It returns ALL the records in the users table.
However, I actually have to write
$customers = $this->Batch->Customer->find('list', array('conditions' => array('Customer.group_id' => CUSTOMERS)));
Is there a way so that the chained model User knows that it is called as Customer by Batch and therefore automatically reads the correct conditions in the associations found in Batch model?
I want to make my code more readable hence the motivation for this question.
I want to write simply
$customers = $this->Batch->Customer->find('list');
or something similarly straightforward.
Of course, I realized that if I do the following:
$batches = $this->Batch->find('all');
The condition stated in the associations will be used. But I don't want to find batches. I want to find just customers.
I am using CakePHP 2.4
I think you can't
but you can create custom find types in User model file
public $findMethods = array('customer' => true); //this enable a custom find method named 'customer'
protected function _findCustomer($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions'] = array('group_id' => CUSTOMERS);
}
return parent::_findList($state, $query, $results);
}
and in BatchesController
$this->Batch->Customer->find('customer');
There are several ways to do this.
1)
do nothing.
Continue to use code like
$customers = $this->Batch->Customer->find('list', array('conditions' => array('Customer.group_id' => CUSTOMERS)));
2)
create a custom find method as suggested by arilia.
3)
write a getCustomers method inside Batch model
where it looks something like this:
public function getCustomers($type, $query = array()) {
if (empty($query['conditions'])) {
$query['conditions'] = array();
}
$query['conditions'] = array_merge($query['conditions'], array('Customer.group_id' => CUSTOMERS));
return $this->Customer->find($type, $query);
}
then you can call
$customers = $this->Batch->getCustomers('list');
UPDATE:
I have written a Plugin that helps with this kind of behavior, utilizing the 3rd solution.
class Batch extends AppModel {
public $name = 'Batch';
public $actsAs = array('UtilityBehaviors.GetAssoc');
public $belongsTo = array(
'Customer' => array(
'className' => 'User',
'foreignKey' => 'customer_id',
'conditions' => array('Customer.group_id' => 7),
'fields' => '',
'order' => '',
),
);
}
You can fetch just the customer data when you are in BatchesController this way:
$customers = $this->Batch->getAssoc('Customer', 'list');
$customers = $this->Batch->getAssoc('Customer', 'all');
$customerCount = $this->Batch->getAssoc('Customer', 'count');
This behavior has tests at travis and you can read about the tests written at github.

Cakephp $this->request->data returns id of select field instead of value which is needed and shown in the select field

I have a CakePHP controller like this:
$this->loadModel('Project');
$list2 = $this->Project->find( 'list', array(
'fields' => array('Project.project'),
'conditions' => array('Project.user_id' => $userId)
));
$this->set($list2, 'list2');
$this->loadModel('Distance');
if(!empty($this->request->data)){
$this->Distance->create();
if ($this->Distance->save($this->request->data)) {
$this->Session->setFlash('Saved.');
// $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('FAILED');
}
}else{
// $this->Session->setFlash('test');
}
and a view like this:
echo $this->Form->input('Distance.project', array('options' => $list2, 'label' => false, 'empty' => '(choose one)' ;
But I get inserted to the database the id of the project instead of the project name.
I never have such problems working with the fields - just with a list of data.
Any idea why it happens?
It's normal ... The $list2 it's and array ... and the values of the options are the indexes from that array.
If you want to insert only the project name you need to change $list2 with $list2['project_name']. You need to remove or replace the indexes of $list2.
LE: take iexiak example. He change also the code for you.
$list2 = $this->Project->find( 'list', array(
'fields' => array('Project.project'),
'conditions' => array('Project.user_id' => $userId)
)
);
This is because $list2 automatically creates a list of ID => project; and when you use that as input for your form it automatically creates the drop down to reflect this. This is generally the best practice, to link to ID's instead of to descriptions, as ID's do not change as often. The below should get you exactly what you want though:
$list2 = $this->Project->find( 'list', array(
'fields' => array('Project.project','Project.project'),
'conditions' => array('Project.user_id' => $userId)
)
);

Categories