CakePHP UpdateAll Only Updating Last Entered Data - cakephp

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.

Related

drupal form field not loading correct data

I am building my first Drupal 7 module and am having trouble with the screen to edit a fieldable entity. I am using field_attach_form and it is working great for all accept one field which is displaying the field default rather than the current content of that field for that entity.
I have a text field, a number field, a number of Boolean fields and the one list_text field which is failing.
Any ideas what I a doing incorrectly? Code below is what I think is needed but please do let me know if you need more.
Code to create the field in hook_enable:
if (!field_info_field('field_available')) {
$field = array (
'field_name' => 'field_available',
'type' => 'list_text',
'settings' => array(
'allowed_values' => array('No', 'Provisionally', 'Yes'),
),
);
field_create_field($field);
Code to create the instance, also in hook_enable:
if (!field_info_instance('appointments_status', 'field_available', 'appointments_status')) {
$instance = array(
'field_name' => 'field_available',
'entity_type' => 'appointments_status',
'bundle' => 'appointments_status',
'label' => t('Available?'),
'required' => TRUE,
'default_value' => array(array('value' => 'No')),
'description' => t('Set to No if appointments with this status make this slot unavailable, Provisionally means that it will only reserve a space temporarily'),
);
field_create_instance($instance);
This entity has only the one bundle with the same name as the entity.
The code to create the URL in hook_menu:
$items['admin/appointments/appointments_statii/%/edit'] = array(
'title' => 'Edit appointment status',
'description' => 'Edit the parameters of the selected status code',
'page callback' => 'drupal_get_form',
'page arguments' => array('appointments_status_edit_form',3),
'access arguments' => array('access administration pages'),
'type' => MENU_CALLBACK,
);
The form function is:
function appointments_status_edit_form($form, &$form_state) {
// Get the status id from the form_state args
$status_id = $form_state['build_info']['args'][0];
// Load the chosen status entity
$status = entity_load_single('appointments_status', $status_id);
// Set up the fields for the form
field_attach_form('appointments_status', $status, $form, $form_state);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Save changes',
'#weight' => 99,
);
return $form;
}
I have used the Devel module's dpm to check that the data is loaded correctly by entity_load_single and it is.
Thanks
Rory
I have answered my own question!
I was also programmatically loading some entities and was not loading this field with the numbers that a list_text field stores, instead I was loading the visual text.
I used a metadata wrapper and the code looked like this:
$w_appointments_status->$appointments_availability= 'Yes';
I changed it to:
$w_appointments_status->$appointments_availability = 2;
In this example 'Yes' was the third allowed value - hence 2.
So the code in my question was in fact correct although I have since added 'widget' and 'formatter' parameters to the instance.
I am sorry if this got some of you scratching your heads thinking ' but that code is correct'!!
Regards
Rory

CakePHP find() group and order by date complex conditions

I am trying to get messages with their corresponding users, in order to display a chat list of profiles with their last message in chronological order.
array(
0 => array(
'Recepient' => array(
'id' => ...
'name' => ...
...
),
'Message' => array(
'content' => ...
'created' => ...
...
)
),
1 => ...
)
and in order to retrieve the results, I've written this find() method:
$msgs = $this->Message->find('all', array(
'group' => array('Recepient.id'),
'order'=>'Message.created DESC',
'conditions'=>
array(
'OR'=> array(
array('recepient_id'=>$pid),
array('sender_id' => $pid)
)
)
));
What I have:
message with corresponding "Recepient",
in chronological order
The problem:
the query DOES NOT retrieve the most recent message from $recepient_id/$sender_id combination.
So instead of list of users with the last message, I have a list of users with a message. What's wrong with my query? Thanks for help!
METHOD 2 results
I've created "chat_id" field in the database which is basically recepient_id+sender_id sorted alphabetically (because if user1 send user2 a message user1 is sender, later when user2 responds, he becomes the sender, so sorting will ensure two users will always have the same chat_id).
Than I added DISTINCT to the query:
$this->Message->recursive = 0;
$msgs = $this->Message->find('all', array(
'fields' => array('DISTINCT Message.chat_id','Message.*','Recepient.*'),
'order'=>'Message.created DESC',
'conditions'=>
array(
'OR'=> array(
array('recepient_id'=>$pid),
array('sender_id' => $pid)
)
)
));
and it does NOT work! I am now getting multiple messages for the same conversation.
If I remove Message fields and Recipient fields from the query, I get correct amount of "chats".
'fields' => array('DISTINCT Message.chat_id'),
but that's not the solution.
CakePHP version 2.7.0
MySQL DB
METHOD 3 results
$msgs = $this->Message->find('all', array(
'order'=>'Message.created DESC',
'fields' => 'recepient_id, content, max(Message.created) as max_created',
'group'=>'recepient_id',
// 'contain' => array('Recepient'),
'conditions'=>array( 'chat_id' => $chats )
));
I gave up on single-find method to resolve this, so now 1.I am getting list of chats, 2.I want to find the last message from each chat.
Acording to http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/ my find query should work. What happens is
(int) 0 => array(
'Message' => array(
'recepient_id' => '55e6d764-1444-4aad-a909-9042f07f76da',
'content' => '1st msg',
'created' => '2015-09-20 18:24:17',
'created_nice' => '2 hours ago'
),
(int) 0 => array(
'max_created' => '2015-09-20 18:24:28'
)
),
the field max(Message.created) is indeed showing the latest message from conversation but the 0=>Message array is for different message! As you can see $results[0]['Message']['created'] time and $results[0][0]['max_created'] are different!
Finally! Working solution:
$db = $this->Message->getDataSource();
$chats = $db->fetchAll(
'select id from (select * from messages order by created desc) Message
where recepient_id = "'.$pid.'" or sender_id = "'.$pid.'"
group by `chat_id`'
);
Above code will retrieve all messages, following the requirements. You can
a) SINGLE QUERY add mysql JOIN to include associated model (we keep the code single-query neat)
b) TWO QUERIES BUT SMARTER easier method, to select just "ids" of the messages and create another query that will have cake's build in containable behaviour. This might be better also because Behaviours will be applied.
You will have array tree as a result, to dig out the actual ids for next query use following:
$chats = Set::extract("/Message/id",$chats);
c) translate the solution to CakePHP query builder... :) up for a challenge?

