CakePHP: Limit associations in the controller - cakephp

As an example lets imagine we have a simple tv show database. Show and Episode as Model.
An Episode belongsTo one Show and one Show hasMany Episodes.
In the episodes/index view we are just echoing all episodes, the same goes for the shows/index view. But I also want to echo lets say the first 5 episodes of each show (just the title). I could simply limit the episodes by setting the limit attribute for the hasMany association.
In shows/episode/x(id) view I want to echo all episodes. And therefore I can't simply use the limit attribute for the hasMany association since it is view dependent.
What solution should I choose to implement that? I could only archive that by using some "dirty workarounds/hacks" but I feel like this is an usual problem and there might be some actual solution.

I believe what you are looking for is the containable behaviour.
Read the doc:
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
Then remove any limit to your associations. Below there is a way of how you can use containable behavior in your example.
class Shows extends AppModel {
public $actsAs = array('Containable');
}
class ShowsController extends AppController {
//Bring all the shows and 5 episodes
public function index(){
$this->Show->find('all', array('contain' => array(
'Episode' => array('limit' => 5)
)));
}
public function view($id){
//Bring the show
$this->Show->findById($id);
//Then bring the episodes of the show
$this->Show->Episode->findByShowId($id);
//Or you can use
$this->Show->find('all', array(
'contain' => array('Episode')),
'conditions' => array('id' => $id)
);
}
}

Related

