Sorry if the Title misses what i'm aiming at, but had no clue how to name this. Here's my Quest:
I have a single Cart, holding multiple Items (1:n) which are belonging to prices (1:1).
The Relations between Items and Prices are working as well:
class Item extends AppModel {
public $hasOne = 'Price';
}
class Price extends AppModel {
public $belongsTo = array(
'Item' => array(
'className' => 'Item',
'foreignKey' => 'id'
)
);
}
But now i make a select (find) of a cart and want those items included, which is also working as well:
class CartItem extends AppModel {
public $useTable = 'cart_items';
public $belongsTo = array('Item', 'Address');
}
But what i really need is to get those prices of each item, which is not working ($result in afterFind()-Callback in Item-Model does not include the assigned prices & afterFind()-Callback in Price-Model is not called when finding a cart)..
what am i missing here?
/EDIT: Recent Changes:
class AppModel extends Model {
var $actsAs = array('Containable');
}
class CartsController extends AppController {
public function getCart() {
$cart = ClassRegistry::init('CartItem')->find('all', array(
'contain' => array(
'Item' => array(
'Price'
),
'Address'
),
'conditions' => array(
'id_cart' => $cart['Cart']['id']
)
));
}
The above changes cause that i'll get a Price into a found Item but only into the last one thats found.
You haven't shown your actual find(), but I suspect you're not setting an appropriate 'recursive' param or are not using 'contain'.
I prefer using the containable behaviour enabled from AppModel:
var $actsAs = array('Containable');
Then you can do something like:
$cartItem = $this->CartItem->find(
'first',
array(
'contain' => array(
'Item' => array(
'Price'
),
'Address'
),
'conditions' => array('CartItem.id' => 123)
)
);
Related
I'm having some trouble with CakePHP's models.
I have three tables - CC_PLAYLIST, CC_PLAYLISTCONTENTS, and CC_FILES.
I'd like to get a list of all playlists, with all of their contents, and then the relevant file information for each piece of playlist content.
I.E
Playlist 1
-- Playlist Content 1
---- File Info
-- Playlist Content 2
---- File Info
-- Playlist Content 3
---- File Info
Playlist 2
-- Playlist Content 123
---- File Info
In my controller I have:
$playlistcontent = $this->AirtimePlaylist->find('all') however that seems to turn up a flat array with all playlistcontents, all files, and empty playlist arrays.
AirtimeFile.php
class AirtimeFile extends AppModel {
public $useTable = 'cc_files';
public $actsAs = array('Containable');
public $hasMany = array('AirtimeFileAttribute' => array(
'className' => 'AirtimeFileAttribute',
'foreignKey' => 'track_id'
));
}
AirtimePlaylist.php
class AirtimePlaylist extends AppModel {
public $useTable = 'cc_playlist';
public $actsAs = array('Containable');
public $hasMany = array('AirtimePlaylistContent' => array(
'className' => 'AirtimePlaylistContent',
'foreignKey' => 'playlist_id'
));
}
AirtimePlaylistContent.php
class AirtimePlaylistContent extends AppModel {
public $useTable = 'cc_playlistcontents';
public $actsAs = array('Containable');
public $hasOne = array('AirtimePlaylist' => array(
'className' => 'AirtimePlaylist',
'foreignKey' => 'id'
),'AirtimeFile' => array(
'className' => 'AirtimeFile',
'foreignKey' => 'id'
));
}
In your controller you need to use contain
$model = $this->AirtimePlaylist->find('all',
array(
'contain' => array('AirtimePlaylistContent' => array('AirtimeFile') )
)
);
My Category Model:
class Category extends AppModel {
public $displayField = 'name';
// public $actsAs = array('Containable');
public $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post',
'joinTable' => 'categories_postss',
'foreignKey' => 'category_id',
'associationForeignKey' => 'post_id',
'unique' => 'keepExisting'
)
);
}
$params['contain'] = array('Post' => array(
'limit'=> 3));
pr($this->Category->find('first',$params)); exit;
It is fetching all Posts, irrespective of limit.
What I want to do:
I have this page where I ma listing all the categories and latest 5 posts related to it.
I want to limit the associated model to only 5 rows.
Any ideas?
Containable behavior is not in use
The most likely reason for this problem is that the containable behavior is not being used at all.
Compare, for the below code example:
$results = $this->Category->find('first', array(
'contain' => array(
'Post' => array(
'limit' => 3
)
)
));
Without containable behavior, it'll generate the following queries:
SELECT ... FROM `crud`.`categories` AS `Category` WHERE 1 = 1 LIMIT
SELECT ... FROM `crud`.`posts` AS `Post`
JOIN `crud`.`categories_posts` AS `CategoriesPost` ON (...)
With containable behavior, it'll generate the following queries:
SELECT ... FROM `crud`.`categories` AS `Category` WHERE 1 = 1 LIMIT
SELECT ... FROM `crud`.`posts` AS `Post`
JOIN `crud`.`categories_posts` AS `CategoriesPost` ON (...) LIMIT 3
Given this (and the code in the question) check that the AppModel has the containable behavior in $actsAs:
<?php
// app/Model/AppModel.php
class AppModel extends Model {
public $actsAs = array('Containable');
}
Limit always required?
Alternatively, or possibly in addition, you may prefer to put a limit in the association definition - To do so just define the 'limit' key:
class Category extends AppModel {
public $hasAndBelongsToMany = array(
'Post' => array(
'limit' => 100, // default to a high but usable number of results
)
);
}
the hasAndBelongsToMany relationship seems unnecessary to me. I think you only need Category hasMany Post and Post belongsTo Category relationships. Add category_id to the posts table. Make both models actAs containable.
Post Model
class Post extends AppModel {
public $actsAs = array('Containable');
var $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id'
),
// ... more relationships
);
Category Model
class Category extends AppModel {
public $actsAs = array('Containable');
var $hasMany = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'category_id'
),
// ... more relationships
);
Categories Controller
class CategoriesController extends AppController {
public $paginate = array(
'Category' => array(
'contain' => array(
'Post' => array(
'limit' => 3
), // end Post
) // end Category contain
) // end Category pagination
); // end pagination
public function index() {
// for paginated results
$this->set('categories', $this->paginate());
// for find results
$this->Category->contain(array(
'Post' => array(
'limit' => 3
)
));
$this->set('categories', $this->Category->find('all'));
}
I have 3 models
Categories:
class Category extends AppModel {
public $belongsTo = array(
'Parent' => array(
'className' => 'Category',
'foreignKey' => 'parent_id'
),
);
public $hasMany = array(
'Children' => array(
'className' => 'Category',
'foreignKey' => 'parent_id'
),
'UserCategoryMeta'
);
}
Users:
class User extends AppModel {
public $hasMany = array(
'UserCategoryMeta' => array(
'className' => 'UserCategoryMeta',
'foreignKey' => 'user_id',
),
);
}
UserCategoryMeta:
class UserCategoryMeta extends AppModel
{
public $belongsTo = array(
'User', 'Category'
);
}
What I need to do is have each user be able to choose many categories and for each of those associations I need the user to set search terms which is just 1 field in the DB.
So the UserCategoryMeta table looks like this:
id | user_id | category_id | search_terms
I've found a way which might work but it seems very hacky.
In the usercontroller I have:
$Categories = $this->User->Category->find('list');
Then in the add view I have the checkboxes:
echo $this->Form->input('Category.Category',array(
'type' => 'select',
'multiple' =>'checkbox',
'options' => $Categories,
));
Then the only way I could get each of those checkboxes to have a search terms input next to them is to do this in the add view:
foreach ($Categories as $key => $category){
echo '<input type="text" id="Category'.$key.'SearchTerms" name="data[Category][search_terms]['.$key.']"><br/>';
}
This produces what I want but obviously since I'm just creating random inputs when the form get's submitted it get's black holed. I have managed to get passed this but I know I'm doing it the wrong way and hopefully someone can help me do it the right way.
Also then once I have this data array in the controller im not sure how to save it to the database correctly.
Thanks in advance for any help!
Just do what you're doing, but in the repeat, use $this->Form->input instead of just manually writing the HTML.
I'm using an existing database (I can't change it and its table names are not like cake conventions want it), and I'd like to do some left joins but can't do it properly :/
I've already defined my tables, giving them primary keys and the relations in the models.
Here is my problem :
Table Wysipage can have 0 to n wysipage_content, and 0 to n wysipage_menu.
an element from wysipage_content corresponds to 1 and only 1 Wysipage.
an element from wysipage_menu corresponds to 0 or 1 Wysipage.
I'd like to make a request who would give me a list of all the elements from Wysipages, with their eventuals contents and menus, all that in a single table, and by only one request.
Here are my tables definitions (I'm avoiding you the entire schema, just be aware there is a wp_id and a wp_name column) :
class Wysipage extends AppModel {
var $actsAs = array('Containable');
public $useTable = 'wysipage';
public $primaryKey = 'wp_id';
public $displayField = 'wp_name';
var $hasMany = array(
'un' => array(
'Wysipage_contenu' => array(
'className' => 'Wysipage_contenu',
'foreignKey' => 'wpc_wp_id',
)),
'deux' => array(
'Wysipage_menu' => array(
'className' => 'Wysipage_menu',
'foreignKey' => 'wpm_wp_id',
))
);
class Wysipage_contenu extends AppModel {
var $actsAs = array('Containable');
public $useTable = 'wysipage_contenu';
public $primaryKey = 'wpc_id';
public $displayField = 'wpc_h1';
public $belongsTo = array(
'Wysipage' => array(
'className' => 'Wysipage',
'foreignKey' => 'wp_id'
)
);
class Wysipage_menu extends AppModel {
var $actsAs = array('Containable');
public $useTable = 'wysipage_menu';
public $primaryKey = 'wm_id';
public $displayField = 'wm_lien';
public $belongsTo = array(
'Wysipage' => array(
'className' => 'Wysipage',
'foreignKey' => 'wm_wp_id'
)
);
And here is my code to try request (but failed) :
$this->loadModel('Wysipage_contenu');
$this->loadModel('Wysipage_menu');
$this->Wysipage->contain();
$mes_wysipages = $this->Wysipage->find('all', array('joins' => array(
array(
'table' => 'wysipage_contenu',
'alias' => 'wpc',
'type' => 'LEFT',
'conditions'=> array('wpc.wpc_wp_id = Wysipage.wp_id')
),
array(
'table' => 'wysipage_menu',
'alias' => 'wpm',
'type' => 'LEFT',
'conditions'=> array('wpm.wm_wp_id = Wysipage.wp_id')
)
)));
$this->set('wysipages', $mes_wysipages);
$this->render();
What have I done wrong? Is the problem in my model declarations? Or do I use a wrong request type? :(
The request I'd like to make is simply :
SELECT wp_id, wp_name, wpc_id, wpc_name
FROM wysipage
LEFT JOIN wysipage_contenu ON wysipage.wp_id = wysipage_contenu.wpc_wp_id
Just this :(
I'm not even sure I want a LEFT join or a RIGHT join, but anyway the problem remains the same, this code gives me bad answers with multiple occurrences of the same lines :/
Thanks :/
PS : Sorry for my bad English, it's not my native language.
You can not join content and menu tables on wp_id in same query because they both have many-to-one relationship to wp_id, and there by for each row from content there are all rows from menu with same wp_id in result of such join. You need to do 2 queries: one for content and one for menu. Or if list columns you needed identical for both tables you can union both results from this two queries.
I want to achieve a structure where e.g. an Organisation has many Departments, and where Departments has many Persons.
I've set up my model structure like this:
Organisations
<?php
class Organisation extends AppModel {
public $hasMany = array(
'Department' => array(
'className' => 'Department',
'foreignKey' => 'organisations_id'
)
);
}
Departments
<?php
class Department extends AppModel {
public $hasMany = array(
'Person' => array(
'className' => 'Person',
'foreignKey' => 'departments_id'
)
);
}
Persons
<?php
class Person extends AppModel {
}
Then I have a controller like this:
<?php
class OrganisationsController extends AppController {
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session');
public function index() {
$this->set('organisations', $this->Organisation->find('all'));
}
}
When I print out $organisations I get an array like this:
Array
(
[0] => Array
(
[Organisation] => Array
(
[id] => 1
[created] => 2013-01-03 16:02:47
)
[Department] => Array
(
[0] => Array
(
[id] => 1
[created] => 2013-01-03 16:02:47
[organisations_id] => 1
)
)
)
)
I'm new to both PHP and CakePHP, but wouldn't you expect the Person array to be included in the Organisation array? And if not, is there another way to achieve a structure like the one described above (Organisation->Department->Person)?
Any hints on how to go about this is highly appreciated! :)
You are probably looking for recursive
Or you could make use of the containable behaviour
But please have a look at the result. When using recursive you can get a lot of data you don't want! So please be careful and select the fields you actually need!
Recursive
You would get something like:
<?php
class OrganisationsController extends AppController {
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session');
public function index() {
$this->Organisation->recursive = 2; # or -1, 0, 1, 2, 3
$this->set('organisations', $this->Organisation->find('all'));
}
}
You could also declare this in the find itself like so:
$this->set('organisations', $this->Organisation->find('all' array(
'recursive' => 2 # or -1, 0, 1, 2, 3
)
));
Containable
class Organisation extends AppModel {
public $hasMany = array(
'Department' => array(
'className' => 'Department',
'foreignKey' => 'organisations_id'
)
);
$actsAs = array('Containable');
}
Now in your controller you can do something like:
$this->set('organisations', $this->Organisation->find('all', array(
'contain' => array('User')
)
));
But as always, there are many roads leading to Rome. So please read the books very carefully!
http://book.cakephp.org/2.0/en/index.html
http://book.cakephp.org/2.0/en/models/model-attributes.htm
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
Recursive function will do it for you.
Just in OrganisationsController index function , try doing like below.
$this->Organisation->recursive = 2;
$this->set('organisations', $this->Organisation->find('all'));
Note: Recursive may affect your performance , you can use unbind method to get rid of it by just fetching the data you want.