Models aren't joining - php

I have 4 tables:
wizards (id, name)
pages (id, page_number, wizard_id)
modules (id, name)
modules_pages(id, page_id, module_id, wizard_id)
The models are baked defaults.
A Wizard has a Page, a Page has a Module. modules_pages is a junction table. I cannot get a list of modules when I display an array from wizard or pages that is filtered by the current wizard_id of the page, or one that I pass. It just displays the modules multiple times.
Page Model
public $belongsTo = array(
'Wizard' => array(
'className' => 'Wizard',
'foreignKey' => 'wizard_id',
),
);
public $hasAndBelongsToMany = array(
'Module' => array(
'className' => 'Module',
'joinTable' => 'modules_pages',
'foreignKey' => 'page_id',
'associationForeignKey' => 'module_id',
'unique' => 'keepExisting',
)
);
Module Model
public $hasAndBelongsToMany = array(
'Page' => array(
'className' => 'Page',
'joinTable' => 'modules_pages',
'foreignKey' => 'module_id',
'associationForeignKey' => 'page_id',
'unique' => 'keepExisting',
),
);
public $hasOne = array(
'CodeReference' => array(
'className' => 'CodeReference',
'foreignKey' => 'code_reference_id',
),
);
Wizard Model
public $hasMany = array(
'Page' => array(
'className' => 'Page',
'foreignKey' => 'wizard_id',
'dependent' => false,
'conditions' => 'ModulesPage.wizard_id = Wizard.id',
)
);
The Controller
$this->loadModel('Page');
$this->Page->recursive = 1;
$options = array('Page.wizard_id' => $wizard_id);
$page = $this->Page->find('first', $options);
$this->set('page');
$this->loadModel('ModulesPage');
$this->ModulesPage->recursive = 2;
$options = array('ModulesPage.wizard_id ' => $wizard_id,
'ModulesPage.page_number' => $page_number,
'ModulesPage.enabled' => 1);
$modules = $this->ModulesPage->find('all', $options);

Start by setting the following in your AppModel:
public $recursive = -1;
What Controller are you putting that code in? You shouldn't need to use loadModel - because you have joined the models together in the model files, you can access like this.
// access Module model from within PagesController
$this->Page->Module->find('all', $options);
There is also an error in your $options set-up. Conditions are provided in an array but they need to be inside another array called 'conditions', e.g.
$options = array(
'conditions' => array(
//conditions go here
)
);
Finally, after turning off recursive you need to enable containable behaviour. Change the $options array I've just given above as follows (this example would get all Pages along with their Wizards and Modules)
$options = array(
'conditions' => array(
//conditions go here
),
'contain' => array(
'Wizard',
'Module'
)
);
$data = $this->Page->find('all', $options);
You can further define additional options for the query with other array keys, e.g. 'order', 'group', 'limit', etc.

Related

hasAndBelongsToMany in CakePHP 2.x

I need help for the simple relationchip in cakephp 2.x, I believe this right however not working:
Customer.php (Model)
public $hasAndBelongsToMany = array(
'Note' => array(
'className' => 'Note',
'joinTable' => 'customers_notes',
'foreignKey' => 'customer_id',
'associationForeignKey' => 'note_id',
'unique' =>false,
'dependent'=>true,
)
);
Note.php (Model)
public $hasAndBelongsToMany = array(
'Customer' => array(
'className' => 'Customer',
'joinTable' => 'customers_notes',
'foreignKey' => 'note_id',
'associationForeignKey' => 'customer_id',
'unique' =>false,
'dependent'=>true
)
);
CustumersController.php (Controller I make the find)
public function admin_notes($id=null){
$this->layout="Adm.admin";
$all = $this->Customer->Note->find('all', array(
'limit' => 10,
'conditions' => array(
"CustomersNote.customer_id" => $id
),
'order'=>'CustomersNote.id DESC'
));
$this->set('notes', $all);
}
The error
SQL Query: SELECT `Note`.`id`, `Note`.`titulo`, `Note`.`conteudo`, `Note`.`created`, `Note`.`modified`, `Note`.`user_id` FROM `crm_prado`.`notes` AS `Note` WHERE `CustomersNote`.`customer_id` = '1' LIMIT 10
Thanks!
The soluction is really simple.
In find uses
$all = $this->Customer->find('all', array(
'recursive'=>2,
'conditions'=>array(
'Customer.id'=>$id
)
));
Works!, but now the recursive mode don't work...

