cakephp saveAll associated model field is not being saved - cakephp

I'm currently using cakephp 2.2.3.
I have the following Model Associations:
VehicleModel -> Vehicle -> Order
Plan -> Order
Vehicle HABTM Tag
Inside the Vehicle controller, add action, I have:
if(!empty($this->request->data)) {
if($this->Vehicle->saveAll($this->request->data)) {
$this->Session->setFlash('Vehicle was successfully added.');
}
}
The $this->request->data array is formatted like this:
array(
'VehicleModel' => array(
'category_id' => '2',
'make_id' => '1'
),
'Order' => array(
'plan_id' => '2'
),
'Vehicle' => array(
'vehicle_model_id' => '13',
'price' => ' 8700',
'year' => '1994',
'km' => '100',
'color' => '61',
'fuel' => '1',
'gear' => '20',
'type' => '51',
'city' => 'Rio de Janeiro',
'state' => 'RJ'
),
'Tag' => array(
'Tag' => array(
(int) 0 => '69',
(int) 1 => '11'
)
)
)
The orders table has the following fields:
id , plan_id , vehicle_id , created , modified.
Vehicle Model:
class Vehicle extends AppModel {
public $belongsTo = array('User' , 'VehicleModel');
public $hasMany = array('Order' , 'Image');
public $hasAndBelongsToMany = array('Accessory' , 'Tag');
}
Order Model:
class Order extends AppModel {
public $belongsTo = array('Vehicle' , 'Part' , 'Plan');
public $validate = array(
'plan_id' => array(
'rule' => 'notEmpty'
)
);
}
The problem I'm having is that the Order.plan_id field is not being saved, although all other fields are being saved normally. What can I be doing wrong?

Just to be clear, When I do the multiple saving manually, everything
works just fine. I mean, when I write:
$this->Vehicle->save()
and then set
$this->request->data['Order']['vehicle_id'] = $this->Vehicle->id
and finally
$this->Vehicle->Order->save()
everything works just fine. It's the saveAll that is causing me
trouble.
If that is the case, see where $this->request->data['Order']['vehicle_id'] = $this->Vehicle->id. Comparing this to your var dump above, order never contains the relation to the main model, which leads me to ask if this is a new record you are trying to save or an update? I think you might have to not go with a saveAll here if you are setting a new record because the main id is not yet set. Please see:
http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-related-model-data-hasone-hasmany-belongsto
Particularly: "If neither of the associated model records exists in the system yet (for example, you want to save a new User and their related Profile records at the same time), you’ll need to first save the primary, or parent model."
They basically do the long version you are doing.

Related

CakePHP update a child model from parent