How to find a model in cakePHP (I can't find)?

I have taken over a project for cakePHP, I am new and I find it not easy, Magento should be difficult but I find cakePHP more difficult, but maybe I have not reach the moment I know it ...
I have the next model (for table postcodes):
public $belongsTo = array(
'Postcode' => array(
'className' => 'Postcode',
'foreignKey' => 'postcode_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
Everything works, but with the tool AgentRansack I can't find the model Postcode. And besides the name of the table is postcodes, I can't even find a relation for Postcode and postcodes.
How can such a model setup in a different way than with a class Postcode.php ?
When requesting models for which no concrete model class exists (or cannot be found for whatever reason), dynamic model objects will be generated from the AppModel class.
From the CakePHP Cookbook:
CakePHP will dynamically create a model object for you if it cannot find a corresponding file in /app/Model. This also means that if your model file isn’t named correctly (for instance, if it is named ingredient.php or Ingredients.php rather than Ingredient.php), CakePHP will use an instance of AppModel rather than your model file (which CakePHP assumes is missing). If you’re trying to use a method you’ve defined in your model, or a behavior attached to your model, and you’re getting SQL errors that are the name of the method you’re calling, it’s a sure sign that CakePHP can’t find your model and you need to check the file names, your application cache, or both.
http://book.cakephp.org/2.0/en/models.html#understanding-models
By the sounds of it you need to create a class for your 'postcodes',
Read the following: http://book.cakephp.org/2.0/en/models.html
But it should look something like this:
// app/Model/Postcode.php
App::uses('AppModel', 'Model');
class PostCode extends AppModel {
public $validate = array(
// Validation
);
// Replace 'ModelName' with the name of the class where the code sites you mentioned above
public $hasMany = array(
'ModelName' => array(
'className' => 'ModelName',
'foreignKey' => 'postcode_id'
)
);
}

How to fetch rows from hasMany relationship in CakePHP

I have the following Models:
class AppModel extends Model {
public $actsAs = array('Containable');
public $recursive = -1;
}
class City extends AppModel { // for "cities" table
public $belongsTo = 'Country';
}
class Country extends AppModel { // for "countries" table
public $hasMany = 'City';
}
..how do I fetch cities for a country. Something like this I'm trying to find out:
$countries = $this->Country->first(); // fetch a country
$cities = $country->city->find('all'); // get the cities for that country
I've set AppModel in this manner to avoid fetching cities every time I call for a country. Sometimes I don't need all the cities to be retrieved so don't want the default join. But, there are times I do want to fetch cities for a given country. The following is the only way I know how:
$cities = City->find('all', array(
'conditions' => array(
'City.country_id' => $country['Country']['id']
)
))
Is this the most convenient way to access cities once relationships have been established in the model? If so, I don't really understand why bother with $belongsTo and/or $hasMany. Thanks
Associated models first will find the main model, and then will find associated models based on the first query. So it's impossible to limit them based on associated model conditions. So, if you want to limit the main model based on the associated model, you have two options:
Do a Join find
Do a reverse find. It means that you can find City based on conditions, and contain the Country associated to it. For example (assuming that you're on CountriesController):
$this->Country->City->find('all', array(
'conditions' => array(
//your conditions
),
'contain' => array('Country')
));
As you are using CakePHP Model relationship, then it's not require to write Join query.
First thing add the following in Model of Country
class Country extends AppModel { // for "countries" table
public $recursive = 1;
public $hasMany = 'City';
}
Just write the following code,
$countries = $this->Country->find('all');
You can get the cities from $countries in an array format.

Table not found, same using useTable

I have a model, called "Cliente" and this model have a association with another table called ClienteRelFot. I declared that ClienteRelFot has a useTable = 'rel_fot_ec', but the cake are looking for "rel_fots".
The rel_fot_ec table exists on my database because I use to find another data.
Someone have a idea to solve this problem?
I tried clear cache and delete all files from tmp folders.
Below, we have the error:
Error: Table rel_fots for model RelFot was not found in datasource default.
Your associations are trying to pull data from the model 'RelFot' (per the error), not 'ClientRelFot', so declaring that 'ClienteRelFot' uses the table 'rel_fot_ec' will have no effect.
Try adding:
public $useTable = 'rel_fots';
in your 'RelFot' model.
I had this problem too, even though using public $useTable = ...
My data model: Event hasMany > Submissions hasMany > Authors
Cake was telling me [MissingTableException] Table authors for model Author ..., the problem was not in the Author model, but in the Submission model:
class Submissions extends AppModel {
public $hasMany = array(
'Author' => array(
'className' => 'authors', // author should be singular
'foreignKey' => 'submission_id'
)
);

Cakephp Model Association Not kicking in

//index.ctp, this forms points to action updateData in profilesController
$this->Form->input('User.lastname');
$this->Form->input('Profile.age');
$this->Form->input('Profile.height');
$this->Form->input('Associate.city');
$this->Form->end('Submit');
//user.php
Class User extends AppModel {
var $hasOne = array('Profile', 'Associate'};
var $primaryKey = 'user_id';
}
//profile.php
Class Profile extends AppModel {
var $belongsTo = array('User');
var $hasOne = 'Associate';
var $primaryKey = 'user_id';
}
//associate.php
Class Associate extends AppModel {
var $belongsTo = array('User');
var $primaryKey = 'user_id';
}
//profiles_controller.php
Class ProfilesController extends AppController{
function updateData(){
//output incoming request for debugging purposes
debug($this->request->data);
//here i fetch the db to get the id of user
$options =
array('conditions' => array('User.username' => $this->Auth->user('username')),
'fields' => array('User.id')
);
//find user id so we can find user in related tables
$id = $this->Profile->User->find('first', $options);
//here I modify request data so cakephp finds the users through primaryKeys
$this->request->data['Profile']['user_id'] = $id['User']['id'];
$this->request->data['Associate']['user_id'] = $id['User']['id'];
$this->request->data['User']['id'] = $id['User']['id'];
if($this->request->is('post'){
//this updates data in table no problem
$this->Profile->save($this->request->data);
//this updates data in table no problem either
$this->Profile->Associate->save($this->request->data);
//this returns false...it breaks here
$this->Profile->User->save($this->request->data);
}
}
}
Table structure:
User
|id|int|auto increment
|firstname|varchar
|lastname|varchar
|date|timestamp
Profile
|id|int|autoincrement
|user_id|int
|age|int
|height|int
Associate
|id|int|autoincrement
|user_id|int
|city|varchar
|country|varchar
Ok I know what some of you might tell me, why do I do this on the profilesController and
not on the UsersController. Well, my idea is to separate some actual important user
data from the profile data so it's my intention to write the code for profile on the ProfilesController...as I was developing I was assuming that the same Model association would have automatically updated the User.lastname field in the User table..but that is the part where my code breaks and I have tried but I can't make it work
The current association in my mind at least is as follows:
User has one Profile
User has one Associate
Profile belongs to User
Associate belongs to Profile and User
Can anyone tell me what am I doing wrong? i am following what I think is a logical approach for my application, cakephp updates Profile and Associate models but User remains unaffected.
Assuming the primaryKey of your users table is 'id', just remove all of the $primaryKey lines, and try again.
The only reason to set the primary key is if it doesn't follow the default that CakePHP has in place. I would GUESS (can't see your tables) that the primaryKey field in your 'users' table isn't 'user_id' - more likely it's just 'id', and in the other tables, it's 'user_id'. If that's the case, there's no need to specify the $primaryKey, since that's the default of CakePHP.
As it turns out after reading the cakephp documentation (and obviously being a n00b) the reason why my code was breaking is because I had a callback beforeSave in my model. I didn't know that in order to save data I had to disable the callback which was unrelated to the part of the code I presented to you. The solution in a case like this is to do as follows:
$this->Profile->User->save($this->request->data, array('callbacks' => false));
I don't know you guys but sometimes I feel the cakephp documentation is a little too simplistic, I discover this by looking at the API.

CakePHP: calling other Model functions

How can i call, from a Model, a function present in another model? I would like not to repeat code.
We can use Model relation to call the function in another model. Eg.
$this->Model->ModelOne->find();
$this->Model->ModelOne->customFunc();
If there is no relation in the models, The we can use
$this->loadModel('ModelName');
To use in the model.
In this case you can use
$this->ModelName->function();
directly as you've loaded that model.
You should try to have relationships between your models. There are many types of relationships which you can read here...
If you have above said associations, you can access your associated models using:
$this->Model->OtherModel->function();
If your models are not related in any way, you should use:
ClassRegistry::init('OtherModel')->function();
You can check out my question on this where I obtained great answers
User App::import()
App::import('Model','OtherModel');
$attr = new OtherModel();
$attr->Othermodelfunction();
if there's a (direct or indirect) relationship between the model, you can call the function: $this->Model1->Model2->...->Modeln->function();
use bindModel
no, you should use ClassRegistry like so:
//MessagesController - in my send() method...
$this->set('content', ClassRegistry::init('Content')->find('first', array(
'conditions' => array('Content.id' => 3)
)));
NOTE:this is from my controller but I am pretty sure it works in model too.
In 2.x versions the $this->Model1->Model2 syntax answered above will not work. Calling functions from another models in many cases is the job of a controller, not the model. Consider that, the model methods should be limited to querying and updating data whilst maintaining database integrity.
1st method: using the controller
I'll illustrate this with an example of Firm and User models, while Firm hasMany users. This method is recommended, if you plan to add extra controller functionality inbetween, such as setting flash messages or cookies.
User model:
public function saveRegisteredUsers($user_data,$firm_id){ ... }
--
FirmsController:
public function add(){
if($this->Firm->save($this->request->data)){
// set $this->Firm->id here
$this->loadModel('User');
$this->User->saveRegisteredUsers($this->request->data['User'],
$this->Firm->id);
// ...
}
}
2nd method: using the model
For this you will need to have correct model associations. The table names have to be users and firms conventionally. Following the terminology of the example above your relation should be defined as this in the Firm model:
public $hasMany = array( 'User' => array(
'className' => 'User',
));
In the User model, you have to set up the belongsTo association properly:
public $belongsTo = array(
'Firm' => array(
'className' => 'Firm',
'foreignKey' => 'firm_id',
'dependent' => false
)
);
After this, you can call $this->User->saveRegisteredUsers() directly from any of the Firm model methods.
If you have a model function that you want to call from many models, the best approach is to abstract any references to the model name ($this->alias) and place the function in AppModel. Then it is accessible in any of your models.
class AppModel extends Model{
public function myFunction($options = array(){
do some stuff with $this->alias;
}
}

Resources