CakePHP update a child model from parent - cakephp

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 . '"')
);

Related

cakephp saveAll associated model field is not being saved

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.

CakePHP find HABTM

I have 3 models (User, Message and Tag) with the following relations:
User hasMany Message
Message belongsto User
Message HABTM Tag
Tag HABTM Message
If a User is logged in he might want to see all Message tagged with something.
$messages = $this->Message->find('all', array(
'conditions' => array("Message.user_id" => $this->uid),
'contain' => array(
'Tag' => array(
'conditions' => array(
'Tag.id' => $activetag['Tag']['id']
)
)
));
However, this find will return ALL messages of that user. (Containable behaviour is included in both models)
Containable on child (tag) does not perform filter on the parent (message), that's why all the messages are returned. The containable only place condition on the tag itself, in your case, messages not matching $activeTag would still get returned but with empty tag array attached, while messages matching would return with an array containing only one tag, the $activeTag, but all messages would get returned.
For your purpose CakepHP recommend using join function for filtering with HABTM, it joins hasOne or belongsTo for you automatically but when it comes to HABTM you may need to perform the join yourself if needed.
assuming tables are named conventionally:
$this->Message->recursive = -1;
$options['joins'] = array(
array('table' => 'messages_tags',
'alias' => 'MessageTag',
'type' => 'INNER',
'conditions' => array(
'Message.id = MessageTag.message_id',
)
) );
$options['conditions'] = array(
'MessageTag.tag_id' => $activetag['Tag']['id'],
'Message.user_id' => $this->uid );
$message = $this->Message->find('all', $options);
more info here:
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
In your Model Message add
**
* #see Model::$actsAs
*/
public $actsAs = array(
'Containable',
);
/**
* #see Model::$belongsTo
*/
public $belongsTo = array(
'Message' => array(
'className' => 'Message',
'foreignKey' => 'message_id',
),
'Tags' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id',
),
);
in your controller :
// $tagsId = tags ids
$message = $this->MessageTag->find('all', array('conditions' => array('MessageTag.tag_id' => $tagsId),'contain' => array('Message')));
also is better follow cake naming convention, if you have tags(plural), message_tags(first singular second plural),messages(plural) tables you must have Tag,MessageTag,Message Models.

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')
)
));

cakephp model with hasmany

I have question in cakephp model,
I want to add dynamic condition in var $hasMany keyword
I want to add condition like current user_id, i got user Id after my login.
var $hasMany = array(
"AskComment"=>array('limit'=>3),
'AskStatistic',
'AskContactsLink',
'AskStatistic',
'AskObject',
'AskLikes'
);
If you want to add dynamic condition in your model, then you might have to bind the model association-ship dynamically into your controller's code. Write the following code into your controller's method for which you want to impose some new condition on the existing/new associated models.
$this->PrimaryModel->bindModel(array('hasMany' => array(
'AskComment' => array(
'className' => 'AskComment',
'foreignKey' => 'primary_id',
'conditions' => array('AskComment.user_id' => $user_id)
)
)
));
Take a look at this link: Creating and destroying associations on the fly. This will surely help you to achieve the same.
I think its better to put your association in the construct function of your Model.
like this:
/**
* #see Model::__construct
*/
public function __construct($id = false, $table = null, $ds = null) {
public $hasMany = array(
'AskComment' => array(
'className' => 'AskComment',
'foreignKey' => 'primary_id',
'conditions' => array(
'AskComment.user_id' => $user_id,
),
),
);
}

CakePHP 2.0: display associated records

I know the solutions is simple and might have something to do with the Containable behavior but I can't get it working. Without all the tries
This is the case. I'd like to display the Event details of an Event (eg. a conference). Each event takes place in a EventVenue and each EventVenue is located in a Country.
So in the Country Model the following is present:
public $hasMany = array(
'EventVenue' => array(
'className' => 'EventVenue',
'foreignKey' => 'country_id'
))
In the EventVenue model a BelongsTo association is made
public $belongsTo = array(
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id'
))
And in the Event model a hasOne association is made
public $hasOne = array(
'EventVenue' => array(
'className' => 'EventVenue',
'foreignKey' => 'event_id',
))
What I want is to display the country name on the page that is renderd in the EventsController. I do get all the Event and EventVenue data but the associated Country for the venue is not retrieved.
The data is retrieved in the following way
$item = $this->Event->findBySlug($slug);
How can I also get the country name (eg. Netherlands) retrieved from the database? I tried something like this but that did not work:
$item = $this->Event->findBySlug($slug,array(
'contain' => array(
'Event' => array(
'EventVenue' => array(
'Country'
)
)
)
)
Try this:
$item = $this->Event->findBySlug($slug,array(
'contain' => array(
'EventVenue' => array(
'Country'
)
)
);
Update
Turns out findBy does not support Containable. You could use this to get the desired result:
$item = $this->Event->find('first',array(
'conditions' => array(
'Event.slug' => $slug
),
'contain' => array(
'EventVenue' => array(
'Country'
)
)
);
Oh and make sure you have this in the model: public $actsAs = array('Containable');
try this method:
$this->Event->recusive = 2;
$item = $this->Event->findBySlug($slug);
You need to set the recursive to 2 before you make the find, something like this
$this->Event->recursive = 2;
with this, you'll get the Event, the EventVenue and the Country on one shot
Hope it helps

Resources