Join single item from related table based on min of field

I have an Item model which has the following associations:
public $hasOne = array(
'Project' => array(
'className' => 'Project',
'foreignKey' => 'item_id'
)
);
public $hasMany = array(
'ItemPic' => array(
'className' => 'ItemPic',
'foreignKey' => 'item_id',
'dependent' => false
)
);
I am wanting custom data for different views of Item. It seems like CakePHP automatically includes Project data (maybe because it is hasOne?) and does not include the ItemPic data. In the index I really don't even want the Project data... however, I do want the ItemPic data. For each Item record pulled, I want a single ItemPic record joined to it. This ItemPic should be basically ItemPic.item_id = Item.id and ORDER BY ItemPic.rank LIMIT 1.
The purpose of this is basically so that in the index I can show a list of Items and a picture associated with each item. I would like all of the images along with the Project data in the view for a single Item, but not in the list/index.
I was told I could use containable like this:
// In the model
public $actsAs = array('Containable');
// In the controller
$this->paginate = array(
'conditions' => $conditions,
'contain' => array(
'ItemPic' => array(
'fields' => array('file_name'),
'order' => 'rank',
'limit' => 1
)
)
);
The above actually works how I want... however, I was also told that doing this would cause an extra query to be ran for every single Item... which I feel I should avoid.
I tried doing this, but I get duplicate data and it doesn't attach any ItemPic data:
$this->paginate = array(
'conditions' => $conditions,
'joins' => array(
array(
'table' => 'item_pics',
'alias' => 'ItemPic',
'type' => 'LEFT',
'conditions' => array(
'ItemPic.item_id = Item.id'
),
'order' => 'rank ASC',
'limit' => 1
)
)
);
$paginated = $this->Paginator->paginate();
Can you please try this:
$this->paginate = array(
'conditions' => $conditions,
'joins' => array(
array(
'table' => 'item_pics',
'alias' => 'ItemPic',
'type' => 'LEFT',
'conditions' => array(
'ItemPic.item_id = Item.id'
),
'order' => 'rank ASC',
'limit' => 1
)
),
'fields' => array('Item.*','Project.*','ItemPic.*')
);
In the fileds section you may or may not assign "Item" , "Project" according to your requirment.
Thanks

CakePHP - should I use HABTM, and if not, how to setup a join table for multiply self-referencing model?

So I am setting up a course system, and courses have pre-reqs. One course may be a pre-req for many others and also may itself have many pre-reqs. I have a Course model, and I'm currently using a HABTM join table, because I also need to track the type of pre-req (regular, co-req, or what we call "pre-req with concurrency": you can take the pre-req at the same time). Here's the Course model:
class Course extends AppModel {
public $name = 'Course';
public $belongsTo = 'Department';
public $hasMany = array(
'Instance' => array('className' => 'Instance'),
);
public $hasAndBelongsToMany = array(
'Prereq' => array(
'className' => 'Course',
'joinTable' => 'prereq_successor',
'foreignKey' => 'successor_id',
'associationForeignKey' => 'prereq_id',
'unique' => 'keepExisting'
)
);
}
The problem is that I need to construct some data for saveAll (from data uploaded via a file, not a form) and can't figure out how to do it from the docs.
I've tried this (controller code), based on the cakePHP 2.0 book, but it fails silently:
//test based on cakePHP 2.0 book fails:
$data = array(
'Course' => array('id' => 1),
'Prereq' => array(
'successor_id' => 1,
'prereq_id' => 3,
'type' => 'prereq'
)
);
$result = $this->Course->saveAssociated($data);
And I've tried this (controller code) without success:
//test based on cakePHP 2.0 book fails:
$data = array(
array(
'Course' => array('id' => 1),
'Prereq' => array(
'successor_id' => 1,
'prereq_id' => 3,
'type' => 'prereq'
)
)
);
$result = $this->Course->saveAll($data);
Thanks in advance,
Dave
I think the problem is with your data.
Try this:
$data = array(
array(
'Course' => array('id' => 1),
'Prereq' => array(
array(
'successor_id' => 1,
'prereq_id' => 3,
'type' => 'prereq'
)
)
)
);
$result = $this->Course->saveAll($data);