CakePHP 2.4 saveAssociated not saving all children elements

Problem Synopsis:
(CakePHP v2.4.2) When I use saveAssociated (or saveAll, same result) for input for a new record with a hasMany/belongsTo relationship with multiple child elements, only the last child element gets saved because it INSERTs the first element, but then executes UPDATES for subsequent elements.
I've used saveAssociated for very similar purposes in this same application and had no problem with it, so I'm baffled.
Queries on all these work just fine, i.e., I get the multiple children associated with each parent.
Models synopsis:
class Site extends AppModel {
// sites columns: id (primary key), bunch of others
public $hasMany = array(
'SiteUser' => array(
'className' => 'SiteUser',
'foreignKey' => 'id', // Yes, I would have preferred 'site_id', lost battle
'dependent' => true
)
);
}
class SiteUser extends AppModel {
// site_users columns: rowid(PK), id (FK to sites), name
public $belongsTo = array(
'className' => 'Site',
'foreignKey' => 'id'
);
}
Equivalent request data (processed from form):
$site_data = array(
'Site' => array('field1' => 'value1', 'field2' => 'value2' ),
'SiteUser' => array(
array('name' => 'Jane Doe'),
array('name' => 'John Doe'),
array('name' => 'Moe Money')
)
);
In the controller:
unset($this->Site->SiteUser->validate['id']);
$saved_site = $this->Site->saveAssociated($site_data);
Results:
All of the Site data gets saved as expected. Only the last SiteUser element (Moe Money in the example) is saved. This is the same regardless of the number of elements in SiteUser, i.e., only the last element gets saved.
SQL Log:
It performs an
INSERT INTO site_users (`name`, `id`) VALUES ('Jane Doe', 1)
but then executes
UPDATE site_users SET 'name' = 'John Doe', 'id' = 1 WHERE site_users = 1
UPDATE site_users SET 'name' = 'Moe Money', 'id' = 1 WHERE site_users = 1
This obviously leaves the very last element as the one to get saved, the others are over-written by updates.
Thanks for any pointers in advance.
You better stick to the conventions, id as the foreign key? No, really, don't do that!
In any case you must tell your SiteUser model about the primary key column name in case it doesn't follow the conventions. See Cookbook > Models > Model Attributes > primaryKey
class SiteUser extends AppModel {
public $primarykey = 'rowid';
// ...
}
And while setting this to rowid will most likely fix the problem, I'd again advise to stick to the naming conventions instead!

