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.
Related
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']);
}
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.
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.
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
Quoting from the cakephp Book (ver 1.3):
Note that only fields of the model you are directly doing find on will be translated. Models attached via associations won't be translated because triggering callbacks on associated models is currently not supported.
Has anyone come up with a solution for this?
If not could you give me some pointers concerning the following simple scenario.
I have 2 models:
Project, Category.
Project HABTM Category
I have properly set up i18n table and I have a few entries in the db, all translated. When I retrieve a project it does retrieve the translation but not the translated category because as it says in the cakephp book models attached via associations won't be translated.
I have another workaround; I don't know if it is any better or worse performance- or style-wise, only that it suits the "fat models, skinny controllers" goal:
AppModel.php
public function getTranslatedModelField($id = 0, $field) {
$res = false;
$db = $this->getDataSource();
$tmp = $db->fetchAll('SELECT content from s2h_i18n WHERE model = ? AND locale = ? AND foreign_key = ? AND field = ? LIMIT 1',
array($this->alias, Configure::read('Config.language'), $id, $field)
);
if (!empty($tmp)) {
$res = $tmp[0]['s2h_i18n']['content'];
}
return $res;
}
SomeModel.php
public function afterFind($results, $primary = false) {
foreach ($results as $key => $val) {
if (isset($val['SomeOtherModel']) && isset($val['SomeOtherModel']['id'])) {
$results[$key]['SomeOtherModel']['name'] =
$this->SomeOtherModel->getTranslatedModelField($val['SomeOtherModel']['id'], 'name');
}
// other possible queries for other models and/or fields
}
return $results;
}
OK I found a solution. Which is mostly a workaround. I should have thought of that earlier.
What I'm doing is the following. I'm finding all projects and recursively all categories associated with projects. Now since cakephp does not translate categories I am using the results from the initial query and I am performing a second one only for categories but using the category id values that I found on the first query. Now cakephp translates categories since I'm only searching for them and I can have their data translated.
At the moment I'm OK with this solution but it would be nice if first cakephp makes the translate behavior out of the box ready or secondly if someone had a behavior that could support retrieval of translation on associated models.
I generalized the afterFind part a bit, so that it automatically grabs the fields to translate from the associated models' actsAs["Translate"] array, and uses an array of associated models to (potentially) translate:
public function afterFind($results, $primary = false){
$modelsToTranslate = array("SomeModel", "AnotherModel");
foreach ($results as $key => $val){
foreach($modelsToTranslate as $mtt){
if (isset($val[$mtt])){
foreach($val[$mtt] as $fieldname => $fieldval){
foreach ($this->$mtt->actsAs["Translate"] as $fieldToTranslate){
$results[$key][$mtt][$fieldname][$fieldToTranslate] = $this->$mtt->getTranslatedModelField($val[$mtt][$fieldname]['id'], $fieldToTranslate);
}
}
}
}
}
return $results;
}
I took above solution and generalized both functions a bit, now it needs to be used together with the translate behaviour and both functions need to go into the model.php - everything else should work by itself:
public function getTranslatedModelField($id = 0, $field) {
$res = false;
$translateTable = (isset($this->translateTable))?$this->translateTable:"i18n";
$db = $this->getDataSource();
$tmp = $db->fetchAll(
"SELECT content from {$translateTable} WHERE model = ? AND locale = ? AND foreign_key = ? AND field = ? LIMIT 1",
array($this->alias, Configure::read('Config.language'), $id, $field)
);
if (!empty($tmp)) {
$res = $tmp[0][$translateTable]['content'];
}
return $res;
}
public function afterFind($results, $primary = false) {
if($primary == false && array_key_exists('Translate', $this->actsAs)) {
foreach ($results as $key => $val) {
if (isset($val[$this->name]) && isset($val[$this->name]['id'])) {
foreach($this->actsAs['Translate'] as $translationfield) {
$results[$key][$this->name][$translationfield] =
$this->getTranslatedModelField($val[$this->name]['id'], $translationfield);
}
} else if($key == 'id' && is_numeric($val)) {
foreach($this->actsAs['Translate'] as $translationfield) {
$results[$translationfield] =
$this->getTranslatedModelField($val, $translationfield);
}
}
}
}
return $results;
}