CakePHP find method with JOIN

Hi,
I need to do the following query using the CakePHP find method:
SELECT *
FROM `messages`
INNER JOIN users ON messages.from = users.id
WHERE messages.to = 4
ORDER BY messages.datetime DESC
Basically I have:
messages table with a Message model
users table with User model
and want to retrieve information from both tables in one query. The users.id field is the same as the messages.from field, so that's what the join is on.
I am doing it in my MessagesController so it would need to be something like:
$this->Message->find();
Thanks
There are two main ways that you can do this. One of them is the standard CakePHP way, and the other is using a custom join.
It's worth pointing out that this advice is for CakePHP 2.x, not 3.x.
The CakePHP Way
You would create a relationship with your User model and Messages Model, and use the containable behavior:
class User extends AppModel {
public $actsAs = array('Containable');
public $hasMany = array('Message');
}
class Message extends AppModel {
public $actsAs = array('Containable');
public $belongsTo = array('User');
}
You need to change the messages.from column to be messages.user_id so that cake can automagically associate the records for you.
Then you can do this from the messages controller:
$this->Message->find('all', array(
'contain' => array('User')
'conditions' => array(
'Message.to' => 4
),
'order' => 'Message.datetime DESC'
));
The (other) CakePHP way
I recommend using the first method, because it will save you a lot of time and work. The first method also does the groundwork of setting up a relationship which can be used for any number of other find calls and conditions besides the one you need now. However, cakePHP does support a syntax for defining your own joins. It would be done like this, from the MessagesController:
$this->Message->find('all', array(
'joins' => array(
array(
'table' => 'users',
'alias' => 'UserJoin',
'type' => 'INNER',
'conditions' => array(
'UserJoin.id = Message.from'
)
)
),
'conditions' => array(
'Message.to' => 4
),
'fields' => array('UserJoin.*', 'Message.*'),
'order' => 'Message.datetime DESC'
));
Note, I've left the field name messages.from the same as your current table in this example.
Using two relationships to the same model
Here is how you can do the first example using two relationships to the same model:
class User extends AppModel {
public $actsAs = array('Containable');
public $hasMany = array(
'MessagesSent' => array(
'className' => 'Message',
'foreignKey' => 'from'
)
);
public $belongsTo = array(
'MessagesReceived' => array(
'className' => 'Message',
'foreignKey' => 'to'
)
);
}
class Message extends AppModel {
public $actsAs = array('Containable');
public $belongsTo = array(
'UserFrom' => array(
'className' => 'User',
'foreignKey' => 'from'
)
);
public $hasMany = array(
'UserTo' => array(
'className' => 'User',
'foreignKey' => 'to'
)
);
}
Now you can do your find call like this:
$this->Message->find('all', array(
'contain' => array('UserFrom')
'conditions' => array(
'Message.to' => 4
),
'order' => 'Message.datetime DESC'
));
Otro example, custom Data Pagination for JOIN
CODE in Controller CakePHP 2.6 is OK:
$this->SenasaPedidosFacturadosSds->recursive = -1;
// Filtro
$where = array(
'joins' => array(
array(
'table' => 'usuarios',
'alias' => 'Usuarios',
'type' => 'INNER',
'conditions' => array(
'Usuarios.usuario_id = SenasaPedidosFacturadosSds.usuarios_id'
)
),
array(
'table' => 'senasa_pedidos',
'alias' => 'SenasaPedidos',
'type' => 'INNER',
'conditions' => array(
'SenasaPedidos.id = SenasaPedidosFacturadosSds.senasa_pedidos_id'
)
),
array(
'table' => 'clientes',
'alias' => 'Clientes',
'type' => 'INNER',
'conditions' => array(
'Clientes.id_cliente = SenasaPedidos.clientes_id'
)
),
),
'fields'=>array(
'SenasaPedidosFacturadosSds.*',
'Usuarios.usuario_id',
'Usuarios.apellido_nombre',
'Usuarios.senasa_establecimientos_id',
'Clientes.id_cliente',
'Clientes.consolida_doc_sanitaria',
'Clientes.requiere_senasa',
'Clientes.razon_social',
'SenasaPedidos.id',
'SenasaPedidos.domicilio_entrega',
'SenasaPedidos.sds',
'SenasaPedidos.pt_ptr'
),
'conditions'=>array(
'Clientes.requiere_senasa'=>1
),
'order' => 'SenasaPedidosFacturadosSds.created DESC',
'limit'=>100
);
$this->paginate = $where;
// Get datos
$data = $this->Paginator->paginate();
exit(debug($data));
OR Example 2, NOT active conditions:
$this->SenasaPedidosFacturadosSds->recursive = -1;
// Filtro
$where = array(
'joins' => array(
array(
'table' => 'usuarios',
'alias' => 'Usuarios',
'type' => 'INNER',
'conditions' => array(
'Usuarios.usuario_id = SenasaPedidosFacturadosSds.usuarios_id'
)
),
array(
'table' => 'senasa_pedidos',
'alias' => 'SenasaPedidos',
'type' => 'INNER',
'conditions' => array(
'SenasaPedidos.id = SenasaPedidosFacturadosSds.senasa_pedidos_id'
)
),
array(
'table' => 'clientes',
'alias' => 'Clientes',
'type' => 'INNER',
'conditions' => array(
'Clientes.id_cliente = SenasaPedidos.clientes_id',
'Clientes.requiere_senasa = 1'
)
),
),
'fields'=>array(
'SenasaPedidosFacturadosSds.*',
'Usuarios.usuario_id',
'Usuarios.apellido_nombre',
'Usuarios.senasa_establecimientos_id',
'Clientes.id_cliente',
'Clientes.consolida_doc_sanitaria',
'Clientes.requiere_senasa',
'Clientes.razon_social',
'SenasaPedidos.id',
'SenasaPedidos.domicilio_entrega',
'SenasaPedidos.sds',
'SenasaPedidos.pt_ptr'
),
//'conditions'=>array(
// 'Clientes.requiere_senasa'=>1
//),
'order' => 'SenasaPedidosFacturadosSds.created DESC',
'limit'=>100
);
$this->paginate = $where;
// Get datos
$data = $this->Paginator->paginate();
exit(debug($data));
$services = $this->Service->find('all', array(
'limit' =>4,
'fields' => array('Service.*','ServiceImage.*'),
'joins' => array(
array(
'table' => 'services_images',
'alias' => 'ServiceImage',
'type' => 'INNER',
'conditions' => array(
'ServiceImage.service_id' =>'Service.id'
)
),
),
)
);
It goges to array is null.