cakephp correct array to use updateAll

I am using cakephp 2.5 and I have an array that I wish to update many records at once in the Page model. I can't seem to get the format of the array correct. I get the error:
Notice (8): Undefined index: newOrder [APP/Controller/PagesController.php, line 133]
$newOrder = array(
'Page' => array(
0 => array(
'id' => 3,
'order' => 0),
1 => array(
'id' => 4,
'order' => 0),
2 => array(
'id' => 7,
'order' => 0
)
));
$this->Page->updateAll($newOrder);
One of the parts I think I am missing is using 'data' as part of the array. But I am unsure where to place it.
I have also tried:
$this->Page->updateAll($newOrder['Page']);
You should use saveMany for your requirement. Find the explanation below -
updateAll
updateAll(array $fields, array $conditions) - is used to update one or more records with the same value based on a condition or multiple conditions. eg: If you want to update all your pages & set all of them to order = 0, you can use updateAll without passing the primary keys -
$this->Page->updateAll(
array('Page.order' => 0)
);
If you want to update some pages based on a condition you will do something like -
$this->Page->updateAll(
array('Page.order' => 0),
array('Page.type' => 'PROMOTED')
);
Assuming you have a type field in your page model, the above query will set order 0 for all pages with type PROMOTED
Ref - http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-array-conditions
saveMany
Now if you want to update some specific records with some specific values which you want to create in an array, you should use a saveMany(array $data = null, array $options = array())
To save/update multiple records using a data array, you should first create the data array in this format -
$data = array(
array('Page' => array('id' => 1, 'order' => 0)),
array('Page' => array('id' => 2, 'order' => 0)),
array('Page' => array('id' => 3, 'order' => 0))
);
$this->Page->saveMany($data);
Now you can use saveMany to update the three records with the given id(primary key) with order 0. Note if you don't pass primary keys i.e id in the arrays, saveMany will just create new records for the given array.
Ref - http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savemany-array-data-null-array-options-array

CakePHP Save data in HABTM Table

I am trying to save an array like this
$myData = array(
'User' => array('id' => 17),
'Group' => array(
array('group_id' => 2),
array('group_id' => 3),
array('group_id' => 4),
array('group_id' => 5),
array('group_id' => 6)
)
);
In my HABTM join table (groups_users). I tried the following save calls, but none of them worked.
$this->User->save($myData);
$this->User->saveAssociated($myData);
$this->User->saveAll($myData);
$this->User->GroupsUsers->save($myData);
$this->User->GroupsUsers->saveAll($myData);
Before you ask: Yes, my associations are set-up correctly and I was able to save data by calling:
$this->User->GroupsUsers->saveAll(array(
0 => array(
'GroupsUsers' => array('user_id' => $id, 'group_id' => 1)
),
1 => array(
'GroupsUsers' => array('user_id' => $id, 'group_id' => 2)
)
));
BUT only one of both records are saved, although I set unique to false in the model's HABTM relationship definition.
Where is the error? Is the structure of my array invalid?
The problem is that your data array is incorrectly formatted.
Your Group model will probably have an id field, and your join model GroupsUser will have a group_id field.
So you need to change your group_id to id
try
$myData = array(
'User' => array('id' => 17),
'Group' => array(
'Group' => array(
0 => 2,
1 => 3,
2 => 4,
3 => 5,
4 => 6
)
)
);
Just to answer your question even if it's old.
You doesn't have to call your 'GroupsUsers' model in your saveAll call, this is precisely the goal of associating Models, cakePHP does that for you.
You should pass this array to a saveAll function on your User/Group model:
array{
array{
'User' => array {
'id' => 25
},
'Group' => array {
'id' => 2
}
}
array{
'User' => array {
'id' => 47
},
'Group' => array {
'id' => 2
}
}
}
Both your User and Group Models should have and HABTM relation with each other, so that you just have to do that:
From the User/Group controller:
$this->User/Group->SaveAll($myData);
From the User/Group Model:
$this->SaveAll($myData);
And that's it, the GroupsUsers table will save 2 records, by saving by yourself in the HABTM relation table, you are not using the power of cakePHP.
And the records in both User and Group tables will be created if the ID doesn't exist, updated if not.
The only reason to save in an HABTM table is if you have some extra information you want to save in this table such as a 'validated' fields if you want to validate a member after he asked to join a group for example.

Resources