How to save a record with associated data - cakephp

In my application there are 3 types of information:
BrokerInfo
BrokerBank
BrokerDocument
A user should fill out all of the data before it is saved to the database.
What I've tried so far is below, but something is wrong with the transactions as for example BrokerInfo records are being created without BrokerBank records. What is the correct way to store everything or nothing?
public function add() {
if ($this->request->is('post')) {
$this->BrokerInfo->begin();
$this->BrokerInfo->create();
$this->request->data['BrokerInfo']['id'] = String::uuid();
$this->request->data['BrokerInfo']['account_status_id'] = 1;
$this->request->data['BrokerInfo']['db_status_id'] = 1;
if ($this->BrokerInfo->save($this->request->data)) {
$this->BrokerBank->begin();
$this->BrokerBank->create();
$this->request->data['BrokerBank']['broker_info_id'] = $this->request->data['BrokerInfo']['id'];
if($this->BrokerBank->save($this->request->data))
{
$this->BrokerDocument->begin();
$this->BrokerDocument->create();
if($this->BrokerDocument->save($this->request->data))
{
$this->BrokerInfo->commit();
$this->BrokerBank->commit();
$this->BrokerDocument->commit();
$this->Session->setFlash(__('The Broker information has been saved'), 'flash_success');
$this->redirect(array('action' => 'index'));
}
else
{
$this->BrokerInfo->rollback();
$this->BrokerBank->rollback();
$this->BrokerDocument->rollback();
$this->Session->setFlash(__('The customer information could not be saved. Please, try again.'), 'flash_fail');
}
}
else
{
$this->BrokerInfo->rollback();
$this->BrokerBank->rollback();
$this->Session->setFlash(__('The customer information could not be saved. Please, try again.'), 'flash_fail');
}
}
else
{
$this->BrokerInfo->rollback();
$this->Session->setFlash(__('The customer information could not be saved. Please, try again.'), 'flash_fail');
}
}

Use saveAssociated
The code in the question is a user-land implementation of something which already exists: saveAssociated
Model::saveAssociated(array $data = null, array $options = array())
Method used to save multiple model associations at once.
Using saveAssociated the code in the question is no more than:
public function add() {
if (!$this->request->is('post')) {
return;
}
$this->BrokerInfo->create();
$this->request->data['BrokerInfo']['account_status_id'] = 1;
$this->request->data['BrokerInfo']['db_status_id'] = 1;
if ($this->BrokerInfo->saveAssociated($this->request->data)) {
$this->Session->setFlash(...);
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(...);
}
Note that it's not necessary to specify the uuid (use $model->id after save to refer to the new id), and the current hard coded defaults would be better placed in a model beforeSave method unless their values are controller-logic dependent.

Related

Cakephp multiple input fields

I'm a newbie at cakephp. I got a form with 5 inputs. My form should be able to save either one user input or all 5 inputs. I'm able to save when user fills all 5 inputs, however, when user fills only 1 or 2 and saves it. Blank spaces with date created (current date) gets saved in the database. How can i make it to save only the user inputs from the form without any empty fields in the database. My Add function below.
public function add() {
if ($this->request->is('post')) {
$this->Item->create();
for ($i=0;$i<5;$i++){
if(empty($this->request->data['Item'][$i]['name'])){
}else{
$name = $this->request->data['Item'][$i]['name'];
$explode_name = explode(":",$name);
$this->request->data['Item'][$i]['name'] = $explode_name[0];
$this->request->data['Item'][$i]['hrid'] = $explode_name[1];
}
}
if ($this->Item->saveAll($this->request->data['Item'])) {
$this->Session->setFlash(__('The Item has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The item could not be saved. Please, try again.'));
}
}
$itemTypes = $this->Item->ItemType->find('list',array('order' =>array('ItemType.name' => 'asc')));
$this->set(compact('itemTypes'));
}
There is a small thing that you are missing, and it is that whether there is name empty or not but it has a value set for that particular index. You should unset that in case the value is empty as below
public function add() {
if ($this->request->is('post')) {
$this->Item->create();
for ($i=0;$i<5;$i++){
if(empty($this->request->data['Item'][$i]['name'])){
// here we are removing the empty name index so that it does not saves the result
unset($this->request->data['Item'][$i]);
}else{
$name = $this->request->data['Item'][$i]['name'];
$explode_name = explode(":",$name);
$this->request->data['Item'][$i]['name'] = $explode_name[0];
$this->request->data['Item'][$i]['hrid'] = $explode_name[1];
}
}
// also here we should check here that atleast there is one entry
if(!empty($this->request->data['Item'])){
if ($this->Item->saveAll($this->request->data['Item'])) {
$this->Session->setFlash(__('The Item has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The item could not be saved. Please, try again.'));
}
} else {
$this->Session->setFlash(__('There is no such item. Please fill value for at least one item.'));
}
}
$itemTypes = $this->Item->ItemType->find('list',array('order' =>array('ItemType.name' => 'asc')));
$this->set(compact('itemTypes'));
}
Please try the above code.
In CakePHP 2.x you can do it as like below-
public function add(){
if ($this->request->is('post')) {
$this->Item->create();
$items = $this->request->data['Item']; /*Get all items Array*/
$items = array_filter(array_map('array_filter', $items)); /*Remove all empty array, only keep Array with user inputs*/
if ($this->Item->saveAll($items)) {
/*Success*/
} else {
/*Error*/
}
}
}
In CakePHP 3.x you can do it as like below-
public function add() {
if ($this->request->is('post')) {
$items = $this->request->data['Item']; /*Get all items Array*/
$items = array_filter(array_map('array_filter', $items)); /*Remove all empty array, only keep Array with user inputs*/
$entities = $this->Item->newEntities($items); /*Prepare all Data*/
if($this->Item->saveMany($entities)){ /*Save all data*/
/*Success*/
}else{
/*Error*/
}
}
}

cakephp 3 controller action -> how to make it smart

I implementet a controller action in the maybe most unelegant way. How could this made better? Table classes are just like after bin/cake bake. I think the part where the Entity is created could be simplyfied very much.
What I'm doing:
Books --belongsTo--> Publishers <--habtm--> Publishernumbers
When adding a Book to the database, the publishernumber is extracted from the ISBN number. This number is then linked to the publisher in a habtm relation. I need this to suggest the user some publishers when typing an isbn in the form.
The code works for now, but in a year, only god will know what I did here. The first part is straightforward.
public function add()
{
$book = $this->Books->newEntity();
$associations = ['associated' =>
[
'Tags',
'Publishers',
'Publishers.Publishernumbers'
]
];
if ($this->request->is('post')) {
$data= $this->request->data;
$publisher = $this->Books->Publishers->get(
$this->request->data['publisher_id'],
['contain' => ['Publishernumbers']]
);
unset($data['publisher_id']);
$book->publisher = $publisher;
//extract group- and publishernumber from the ISBN
$this->loadComponent('Isbn.Isbn');
$split = $this->Isbn->splitIsbn($this->request->data['isbn']);
$publishernumber = $split[1].$split[2];
This is the part where the mess begins. I think this could be done way more elegant.
//check if the publisher contains already the $publishernumber
//and if not, add it to the entity
$new = true;
foreach ($book->publisher->publishernumbers as $n){
if ($n->number == $publishernumber){
$new = false;
}
}
if ($new){
$existingNumber = $this->Books->Publishers->Publishernumbers
->findByNumber($publishernumber)
->first();
if (!$existingNumber){
//publishernumber does not exist in the database
$pubnumber = $this->Books->Publishers->Publishernumbers
->newEntity();
$pubnumber = $this->Books->Publishers->Publishernumbers
->patchEntity($pubnumber, ['number' => $publishernumber]);
$book->publisher->publishernumbers[] = $pubnumber;
} else {
//publishernumber exists in the database
//but is not associated with the publisher
$book->publisher->publishernumbers[] = $existingNumber;
}
$book->publisher->dirty('publishernumbers', true);
}
$book = $this->Books->patchEntity($book, $data, $associations);
Saving
if ($this->Books->save($book, $associations)){
Cache::delete('exlibrisBooksIndex');
$this->Flash->success(__('The book has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('Error.'));
}
}
$publishers = $this->Books->Publishers->find('list')
->order('name')
->toArray();
$this->set(compact('book', 'publishers'));
$this->set('_serialize', ['book']);
}

cakephp retrieve HABTM value from view

I have a question to ask.
I am using cakephp 2.0, i have table products, materials and materials_products.
in table products:
id
name
description
sale
saleprice
frame_material_id
in table materials
id
name
in table materials_products
id
product_id
material_id
i just want to ask how to retrieve the input data for material_id in productController?(from view).
materials and products have a HABTM relationship.
i mean i know if i want to retrieve input data from name in products controller i can use
$this->request->data['Product']['name']
but if i want to retrieve input material_id from products should i use:
$this->request->data['Product']['Material']
or
$this->request->data['Material']['id']
or
$this->request->data['Material']
or something else? i tried to use all 3 but i keep getting error. different type of error.
this is the code that i have tried so far (productController):
public function addproductnewcont($colorcounts = null, $groupid = null, $id = null) {
$start = $id - $colorcounts + 1;
$setids = range($start, $id);
if (!$this->Product->exists($id)) {
throw new NotFoundException(__('Invalid product'));
}
if ($this->request->is('post') || $this->request->is('put')) {
$saleprice = $this->request->data['Product']['saleprice'];
$sale = $this->request->data['Product']['sale'];
$framematid = $this->request->data['Product']['frame_material_id'];
$materialids = $this->request->data['Material']; //this is the problem
$fieldtoupdate=array(
'saleprice'=>"'$saleprice'",
'frame_material_id'=>"'$framematid'",
'sale'=>"'$sale'");
$this->loadModel('MaterialsProduct', 2);
foreach($setids as $setid) {
if ($this->Product->updateAll($fieldtoupdate,array('Product.id'=> $setid))) {
$this->_savematerial($materialids, $setid);
if ($setid == $id) {
$this->Session->setFlash(__('The product has been saved'));
return $this->redirect(array('action' => 'index'));
}
} else {
$this->Session->setFlash(__('The product could not be saved. Please, try again.'));
} }
}
public function _savematerial($materialids = null, $setid = null){
foreach($materialids as $materialid){
$this->MaterialsProduct->create();
$this->MaterialsProduct->saveField('product_id', $setid);
$this->MaterialsProduct->saveField('material_id', $materialid);
}
this give me error saying
Array to string conversion
can you help me? thanks!
I have found the answer to my question. hope this will help anyone who seek the same answer as me.
my code is alright except for i need to add one more foreach in the code. (i don't know the actual problem).
public function _savematerial($materialids = null, $setid = null){
foreach($materialids as $materialid){
foreach($materialid as $materiali){
$this->MaterialsProduct->create();
$this->MaterialsProduct->saveField('product_id', $setid);
$this->MaterialsProduct->saveField('material_id', $materiali);
}
}
}
thank you for everyone.

Refresh the view after I submitted POST params on deleting a record

My question is how can I refresh my view "search.ctp" to take into account the record I just deleted. The problem is the following.
My controller code
public function search() {
if ($this->request->is('post')) {
$this->set("isPost", TRUE);
$query = $this->data;
$output = $this->Question->find("all", array("conditions"=>array("Question.lectureId"=>$query["Lecture"]["Lecture"],
"Question.type"=>$query["Lecture"]["status"])));
$this->set("questions", $output);
} else {
$this->LoadModel("Lecture");
$outputL = array();
$for = $this->Lecture->find("all", array("fields" => array("_id", "title")));
foreach ($for as $key => $value) {
$outputL[$value["Lecture"]["_id"]] = $value["Lecture"]["title"];
}
$this->set("lectures",$outputL);
//
$statuses = array(
"" => "Select a question type",
"anonymousQuestion" => "anonymousQuestion",
"handUp" => "handUp",
"userQuestion" => "userQuestion"
);
$this->set("statuses", $statuses);
}
}
So the following happens;
I open the view "search.ctp" ("my admin interface"), set the 2 search params,
and use the submit button to post that data. Then my IF statement recognizes that as POSt and gives me back my query results. The problem is when i delete a record...
It redirects me back to my search action to enter the query params again... How do i just refresh the page with the same query params and NOT leave my view.
o forgot my delete function code:
public function delete($id = null) {
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->Question->id = $id;
if (!$this->Question->exists()) {
throw new NotFoundException(__('Invalid configuration'));
}
if ($this->Question->delete()) {
$this->Session->setFlash(__('Question deleted'));
return $this->redirect(array("action"=>"search"));
}
$this->Session->setFlash(__('Question was not deleted'));
$this->redirect(array('action' => 'search'));
}
As a workaround i made another function that does the same thing with GET request that my search function does with a POST request. Basically returns the data with the query params. And i used the Session helper to carry the query over to my other function. Dont know how smart that was, but it does the trick for me...
Still would be nice to know if someone has a solution where i dont have to make another function/view

Saving data with saveAll() in CakePHP

This might not be the problem with saveAll, the situation is like this. I have table messages, and table sent_messages, which holds message id and user id. Models are created, where Messaage hasMany SentMessage, and SentMessage belongsTo Message. I need to save just one message in messages table, and save user ids and message id in *sent_messages* table. The problem starts when I try to save that in MessageController. Here is the code:
function custom() {
if (!empty($this->data)) {
$this->loadModel('User');
$userIDs = $this->User->listActiveUsers();
$this->data['Message']['message_type_id'] = 2;
if ($this->Message->save($this->data)) {
array_pop($this->data);
foreach ($userIDs as $key => $value ) {
$this->data['SentMessage'][$key]['user_id'] = $value;
$this->data['SentMessage'][$key]['message_id'] = $this->Message->getLastInsertID();
}
//die($this->Message->SentMessage->saveAll($this->data));
if ($this->Message->SentMessage->saveAll($this->data)) {
$this->Session->setFlash('Data saved', 'success');
}
else {
$this->Session->setFlash('Data not saved', 'error');
}
}
}
}
Any help would be appreciated.
I think your issue come from using "$key" as key for $userIDs array, saveAll take unindexed array for hasMany relations as parameters.
More-over, saveAll is perfect to save two related model in same time. Here something that should work :
function custom() {
if (!empty($this->data)) {
$this->loadModel('User');
$this->data['Message']['message_type_id'] = 2;
$userIDs = $this->User->listActiveUsers();
foreach ($userIDs as $value ) {
$this->data['SentMessage'][]['user_id'] = $value;
}
if ($this->Message->saveAll($this->data)) {
$this->Session->setFlash('Data saved', 'success');
} else {
$this->Session->setFlash('Data not saved', 'error');
}
}
}
Check the doc ! http://book.cakephp.org/view/1031/Saving-Your-Data
About using loadModel, I suppose that your SentMessage model is related to User, so you can use : $this->Message->SentMessage->User. But well no idea of which is the more efficient.
Easiest Way to save all data in cakephp
$this->ModelName->saveAll($data_array);
Note : name of the fields in table and in array should be same.

Resources