I'm a newbie to cakePHP and I've taken on creating a very small "status reporting" project, that would allow a user to report their current status on a project that they were assigned on.
I'm currently using the acl, auth, and session components to allow for multiple tier users to manage one another with an admin creating users, projects, and assigning them to one another. I have also fixed it so that when a user is logged in and goes to add a "status" that their login session automatically takes care of the "user_id" for the status:
$this->data['Status']['user_id'] = $this->Auth->user('id');
What I need help doing now is filter the options for the project that the user can pick to add a "status" to.
I currently have a setup with a table that holds the relationships between users and projects named *projects_users* with *id, project_id, user_id* as fields. But, after baking this setup, when the user is adding a "status" the drop down menu provides all projects rather than strictly the ones they are "assigned to."
I would like to filter the user's options to the projects that they are assigned to. Is there considerable more relationships I should be setting up or is this a pretty easy little function to write? Any help would be greatly appreciated and if I have left out information, I'd be glad to post anything else.
Here is the Status Model setup:
class Status extends AppModel {
var $name = 'Status';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Project' => array(
'className' => 'Project',
'foreignKey' => 'project_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
My User Model looks like this:
var $belongsTo = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasMany = array(
'Status' => array(
'className' => 'Status',
'foreignKey' => 'user_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
var $hasAndBelongsToMany = array(
'Project' => array(
'className' => 'Project',
'joinTable' => 'projects_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'project_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
In the database, You should have a column called project_id in the user table that is associated with the project table's id column.
Then, in your User model, you should create a hasMany relationship with the Project model.
class User extends AppModel {
var $name = 'User';
var $hasMany = 'Project';
}
Now, when you fetch a particular user, you will get all projects that the user is associated with.
Related
I am new to cake php . Please be cool with me . I am trying to fetch the data from the two table ce_actionitems and ce_projects.
i want to fetch the data from action items . my model is
public $hasOne = array(
'Project' => array(
'className' => 'Project',
'foreignKey' => 'id',
'joinTable' => 'actionitems',
'unique' => 'keepExisting',
'associatedKey' => 'project_id',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
but is is returning the null data because it is matching id in projects with id in action items. this is the generated query
LEFT JOIN EB.ce_projects AS Project ON (Project.id =
Actionitems.id)
but i want this to be
LEFT JOIN EB.ce_projects AS Project ON (Project.id =
Actionitems.project_id)
i got this working if i mention public $primaryKey = 'project_id'; in my Actionitems but then it stop inserting the action items.
any help will be appriciated.
mention 'project_id' as foreign key in Actionitems
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
In a controller which is completely unrelated - a DoctorsController, I need to get some info from my ChildrenMedicine model and also the name of the medicine which is in it's own related Medicine model. So in my (unrelated) DoctorsController controller I use:
$this->loadModel('ChildrenMedicine');
$this->ChildrenMedicine->recursive = 2;
$childsMeds = $this->ChildrenMedicine->find('all', array('conditions'=>array('child_id'=>$child)));
I expected this to get me an array with all the medicines which the child currently uses, plus the info from the medicines table so I can get the name. However it only gives me:
$childsMeds[0]['ChildrenMedicine']
$childsMeds[1]['ChildrenMedicine']
whereas I was hoping for
$childsMeds[0]['ChildrenMedicine']
$childsMeds[0]['Medicine']
$childsMeds[0]['Child']
$childsMeds[1]['ChildrenMedicine']
$childsMeds[1]['Medicine']
$childsMeds[1]['Child']
Can anyone enlighten me?
EDIT - my ChildrenMedicine model associations:
public $belongsTo = array(
'Child' => array(
'className' => 'Child',
'foreignKey' => 'child_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Medicine' => array(
'className' => 'Medicine',
'foreignKey' => 'medicine_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Use Containable Behavior.
$this->loadModel('ChildrenMedicine');
$this->ChildrenMedicine->Behaviors->load('Containable');
$childsMeds = $this->ChildrenMedicine->find('all',
array(
'conditions' => array('child_id'=>$child),
'contain' => array('Child', 'Medicine')
));
A alternative method would be to use joins to solve this issue. It would be a better option that relying on setting recursive = 2.
$data = $this->ChildrenMedicine->find('all', array('conditions' => array('child_id'=>$child), 'joins' => array(
array(
'table' => 'medicines',
'alias' => 'Medicine',
'type' => 'inner', // could also do outter
'foreignKey' => false,
'conditions'=> array('ChildrenMedicine.medicine_id= Medicine.medicine_id')
))));
You can read more on joins here
For setting up HABTM relationships this article seems helpful. Look at "The Model" code
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'm trying to search based on a field in a related model. I can do so with the belongsTo model, but not the hasMany model. I'm sure this is something simple I'm overlooking but I can't see it. Could someone point me in the right direction (or even the cakebook page I need - searched, but didn't find anything that looked like this). Thanks
In groups controller: (this doesn't work)
$group = $this->Group->find('all', array('conditions' => array('Voucher.id' => '55')));
This does:
$group = $this->Group->find('all', array('conditions' => array('TourOperator.id' => '3')));
Background file snippets:
Group model:
var $belongsTo = array(
'TourOperator' => array(
'className' => 'TourOperator',
'foreignKey' => 'tour_operator_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasMany = array(
'Voucher' => array(
'className' => 'Voucher',
'foreignKey' => 'group_id',
'dependent' => true,
'conditions' => '',
'fields' => '',
'order' => 'Voucher.date, Voucher.meal_type_id'
)
);
Voucher model:
var $belongsTo = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Tour Operator model:
var $hasMany = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'tour_operator_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
Update (as posted in comment below, but clearer to read here)
I've ended up using this.
$groups = $this->Group->Voucher->find('all', array('fields' => 'DISTINCT group_id', 'conditions' => array('Voucher.status' => 'pending')));
$group_ids = Set::extract($groups, '/Voucher/group_id');
$data = $this->Group->find('all', array('conditions' => array('Group.id' => $group_ids)));
I get a distinct list of group IDs matching my criteria, create an array of just the IDs and then use that to pull the groups so that the arrays are ordered as I expect them in my view.
Can you recast your query to $this->Voucher->find, and then use the associated Group data returned from that query? As you say, there's no problem finding groups by their TourOperator id, and the Groups/Voucher relationship looks like it's the inverse of that.
With CakePHP's Containable behaviour you can't use conditions on related data in hasMany and hasAndBelongsToMany associations. This is because it uses seperate queries to get those associations.
With the Linkable behaviour, however, you can use data from hasMany associations to filter records. The behaviour can be downloaded here: https://github.com/Terr/linkable
Linkable uses standard JOINs to, well, join related data. This means it doesn't pull in all the related data (ie, all the Vouchers related to your Group(s)), just like a normal SELECT. However, it can work together with Containable to do both.
You can find examples of how Linkable works in the README.
(disclaimer: I currently maintain Linkable, but that is not the reason I point to it)