problem with counterCache of cakePHP

I have in a video sharing website project these models :
class Video extends AppModel {
var $name = 'Video';
var $hasAndBelongsToMany = array(
'Tag' => array(
'className' => 'Tag',
'joinTable' => 'videos_tags',
'foreignKey' => 'video_id',
'associationForeignKey' => 'tag_id',
'unique' => true,
)
);
}
class Tag extends AppModel {
var $name = 'Tag';
var $hasAndBelongsToMany = array(
'Video' => array(
'className' => 'Video',
'joinTable' => 'videos_tags',
'foreignKey' => 'tag_id',
'associationForeignKey' => 'video_id',
'unique' => true,
)
);
}
class VideosTag extends AppModel {
var $name = 'VideosTag';
var $belongsTo = array(
'Video' => array(
'className' => 'Video',
'foreignKey' => 'video_id',
),
'Tag' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id',
'conditions' => '',
'counterCache' => 'videos_tag_counter'
)
);
}
The counterCache for tags is not working. I don't know why and when I tried to add a beforeSave() callback to videosTag model I found that it doesn't execute when a video get saved (and this video has tags and i find them in the database so how the relation videosTags is saved ? ) !!! can any body explain why this is happening.
Saving a Video with data like this:
array(
'Video' => array(
...
),
'Tag' => array(
'Tag' => array(
...
),
),
);
on the Video model will not trigger a beforeSave callback on the VideosTag model because Cake handles HABTM data without requiring (or even using) the join/with/through model.
There is no built in functionality for what you are trying to achieve, as far as I am aware.
Check out Counter Cache behavior for HABTM relations, might do what you need

Categories