cakephp: hasMany not working - cakephp

device.php:
var $hasOne = array(
'Monitordevice' => array(
'className' => 'Monitordevice',
'foreignKey' => 'deviceId', // 'deviceId'
'conditions' => '',
)
);
var $hasMany = array(
'CreativeSchedule' => array(
'className' => 'CreativeSchedule',
'foreignKey' => 'deviceId',
'conditions' => '',
)
);
creative_schedules.php
var $belongsTo = array(
'Device' => array(
'className' => 'Device',
'foreignKey' => 'deviceId',
'conditions' => '',
)
);
when I call this:
$all_devices=$this->Device->CreativeSchedule->find('all');
device table is not making any join with creative_schedules
instead I get 2 queries:
SELECT `Device`.`id`, ....FROM `devices` AS `Device` LEFT JOIN `monitordevices` AS `Monitordevice` ON (`Monitordevice`.`deviceId` = `Device`.`id`)
AND
SELECT `CreativeSchedule`.`id`, ....FROM `creative_schedules` AS `CreativeSchedule` WHERE `CreativeSchedule`.`deviceId` IN (1, 2, 3, 4, 5, 6, 7)

Use Containable.
In both of these classes, add:
var $actsAs = array( 'Containable' );
Then your find should look like:
$all_devices = $this->Device->CreativeSchedule->find('all', 'contain' => array('Device'));
As a side note, I'm not sure where you're putting the find(), and if that controller uses CreativeSchedule, but consider just $this->CreativeSchedule->find(...) if you can, as model chaining can lead to reduced performance, and it does not "link" the queries together through their associations. (Which I'm not sure if you were assuming or not.)
At worse case scenario, you could always put $this->loadModel('CreativeSchedule') at the top of the controller action if you would like.

Related

Cakephp 2 retrieve data where condition depends on child table

I am having a strange behavior which I do not understand with my cakephp 2.
In my Model 'Study' I have a has many relation:
class Study extends AppModel {
public $hasOne = array(
'SubjectFilter' => array(
'className' => 'Subject_filter',
'dependent' => true
)
);
public $hasMany = array(
'ExecutedStudyTable' => array(
'className' => 'ExecutedStudyTable',
'foreignKey' =>'study_id',
'dependent' => true,
),
);
The ExecutedtStudyTable Model looks like this:
class ExecutedStudyTable extends AppModel {
public $belongsTo= array(
'Study' => array(
'className' => 'Study',
'foreignKey' => 'study_id'
),
'User' => array(
'className' => 'User',
'foreignKey' => false,
'conditions' => array('ExecutedStudyTable.user_id = User.id')
)
);
}
When retrieving data everything looks good until I try to do this:
$studies = $this -> Study -> find('all',array(
'conditions' => array(
'Study.user_id' => $cId,
'OR'=>array(
array('Study.state'=>'active'),
array('Study.state'=>'rehearsal')
),
'SubjectFilter.studyCode'=>null,
'ExecutedStudyTable.user_id'=>$user
)
));
I get this error: Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'ExecutedStudyTable.user_id' in 'where clause'
Cakephp builds this query like this:
SELECT `Study`.`id`, `Study`.`user_id`, `Study`.`created`, `Study`.`modified`, `Study`.`started`, `Study`.`completed`, `Study`.`title`, `Study`.`description`, `Study`.`numberParticipants`, `Study`.`state`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`firstName`, `User`.`familyName`, `User`.`email`, `User`.`addressStreet`, `User`.`addressCity`, `User`.`addressZip`, `User`.`phone1`, `User`.`phone2`, `User`.`created`, `User`.`modified`, `User`.`passwordResetCode`, `User`.`passwordResetDate`, `User`.`sendUsernameDate`, `SubjectFilter`.`id`, `SubjectFilter`.`study_id`, `SubjectFilter`.`m`, `SubjectFilter`.`f`, `SubjectFilter`.`age18_24`, `SubjectFilter`.`age25_34`, `SubjectFilter`.`age35_44`, `SubjectFilter`.`age45_54`, `SubjectFilter`.`age55plus`, `SubjectFilter`.`studyCode` FROM `eyevido`.`studies` AS `Study` LEFT JOIN `eyevido`.`users` AS `User` ON (`Study`.`user_id` = `User`.`id`) LEFT JOIN `eyevido`.`subject_filters` AS `SubjectFilter` ON (`SubjectFilter`.`study_id` = `Study`.`id`) WHERE `Study`.`user_id` = 1402402538 AND ((`Study`.`state` = 'active') OR (`Study`.`state` = 'rehearsal')) AND `SubjectFilter`.`studyCode` IS NULL AND `ExecutedStudyTable`.`user_id` = 1130451831
The ExecutedStudyTable is not joined like the SubjectFilter and no alias is defined for the table. Why is this working correctly on the hasOne relation and not on the hasMany? Where do I make the mistake?
I appreciate your replies
If you want to filter a model by associated model's field, one way is by Joining tables.
Note: Mention proper table name for model ExecutedStudyTable. Also mention the join condition between two tables.
$studies = $this->Study->find('all',array(
'fields' => array('Study.*'),
'joins' => array(
array('table' => 'executed_study_table', // Table name
'alias' => 'ExecutedStudyTable',
'type' => 'INNER',
'conditions' => array(
'ExecutedStudyTable.<field name> = Study.<field>', // Mention join condition here
)
)
),
'conditions' => array(
'Study.user_id' => $cId,
'OR'=>array(
array('Study.state'=>'active'),
array('Study.state'=>'rehearsal')
),
'SubjectFilter.studyCode' => null,
'ExecutedStudyTable.user_id' => $user
),
'recursive' => -1
));

cake 2.5.1 bind 2 models together

I have a model Ranking which holds a contact_id and belongsTo Model Contact.
Model Contact has a costumer_id and belongsTo Model Costumer.
And hasMany Rankings.
There is also a Model Product which hasMany Ranking.
On a statistics page I select
$this->Product->recursive = 1;
$this->set('products', $this->Paginator->paginate())
;
and I get the array of
array(
'Product' => array(
'id' => '69',
),
'Ranking' => array(
(int) 0 => array(
'id' => '29',
'contact_id' => '9',
'product_id' => '69',
'ranking' => '9',
),
I would like to bind now the Contact and Costumer to the ranking based on the contact_id.
Is this manually possible via bindModel?
If yes, how can I do that?
I tried to set $this->Product->recursive = 1; to 2 and 3, but that select so many other things which I would need to clear with unbindModel... So I hope there is a smarter way of bind those model to get to the data...?
What you basically want to use is containable behavior. With this behavior you are able to filter and limit model find operations. You have the possibility to add this behavior on model level or at the controller to avoid side effects if the application has already grown to a complicated level.
Example from Cake-Book:
// Activate Containable Behavior on the fly in a controller
$this->User->Behaviors->load('Containable');
$this->User->contain();
$this->User->find('all', array(
'contain' => array(
'Profile',
'Account' => array(
'AccountSummary'
),
'Post' => array(
'PostAttachment' => array(
'fields' => array('id', 'name'),
'PostAttachmentHistory' => array(
'HistoryNotes' => array(
'fields' => array('id', 'note')
)
)
),
'Tag' => array(
'conditions' => array('Tag.name LIKE' => '%happy%')
)
)
)
));
Hope this gives you a push into the right direction.
using find it will get me the right data with this:
$this->set('products', $this->Product->find('all', array(
'contain' => array(
'Ranking' => array(
'Contact' => array(
'foreignKey' => 'contact_id',
'Customer' => array(
'foreignKey' => 'customer_id',
)
)
)
)
)));
When using the Paginator it looks like
$this->Paginator->settings['contain'] = array(
'Ranking' => array(
'Contact' => array(
'foreignKey' => 'contact_id',
'Customer' => array(
'foreignKey' => 'customer_id',
)
)
)
);
$this->Product->Behaviors->load('Containable');
$this->set('products', $this->Paginator->paginate());
Thanks so much!!

Simple one to many relationship cakePHP

So there are to classes that have a relationship one to many.
Event Type:
var $hasMany = array(
'Event' => array(
'className' => 'Event',
'foreignKey' => 'event_type_initials',
'dependent' => false,
)
);
Event:
var $belongsTo = array(
'EventType' => array(
'className' => 'EventType',
'foreignKey' => 'event_type_initials'
)
);
Previously the relationship was by the event_type_id, but now it was changed to initals. The problem occurs in the query when trying to access the data. At the end of the following query you can see that the left join is Event.event_type_initials = EventType.id, which doesnt make sense.
SELECT `Event`.`id`, `Event`.`event_type_initials`, `Event`.`user_id`, `Event`.`details`, `Event`.`start`, `Event`.`hours`, `Event`.`minutes`, `Event`.`all_day`, `Event`.`active`, `Event`.`created`, `Event`.`modified`, `EventType`.`id`, `EventType`.`initials`, `EventType`.`name`, `EventType`.`address`, `EventType`.`email`, `EventType`.`phone`, `EventType`.`person`, `EventType`.`color` FROM `sunshine3`.`events` AS `Event` LEFT JOIN `sunshine3`.`event_types` AS `EventType` ON (**`Event`.`event_type_initials` = `EventType`.`id`**) WHERE `Event`.`id` = 30
Any help is more than welcome.
If you specify a foreignKey it will always match to the id of the related table.
You need to use conditions here, though:
// this is important for the correct left joins
var $belongsTo = array(
'EventType' => array(
'className' => 'EventType',
'foreignKey' => false,
'conditions' => array('Event.event_type_initials = EventType.initials')
)
);
// for has many this is usually not necessary/possible (1:n), you can try though
var $hasMany = array(
'Event' => array(
'className' => 'Event',
'foreignKey' => false,
'conditions' => 'Event.event_type_initials = EventType.initials'
)
);

what is CakePHP model alias used for?

In user model:
var $hasMany = array(
'Photo' => array(
'className' => 'Photo',
'foreignKey' => 'owner_id',
...
),
);
In photo model:
var $belongsTo = array(
'Owner' => array(
'className' => 'User',
'foreignKey' => 'owner_id',
...
),
);
Here one user has many photos.
So what my question is that here the alias name is 'Owner', which make me clear to understand the exact meaning of 'User', but is this the only reason to use alias? does it affect 'Photo' in user model? or how to use 'Owner' in/by cakephp?
I don't quite understand the meaning of alias in model.
Appreciate your help!
Two useful scenarios for aliases:
1. Multiple foreign keys to the same model
For example, your photos table has two fields: created_user_id & modified_user_id
var $belongsTo = array(
'CreatedUser' => array(
'className' => 'User',
'foreignKey' => 'created_user_id',
...
),
'ModifiedUser' => array(
'className' => 'User',
'foreignKey' => 'modified_user_id',
...
),
);
2. Creating logical words specific to your application's domain
Using the conditions field in the array, you could specify different kinds of models:
var $hasMany = array(
'ApprovedUser' => array(
'className' => 'User',
'foreignKey' => 'group_id',
'conditions' => array(
'User.approved' => 1,
'User.deleted' => 0
),
...
),
'UnapprovedUser' => array(
'className' => 'User',
'foreignKey' => 'group_id',
'conditions' => array(
'User.approved' => 0,
'User.deleted' => 0
),
...
),
'DeletedUser' => array(
'className' => 'User',
'foreignKey' => 'group_id',
'conditions' => array('User.deleted' => 1),
...
),
);
In the above example, a Group model has different kinds of users (approved, unapproved and deleted). Using aliases helps make your code very elegant.
It allows you to do things like $this->Owner->read(null,$userId); You can have an OwnersController and views/owners.
It is ... an alias. In a sense, User is an alias for the db table users.
A better example: I have a CMS where I use the table articles for Article, BlogItem and News. Those three names are aliases for the same table that allow me to set up different models, relationships and behaviour. So I have a BlogItemsController and a NewsController as well as an ArticlesController.

CakePhp: On the fly associations using a re-named Model field?

I'm trying to use on the fly associations to trim down the data I retrieve, but the model I'm using is associated to other models with a re-named field because I have 2 of the same models associated with it.
So, here's the model, say 'test', that has two 'user' fields, both related to the User model.
In the model:
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
),
'User_Watched' => array(
'className' => 'User',
'foreignKey' => 'user_id_watched'
)
);
When I retrieve data related to 'test', I want to only retrieve particular data linked to the 'User' and 'User_Watched' fields without any other nested information.
But when I do:
$this->User->unbindModel(array('hasMany' => array('something1', 'something2')), false);
then something1 and something2 data does not show up for the 'User' field of model 'test', but is still retrieved for the 'User_watched' field.
Can I not retrieve unwanted data for the 'User_watched' field?
Hope this makes sense... :)
KcYxA,
Containable behavior might help a lot in this case, as benjamin mentioned, your "find" queries would look like:
$this->User->find('first', array(
'conditions' => array('User.id' => $id),
'contain' => array('UserWatched')
));
In this case, you won't have to use unbindModel method. In this example, you'll get User and UserWatched data.
If you need only User data from "find", then tell Cake to "$this->User->contain();" so it won't go further then User model.
to use on the fly associations to trim
down the data I retrieve
Good idea.
'foreignKey' => 'user_id_watched'
should possibly be:
'foreignKey' => 'user_watched_id'.
Edit 1: At least this would make sense according to my current understanding. If user_id is a correct foreign key(FK), which cakephp uses to unbind the relations, but user_id_watched isn't, than your described behavior is explained.
Edit 2: The Containable behavior gives you another tool for controlling associated models.
Change $primaryKey in fly, run controller
Sample:
// Models
//....
class PreProductoDescripcion extends AppModel {
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_producto_descripcion';
//....
//....
}
class SenasaPedidosDetalles extends AppModel {
/**
* Display field
*
* #var string
*/
public $displayField = 'cod_tango';
public $belongsTo = array(
'SenasaPedidos' => array(
'className' => 'SenasaPedidos',
'foreignKey' => 'senasa_pedidos_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'PreProductoDescripcion' => array(
'className' => 'PreProductoDescripcion',
'foreignKey' => 'cod_tango',
//'conditions' => array('SenasaPedidosDetalles.cod_tango' => 'PreProductoDescripcion.codigo'),
'fields' => '',
'order' => ''
)
);
//....
#
// Controller Fly
//...
$this->SenasaPedidos->Behaviors->load('Containable');
$this->SenasaPedidos->SenasaPedidosDetalles->PreProductoDescripcion->primaryKey = 'codigo';
$datos = $this->SenasaPedidos->find(
'first', array(
'fields' => array( 'SenasaPedidos.*' ),
'conditions' => array( 'SenasaPedidos.id' => $id ),
'contain' => array(
'Usuarios' => array(
'fields' => array( 'Usuarios.apellido_nombre' )
),
'Clientes' => array(
'fields' => array( 'Clientes.razon_social' )
),
'Provincias' => array(
'fields' => array( 'Provincias.nombre' )
),
'Transportes' => array(
'fields' => array( 'Transportes.razon_social' )
),
'SenasaPedidosDetalles' => array(
'fields' => array( 'SenasaPedidosDetalles.*' ),
'PreProductoDescripcion' => array(
'fields' => array(
'PreProductoDescripcion.id_producto_descripcion',
'PreProductoDescripcion.descripcion'
)
)
),
)
));
//...

Resources