I am currently struggling to get an associated model to update with CakePHP 2.3. I want the parent model to be inactive, and the child model record to be inactive. I have the following method in my Company Model, which just updates the parent model (and not the child). What is the best way to achieve this? I tried moving the method to the child object, but it makes no difference.
class Company extends AppModel {
public $hasMany = array(
'CompaniesUser' => array(
'className' => 'CompaniesUser'
)
);
function updateToInactive( $companyId ){
$data = array(
array(
'Company' => array(
'id' =>$companyId,
'active' => 0,
'payment_active'=> 0
),
'CompaniesUser' => array(
'company_id' =>$companyId,
'active' => 0)
)
);
$this->saveAll($data);
}
}
My CompaniesUser Model looks like this:( belongs to Company )
class CompaniesUser extends AppModel {
public $belongsTo = array(
'Company' => array(
'className' => 'Company',
'foreignKey' => 'company_id'
)
);
Your $data array looks wrong to me for a hasMany relationship. It should look something more like this:-
$data = array(
'Company' => array(
'id' =>$companyId,
'active' => 0,
'payment_active'=> 0
),
'CompaniesUser' => array(
array(
'company_id' =>$companyId,
'active' => 0
)
)
);
$this->saveAssociated($data);
It is also better to use saveAssociated() than saveAll() as you are saving associated data.
Update
The above will not update the existing CompaniesUsers unless you pass the primary keys with the save data. You could save the company first then use updateAll() to update the users data for the related company. If you use updateAll() you need to remember to escape the values being saved:-
// Update company
$data = array(
'id' =>$companyId,
'active' => 0,
'payment_active'=> 0
);
$this->save($data);
// Update company users
$this->CompaniesUser->updateAll(
array('CompaniesUser.active' => '"0"'),
array('CompaniesUser.company_id' => '"' . $companyId . '"')
);

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!

Getting value of a field in a nesting relation

I have two tables with a relation. The find method works very well without problem, i get all the fields that i want between both tables. The delete method work fine also.
But i want to add a validation before delete the record, verifiying the relation with the secondary table, if the relation exists can't delete the record.
My Tables:
invoices: id, company_id, number, invoice_date
invoice_numbers: id, company_id, number
companies: id, name
My Models:
Invoice:
class Invoice extends AppModel
{
public $belongsTo = array(
'Company',
'InvoiceNumber' => array(
'className' => 'InvoiceNumber',
'foreignKey' => false,
'conditions' => array(
'InvoiceNumber.company_id = Invoice.company_id',
'InvoiceNumber.number = Invoice.number',
),
'type' => 'left',
'fields' => array(
'FacturaNumero.id',
),
)
);
}
InvoiceNumber:
class InvoiceNumber extends AppModel
{
public $hasOne = array(
'Invoice' => array(
'className' => 'Invoice',
'foreignKey' => false,
'conditions' => array(
'Invoice.company_id = InvoiceNumber.company_id',
'Invoice.number = InvoiceNumber.number',
),
'type' => 'left',
'fields' => array(
'Invoice.invoice_date',
),
)
);
}
My Controller (delete Method):
public function delete($id)
{
$this->InvoiceNumber->id = $id;
if (!$this->InvoiceNumber->exists()) {
throw new NotFoundException('Invalid Record.');
}
/**
* I want to add a validation here if field invoide_date is not null, by example
* but this dont work, because get the first value in the invoice table.
*/
if (!empty($this->InvoiceNumber->Invoice->field('invoice_date')) {
throw new NotFoundException('I Cant Delete this record.');
} else {
$this->InvoiceNumber->delete();
}
}
The misunderstanding here is that when you write
$this->InvoiceNumber->Invoice
you are just accesing a generic Invoice Model Object and not the Invoice of that specific InvoiceNumber
I think that you just need to change your code in
$InvoiceNumber = $this->InvoiceNumber->read();
if (!empty($InvoiceNumber['Invoice'])) {
// ...
}
but maybe this is not the best way: I think that a cleaner approach could be to do the validation in the InvoiceNumber beforeDelete() method
PS: sorry for the typos. Now should be correct

CakePHP - How do I join a table on a joined table?

I am in the FilesController and I'm trying to get a file based on the condition that its order belongs to the current user.
FilesController
// Check the file exists and that it belongs to the user
$this->File->find('first', array(
'conditions' => array(
'File.id' => $id,
'Order.Customer.id' => $this->Auth->user('id')
),
'recursive' => 2
));
Cake SQL Error
Unknown column 'Order.Customer.id' in 'where clause'
I'm trying to get the SQL to left join orders onto files and then left join customers onto orders, but I can't figure out how to join the customers table, although I'm sure I've done this before. I've tried everything I can think of with the conditions, and using contains.
Here's my model relationships:
Customer Model
class Customer extends AppModel {
public $hasMany = array('Order');
}
Order Model
class Order extends AppModel {
public $belongsTo = array('Customer');
public $hasMany = array('File');
}
File Model
class File extends AppModel {
public $belongsTo = array('Order');
}
Try joining the tables using the 'joins' parameter. Sometimes 'contains' doesn't work and you need to fall back on this.
$this->File->find('first', array(
'conditions' => array(
'File.id' => $id,
'Order.Customer_id' => $this->Auth->user('id')
),
'joins' => array(
array(
'table' => 'orders',
'alias' => 'Order',
'type' => 'LEFT',
'conditions' => array('File.orders_id = Order.id')
),
array(
'table' => 'customers',
'alias' => 'Customer',
'type' => 'LEFT',
'conditions' => array('Customer.orders_id = Order.id')
)
)
));
You may want to use Containable (http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html) as it is the easiest solution. You can not use Order.Customer.id as Cake does not nest conditions like that. Manual joins would also work.
$this->loadModel('Customer');
$customer = $this->Customer->findById($this->Auth->user('id'), array(
'conditions' => array(
'File.id' => $id
),
'recursive' => 2
));
Orders will be accessible as:
pr($customer['Order']);
File will be accessible as:
pr($customer['File']);
I realised there is no need to actually join the customers table in this instance, because the orders table already has the customer_id.
$this->File->find('first', array(
'conditions' => array(
'File.id' => $id,
'Order.customer_id' => $this->Auth->user('id')
)
));

Lack of 'required' fields in form suggests I am not correctly associating models

I'm having trouble validating an input form and saving the data from that form. I suspect that perhaps these are both caused by an association problem, though I am not sure.
I'm using CakePHP 2.2.0 RC2.
I have three models: User, Member and Address. Each user can have several members in their account and each user can have many addresses (it remembers past addresses, too).
My model associations are:
User model:
class User extends AppModel {
public $name = 'User';
public $hasMany = array(
'Member' => array(
'className' => 'Member',
'foreignKey' => 'user_id',
'order' => 'Member.member ASC',
'dependent' => true
),
'Address' => array(
'className' => 'Address',
'foreignKey' => 'user_id',
'order' => 'Address.address ASC',
'dependent' => true
)
);
...
Member model:
class Member extends AppModel {
public $name = 'Member';
public $displayField = 'member';
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
...
Address model:
class Address extends AppModel {
public $name = 'Address';
public $displayField = 'address';
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
...
For my Form create statement, I have:
$this->Form->create( 'Member', array( 'url' => array( 'action' => 'add' ) ) );
I am using the Member model and controller to handle the form as it submits either only Member fields, or Member and Address fields, depending on whether the user already has an address entry or not (if the user doesn't, it adds address input fields to the form, otherwise it doesn't).
I am not submitting any User fields in the form.
Now, I would expect, if my associations were set up correctly, that any 'required' fields in any of the models would have an asterisk next to them. There are some fields set as required in both the Member and Address models, and while the Member fields show up on the form as being required (with an asterisk), the Address fields do not.
An example of required Address field code from the Address model:
public $validate = array(
'street1' => array(
'required' => array(
'rule' => array( 'notEmpty' ),
'required' => true, 'allowEmpty' => false,
'message' => 'Please enter a street address'
),
....
And here's some of my view/form code:
echo $this->Form->input( 'Member.firstname', array( 'label' => 'First name', 'type' => 'text' ) );
echo $this->Form->input( 'Member.middlename', array( 'label' => 'Middle name', 'type' => 'text' ) );
echo $this->Form->input( 'Member.lastname', array( 'label' => 'Last name', 'type' => 'text' ) );
echo '<p class="bold">Please enter current address details:</p>';
echo $this->Form->input( 'Address.street1', array( 'label' => 'Street address (line 1)', 'type' => 'text' ) );
echo $this->Form->input( 'Address.street2', array( 'label' => 'Street address (line 2)', 'type' => 'text' ) );
echo $this->Form->input( 'Address.suburb', array( 'label' => 'Suburb', 'type' => 'text' ) );
...
When I output $validationErrors on the form page (before form submission), I get this:
Array
(
[Member] => Array
(
)
[User] => Array
(
)
)
I'm assuming there should also be an [Address] section there.
I can't spot anything obvious but I'm new to CakePHP (though I do have PHP experience), so any ideas would be appreciated.
Thanks!
Peter.
Firstly you said you were using a release candidate (RC2) of Cake 2.2. There is now a stable release and I strongly encourage you to upgrade to that.
You create the form with the Member model but it doesn't have a direct relation(association) to Address. Address is associated to User. If you create the form via the User model it should work all right.
If this scenario - creating the form with the User model - doesn't work for you and your application
logic than your Database model and your Model associations are wrong and they do not comply with your business needs. If this is the case you should consider changing the Architecture of the application.
And a note on $uses: you shouldn't have to include the Address model in your controller's $uses in order to be able to validate inputs. Controller::$uses is for use of models (other than the controller's own model) directly in the Controller itself. For example:
public $uses = array('Article', 'News');
in any controller would make all Article and News Models' methods available in that controller with:
$this->Article->methodName();
or
$this->News->methodName();

Resources