CakePHP SaveMany not saving - cakephp

I have a issue with the SaveMany call in my CakePHP function.
The code below is my current attempt. This now sort of works, it does not make any errors at all but only the savefield will save any data, the saveMany, although it does not come back with errors does NOT save the data?
Please Help???
$InvoiceArrayData = array(
array('Invoicedata' => array('workdes' => $WorkHolder)),
array('Invoicedata' => array('price' => $PriceHolder)),
);
foreach($InvoiceArrayData as $InvoiceArrayKey => $InvoiceArrayValue) {
debug($InvoiceArrayValue);
$this->Invoicedata->saveMany($InvoiceArrayValue['Invoicedata']);
$this->Invoicedata->saveField('invoicejoblists_id', $MyJobIDInput);
$this->Invoicedata->saveField('invoicejoblists_id', $MyJobIDInput);
$this->Session->setFlash('Invoice Data Saved', 'default', array(), 'good');
//$this->redirect($this->Auth->redirect('/AdminInvoiceSystem/'));
}
When the code runs it does redirect with the saved meessaged but when the database is checked, only the invoicejoblist_id is saved.
Have not not done something right?
Glenn
UPDATE ::
Ok so what I am trying to do is to build an invoice system, I have a number of tables one of which 'invoicedatas' which has 'id, workdes, price, invoicejoblists_id' contained within it. ID is the key and is sent to auto count.
When the user starts to make a new invoice they fill in a form which saves in the other tables. But also in this form they input a work detail and price for that work.
This is then sent over to the function I am working on now, which returns no errors but does not save. Or I should say the saveMany call does not work, the saveField works and saves the ID number which I call from one of my other tables.
A bit of code I forget to add are the vars, which are just basic holders for the $this->data I am trying to save.
$WorkHolder = $this->data['workdes'];
$PriceHolder = $this->data['price'];
Please Help......
Glenn.
OK Here goes, I try and see if I can explain myself in more detail.
I am going to re-do so of my work to make it just plan easier for the UI apart form anything. To explain, I will now have a form which sets the invoice job, e.g jobnumber per user, user id, and other bits. This information will go into a table call invoicejoblist
The second table invoicedata holds just workdes and price along with invociejoblists_id (this links each row with the invoice job details).
But no matter what I do I can not get it to save into workdes and price. This is how my current test setup looks :
MY .cpt file:
$InvoiceMake = $this->Form->create('Invoice', array('url'=>'/InvoiceSet/', 'id' => 'InvoiceSet', 'inputDefaults' => array('div' => false) ) );
$InvoiceMake .= "<div id=\"InvoiceDataInput\">Please Input Description<br />";
$InvoiceMake .= $this->Form->textarea('Invoicedata.0.workdes');
$InvoiceMake .= $this->Form->textarea('Invoicedata.1.workdes');
$InvoiceMake .= $this->Form->textarea('Invoicedata.2.workdes');
$InvoiceMake .= $this->Form->textarea('Invoicedata.3.workdes');
$InvoiceMake .= $this->Form->textarea('Invoicedata.4.workdes');
$InvoiceMake .= "</div>";
$InvoiceMake .= $this->Form->submit('Make Invoice', array('div' => false, 'class' => 'ButtonInvoice'));
$InvoiceMake .= $this->Form->end();
echo $InvoiceMake;
My test function in controller:
function test() {
debug($this->data);
$HoldeME = $this->data;
$this->loadModel('Invoicedata');
$this->Invoicedata->saveAll($HoldeME);
}
These return not errors but the workdes information submitted will not save. The only thing I put in my model was:
public $belongsTo = array(
'Invoicejoblists' => array(
'className' => 'Invoicejoblists',
'foreignKey' => 'invoicejoblists_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
I did have some checks to make sure the inputs where not empty but remove them to see if that was my problem, not sure it that was right or not.
Hope that is better explained, I have gone down to the right basic level to just test save any data set but it still will not let me, is that because I should have something in my model?
Any help most welcome
Glenn.

I really have no clue what you're asking, but I'm giving it a blind-shot.
You should be able to save an item and as many sub/related items as you want in a single saveAll().
Just make sure your form (or the data) is formatted correctly per the copious instructions found in the CakePHP book.
As an example:
echo $this->Form->create('Invoice');
echo $this->Form->input('name');
echo $this->Form->input('Price.0.amount');
echo $this->Form->input('Price.1.amount');
echo $this->Form->end('Submit');
Then, when you do a saveAll() (or saveMany() - whatever), your Invoice will save as well as the two Prices (assuming Invoice is related to Price).
If you're trying to build the data manually, but don't know how it should be formatted, you can always create a form like above, submit it, and debug() the results to see how CakePHP would format it.
Hope that helps clarify for you.

Related

Settings page and multi edit rows

I would like to make a configuration page like this for my CMS
http://img4.hostingpics.net/pics/72087272ok.png
I want this page (admin/settings/index) gets me the various settings (ID 1 to 21) came to my table and in case of change, I can, in this form, do an update
After 3 days of not especially fruitful research I found something. I put in my SettingsController:
admin_index public function () {
if (!empty($this->data)) {
$this->Setting->saveAll($this->request->data['Setting']);
} else {
$this->request->data['Setting'] = Set::combine($this->Setting->find('all'),
'{n}. Setting.id', '{n}. Setting');
}
}
and my view:
<?php
echo $this->Form->create('Setting', array ('class' => 'form-horizontal'));
foreach($this->request->data['Setting'] as $key => $value) {
echo $this->Form->input('Setting.' . $key . '.pair');
echo $this->Form->input('Setting.' . $key . '.id');
}
echo $this->Form->end(array('class' => 'btn btn-primary', 'label' => 'Save',
'value' => 'Update!', 'div' => array ('class' => 'form-actions')));
?>
he is recovering well all my information, I can even update. The trouble is that I do not really know how I can do to get the same result as my first screenshot. He puts everything in the textarea while in some cases I want the checkbox and / or drop-down.
please help me or explain to me how to make a page that retrieves configuration information from my table and allows me to edit without use an address like admin/settings/edit/ID
My table settings is something like this here
http://img4.hostingpics.net/pics/724403settings.png
If you are asking about how to format the output to produce a "settings" page, you will need to do it manually or add some kind of metadata to your table schema, such as "field_type".
Now Cake will automatically generate a checkbox for a boolean or tiny int, for example, but as you have varchars, ints, texts and so on, this won't work for you (pair is presumably varchar or text to allow for the different possible values.
You cannot really automagically generate the outputs you want without telling Cake.
One option would be to add a field_type to the table, so:
key value field
site_name My Site input
site_online 1 checkbox
meta_desc [some text] textarea
and something like
foreach($settings as $setting) {
echo $this->Form->{$setting['field']}($setting['key'],
array('value' => $setting['value']));
}
might do what you are after.
Or if($key=='site_name') // output a textbox
but this is not exactly ideal.

cakephp echo count

Hi I have a like button which updates the likes table field type with like. I want to count the number of likes in the table, in my controller I have this,
// Load Likes Model and retrive number of likes and dislikes
$this->loadModel('Like');
$related_likes = $this->Like->find('count', array(
'conditions' => array('uploadid' => $id)
));
$this->set('likes', $related_likes);
}
How do I go about echoing the number of likes in my view??
Would really appreciate any help
When you use set(), the name you define becomes a variable in the view.
In your view:
echo $likes;

Complex find statement, don't know how to write multiple values for one condition

I am trying to build a paginated find call to my Unit model. I need the condition to be that it looks for unit.type of condo and rentalco, house and rentalco, but NOT rentalco and hotel. Additionally, the way I have my code worded, cake only returns unit types that are rentalco.
public function view($type=null) {
$this->set('title', 'All '.$type.' in and near Gulf Shores');
$this->set('featured', $this->Unit->getFeatured());
$this->paginate['Unit']=array(
'limit'=>9,
'order' => 'RAND()',
'contain'=>array(
'User'=>array('id'),
'Location',
'Complex',
'Image'
),
'conditions'=>array(
'Unit.type'=>array($type, 'rentalco'),
'Unit.active'=>1)
);
$data = $this->paginate('Unit');
$this->set('allaccommodations', $data);
$this->set('type', $type);
}
UPDATE I figured out why my find statement wasn't working (just had been passing the word condos instead of condo into my browser bar....derp derp); however, I would still love to know how I can tell cake to NOT allow a find with both type hotel and rentalco.
You are looking for the NOT. It would be something like:
'conditions' => array(
'NOT' => array('Unit.type' => array('hotel', 'rentalco')),
),
To be more specific, I would need to see your model schema.

CakePHP AutoComplete Question

I am working on a book review application and I am using autoComplete to search for titles in when creating a review. The review model has an associated book_id field and the relationship is setup as the review hasOne book and a book hasMany reviews.
I am trying to pass the Book.id (into the book_id field), but I want to display Book.name for the user to select from. With the default setup (accomplished via CakePHP's tutorial), I can only pass Book.name. Is it possible to display the name and pass the id?
Also, I am passing it via the following code in the create() action of the review controller:
$this->data['Review']['book_id'] = $this->data['Book']['id'];
Is that the proper way to do it in CakePHP? I know in Ruby on Rails, it is automatic, but I can't seem to make it work automagically in CakePHP. Finally, I am not using the generator because it is not available in my shared hosting environment... so if this is the wrong way, what do I need other than associates in my models to make it happen automatically?
Thanks for the help and I promise this is my question for awhile...
UPDATE- I tried the following, but it is not working. Any ideas why?
function autoComplete() {
$this->set('books', $this->Book->find('all', array(
'conditions' => array(
'Book.name LIKE' => $this->data['Book']['name'].'%'
),
'fields' => array('id','name')
)));
$this->layout = 'ajax';
}
The problem is that when I use the code above in the controller, the form submits, but it doesn't save the record... No errors are also thrown, which is weird.
UPDATE2:
I have determine that the reason this isn't working is because the array types are different and you can't change the array type with the autoComplete helper. As a workaround, I tried the follow, but it isn't working. Can anyone offer guidance why?
function create() {
if($this->Review->create($this->data) && $this->Review->validates()) {
$this->data['Review']['user_id'] = $this->Session->read('Auth.User.id');
$this->Book->find('first', array('fields' => array('Book.id'), 'conditions' => array('Book.name' => $this->data['Book']['name'])));
$this->data['Review']['book_id'] = $this->Book->id;
$this->Review->save($this->data);
$this->redirect(array('action' => 'index'));
} else {
$errors = $this->Review->invalidFields();
}
}
FINAL UPDATE:
Ok, I found that the helper only takes the find(all) type or array and that the "id" field wasn't passing because it only applied to the autoComplete's LI list being generated. So, I used the observeField to obtain the information and then do a database lookup and tried to create a hidden field on the fly with the ID, but that didn't work. Finally, the observeField would only take the characters that I put in instead of what I clicked, due to an apparent Scriptaculous limitation. So, I ended up going to a dropdown box solution for now and may eventually look into something else. Thanks for all of the help anyway!
First of all, $this->data will only contain ['Book']['id'] if the field exists in the form (even if it's hidden).
To select something by name and return the id, use the list variant of the find method, viz:
$selectList = $this->Book->find('list', array(
'fields' => array(
'id',
'name'
)));
$this->set('selectList', $selectList);
In the view, you can now use $selectList for the options in the select element:
echo $form->input('Book.id', array('type' => 'hidden'));
echo $form->input('template_id', array(
'options' => $selectList,
'type' => 'select'
));

HABTM form validation in CakePHP

I have a Projects table and a Users table which are linked by a HABTM relation. In the "add" new Project page I have a multiple checkbox section to select Users for the new project. I want to have at least one User for the Project. What's the best way to approach this in CakePHP ?
Try this:
// app/models/project.php
/**
* An additional validation check to ensure at least one User is
* selected. Spoofs Cake into thinking that there are validation
* errors on the Project model by invalidating a non-existent field
* on the Project model, then also invalidates the habtm field as
* well, so when the form is re-displayed, the error is displayed
* on the User field.
**/
function beforeValidate() {
if (!isset($this->data['User']['User'])
|| empty($this->data['User']['User'])) {
$this->invalidate('non_existent_field'); // fake validation error on Project
$this->User->invalidate('User', 'Please select at least one user');
}
return true;
}
I stumbled on the same issue, but now - 3 years later - with CakePHP 2.3.
To be clear; Group has and belongs to User. I've had a form like this:
// View/Groups/add.ctp
echo $this->Form->input('name');
echo $this->Form->input('User');
With the validation rule like in user448164's answer:
// Model/Group.php
public $validate = array(
'User' => array(
'rule' => array('multiple', array('min' => 1)),
'message' => 'Please select one or more users'
)
);
That didn't work, after Googling for it, I found this question which couldn't still be the best solution. Then I tried several things, and discovered this to work just fine:
// View/Groups/add.ctp
echo $this->Form->input('name');
echo $this->Form->input('Group.User');
Way too easy solution, but had to dig into it to find out it works this way.
Hopefully it helps somebody one day.
Update for CakePHP 2.4.x (possibly 2.3.x as well)
When I wrote this answer, I was using CakePHP 2.3.x. Back then it worked perfectly for both validating and saving the data. Now when applying the same code on a new project, using CakePHP 2.4.x, it didn't work anymore.
I created a test case, using the following code:
$data = array(
'User' => array(
'Client' => array(8)
),
);
$this->User->create();
$this->User->saveAll($data);
My first thought was: Saving all means saving all "root" models, what actually makes sense to me. To save deeper than just the "root" ones, you'll have to add the deep option. So I ended up with the following code:
$data = array(
'User' => array(
'Client' => array(8)
),
);
$this->User->create();
$this->User->saveAll($data, array('deep' => true));
Works like a charm! Happy coding. :)
Update (2014/03/06)
Struggling with the same problem again, in this case with hasMany instead of habtm. Seems like it behaves the same way. But I found myself looking for this answer again, and got confused.
I'd like to make clear that it's key to use Group.User instead of User in your input. Else it won't use the User model validation.
I've just been looking at his problem myself on a project and came across a slightly more elegant solution, as long as you're only dealing with a habtm relationship and you need to ensure that at least one checkbox is selected.
so for example you're editing a Project and you want it to be associated with at least one user
Add this to beforeValidate()
// check habtm model and add to data
foreach($this->hasAndBelongsToMany as $k=>$v) {
if(isset($this->data[$k][$k]))
{
$this->data[$this->alias][$k] = $this->data[$k][$k];
}
}
In the validation rules add the following:
'User' => array(
'rule' => array('multiple', array('min' => 1)),
'message' => 'Please select one or more users'
)
teknoid's blog has a pretty in depth solution to your issue here. The most Cakey way of doing this would be to add custom validation to your model, as you mention in your comment above. Check out http://teknoid.wordpress.com/2008/10/16/how-to-validate-habtm-data/
From the article, where Tag HABTM Post (:: Project HABTM Users):
First, we validate the Tag model, by
using the data from the form to ensure
that at least one Tag was selected. If
so, we save the Post and the relevant
Tags.
2016 update for CakePhp 2.7
Full answer here : HABTM form validation with CakePHP 2.x
TL;DR;
AppModel.php
public function beforeValidate($options = array()){
foreach (array_keys($this->hasAndBelongsToMany) as $model){
if(isset($this->data[$model][$model]))
$this->data[$this->name][$model] = $this->data[$model][$model];
}
return true;
}
public function afterValidate($options = array()){
foreach (array_keys($this->hasAndBelongsToMany) as $model){
unset($this->data[$this->name][$model]);
if(isset($this->validationErrors[$model]))
$this->$model->validationErrors[$model] = $this->validationErrors[$model];
}
return true;
}
In the main model of your HABTM :
public $validate = array(
'Tag' => array(
'rule' => array('multiple', array('min' => 1)),
'required' => true,
'message' => 'Please select at least one Tag for this Post.'
)
);
If you are using CakePHP 2.3.x, you may need to add this code to your model in addition to the code that GuidoH provided, otherwise your HABTM model data may not save:
public function beforeSave($options = array()){
foreach (array_keys($this->hasAndBelongsToMany) as $model){
if(isset($this->data[$this->name][$model])){
$this->data[$model][$model] = $this->data[$this->name][$model];
unset($this->data[$this->name][$model]);
}
}
return true;
}
As per my comment on Guido's answer above, I use Guido's answer exactly, however I modify the data with the beforeSave callback before it saves to the database.
I have this issue on Cake 2.4.5+
public function beforeSave($options = array()) {
$temp = $this->data['Group']['User'];
unset($this->data['Group']['User']);
$this->data['User']['User'] = $temp;
return true;
}

Resources