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
Related
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.
Hi, I am new to cakephp and doing a project on cakephp 2.3.4.
I have to associate product metal class through has many through association . But it doesn't seem to be working.
Model code
class Metal extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
class Product extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
App::uses('AppModel', 'Model');
class MetalProduct extends AppModel {
public $belongsTo = array(
'Metal' => array(
'className' => 'Metal',
'foreignKey' => 'metal_id'
),
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id'
)
);}
My database table names are metal, products and metal_products
I have multiple select option for selecting more than one metal type.
This is how I get the the list of metals
$metals=$this->Metal->find('list');
$this->set(compact('metals'));
FormHelper code for listbox is
<?php echo $this->Form->input('Metal',array('type' => 'select',
'multiple' => true)); ?>
The product is getting saved successfully but the associations are not.
The debug array give me this
$message = array(
'Product' => array(
'category_id' => '517a514b-0eb0-4ec9-b018-0b948620d4f0',
'name' => 'mangalsutra Diamond',
'slug' => 'mangalsutra_diamond',
'description' => '1212',
'Metal' => array(
(int) 0 => '5183cb65-bf90-459c-b22e-0b748620d4f0',
(int) 1 => '5183ce25-c744-433e-b035-0b748620d4f0'
),
'image' => '121212',
'price' => '12121',
'weight' => '12',
'active' => '1',
'category' => 'Mangalsutra'
)
)
I had put my head through walls but no clue why the associations are not getting saved.
The way they say in tutorials it seems easy, but why its not working?
I have doubts that its not saving because the metal array is passed like this
'Metal' => array(
(int) 0 => '5183cb65-bf90-459c-b22e-0b748620d4f0',
(int) 1 => '5183ce25-c744-433e-b035-0b748620d4f0'
),
It should mention 'id''rather than (int) 0 or something.
Also, my database table for metal_products which I have created manually has
id(primary key)
metal_id(foreign key to Metal.id)
product_id(foreign key to Product.id)
Am I doing something wrong with naming conventions or the way database is created?
Please give me correct ans cause anything I tried from others answer is not working
I am saving it via
$this->Product->saveAll($this->request->data, array('deep' => true))
In your model, is all of that in your MetalProduct Model? If so you need to move
class Metal extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
to the Metal model and
class Product extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
to the Product Model
Also add your belongsTo to each Model
I see you have a join table. Join tables are usually for HABTM associations. This is a HasMany. You need to use the other options available to define the foreign key relationships in each model as outlined above.
Please see the examples on the Cake Documentation.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
Also, if you are unsure of how to set up the models correctly, you can always use the Bake feature of Cakephp to generate the models and code for you in that regard.
book.cakephp.org/2.0/en/console-and-shells/code-generation-with-bake.html
If you are more of a visual learner this video tut should help you through the basics
http://www.youtube.com/watch?v=kJAMifqF5s8
The Relation i generated using Bake looks something like this
class Product extends AppModel {
/**
* hasAndBelongsToMany associations
*/
public $hasAndBelongsToMany = array(
'Metal' => array(
'className' => 'Metal',
'joinTable' => 'metals_products',
'foreignKey' => 'product_id',
'associationForeignKey' => 'metal_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
));
}
class Metal extends AppModel {
/** hasAndBelongsToMany associations */
public $hasAndBelongsToMany = array(
'Product' => array(
'className' => 'Product',
'joinTable' => 'metals_products',
'foreignKey' => 'metal_id',
'associationForeignKey' => 'product_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
));
}
/**
* MetalsProduct Model
* #property Product $Product
* #property Metal $Metal
*/
class MetalsProduct extends AppModel {
/* belongsTo associations */
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Metal' => array(
'className' => 'Metal',
'foreignKey' => 'metal_id',
'conditions' => '',
'fields' => '',
'order' => ''
));
}
This worked for me flawlessly.
Hope it works for all.
I have a site that listings mp3 files in various categories. The site is in CakePHP and I am using the CakePHP star rating plugin for the rating functionality. The songs are categorized based on Albums, Singer and Music director so I need to implement rating for each of these. The rating plugin now I am using has an option for specify the model. It's model class is something like this :
class Rating extends Model {
var $name = 'Rating';
var $validate = array(
'user_id' => array(
'rule' => array('maxLength', 36),
'required' => true
),
'model_id' => array(
'rule' => array('maxLength', 36),
'required' => true
),
'model' => array(
'rule' => 'alphaNumeric',
'required' => true
)
);
var $hasMany = array(
'Rating' => array(
'className' => 'Rating',
'foreignKey' => 'model_id',
'conditions' => array('Rating.model' => 'rating'),
'dependent' => true,
'exclusive' => true
)
);
}
It has option to specify the model name and model id so I think my issue will be fixed if I will be able to specify the model name or any other parameter along with this Array. As I am newbie in CakePHP, someone please help me to find a solution.
Please check the rating plugin I am talking about here:
http://bakery.cakephp.org/articles/schneimi/2010/08/19/ajax-star-rating-plugin-1
The $hasMany attribute definition needs to go in the model you would like to rate. So for Album, it would look something like:
/**
* Album Model
*/
class Album extends Model
{
var $name = 'Album';
var $hasMany = array(
'Rating' => array(
'className' => 'Rating',
'foreignKey' => 'model_id',
'conditions' => array(
'Rating.model' => 'Album'
),
'dependent' => true,
'exclusive' => true
)
);
}
I'm trying to make it so when I do a $this->Topic->find('all'), it will pull all the associated User data, and all the associated Post data. The problem is that I can't manage to do that right now.
Right now I do $this->User->find('all'), which returns the Topic data, and all Posts inside that Topic. I guess this is what my code tells it to do, but it's not what I want. Please forgive my explanation.. I really suck at CakePHP associations.
Here are my models for your reference:
Topic.php
class Topic extends AppModel {
var $name = 'Topic';
var $actsAs = array('Containable');
var $hasMany = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'topicid'
)
);
var $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'id'
)
);
var $belongsTo = array(
'Forum' => array(
'className' => 'Forum',
'foreignKey' => 'id'
)
);
}
Post.php
class Post extends AppModel {
var $name = 'Post';
var $actsAs = array('Containable');
var $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'id'
)
);
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'userid'
),
'Topic' => array(
'className' => 'Topic',
'foreignKey' => 'topicid'
)
);
}
Forum.php
class Forum extends AppModel {
var $name = 'Forum';
var $hasMany = array(
'Topic' => array(
'className' => 'Topic',
'foreignKey' => 'forumid'
)
);
}
User.php
class User extends AppModel {
var $name = 'User';
var $actsAs = array('Containable');
var $hasMany = array(
'ForumPost' => array(
'className' => 'ForumPost',
'foreignKey' => 'userid'
),
'ForumTopic' => array(
'className' => 'ForumTopic',
'foreignKey' => 'userid'
)
);
}
I want it so that I can do $this->Topic->find('all'), and it gives me the Topic data, then the Post data for each Post in the Topic, and the User data for each Post in the Topic (respectively)..
Thanks for any help.
I found out my problem, thanks to savant in the #cakephp IRC channel. My models were named wrong.. :)
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.