I currently have 2 db tables, one containing a list of shows and one containing times these shows are on.
So I have a HAS_MANY relationship between the two.
I have successfully managed to pull out the films and the times associated to them via the HAS_MANY relationship.
The section I am struggling with is I want to show the first 10 films that have times against them (some do not). The code below pulls out just the first 10 irrelevant of whether they have times against them:
$films=filmrecord::model()
->findAll(array(
'order' => 'priority',
'limit' => 10
));
foreach ($films as $thefilm) {
array_push($filmListings, array(
'id' => $thefilm->id,
'title' => $thefilm->title,
'url' => $thefilm->url,
'poster_url' => $thefilm->p_url,
'times' => $thefilm->filmtimes
));
}
return filmListings;
The HAS_MANY relationship I am using is:
'filmtimes' => array(self::HAS_MANY, 'FilmsTimesRecord', 'film_id')
I know I could simply put in a if statement to check if $thefilm->filmtimes was set however ideally I would like to check at an db point to avoid having to pull all films out before checking.
Is there any way I can the existence of a relationship within the findAll?
Any pointers would be much appreciated.
Regards
wizzer
please try this solution for find all film with film times records using has_many relationship.
'filmtimes' => array(self::HAS_MANY, 'FilmsTimesRecord', 'film_id')
$films=filmrecord::model()->with('filmtimes')->findAll(array('order' => 'priority','limit' => 10));
Related
I am building a small core piece for my DataMapper ORM library in CodeIgniter to control user's access/edit rights (CRUD) over DataMapper objects itself. For this I want to link a DataMapper Entity, Userrole and Userright together.
The documentation is here http://datamapper.wanwizard.eu/pages/advancedrelations.html (Under head; Multi-table Relationships).
So if I fill in the join table by hand I can just get the values without any problems. The only problem is saving the relationship.
Here are my Model-rules
Userrole
var $has_many = array(
'userright' => array(
'join_table' => 'dm_entities_userrights_userroles',
),
'dm_entity' => array(
'join_table' => 'dm_entities_userrights_userroles',
),
);
Userright
var $has_many = array(
'dm_entity' => array(
'join_table' => 'dm_entities_userrights_userroles',
),
'userrole' => array(
'join_table' => 'dm_entities_userrights_userroles',
),
);
Dm_entity
var $has_many = array(
'userrole' => array(
'join_table' => 'dm_entities_userrights_userroles',
),
'userright' => array(
'join_table' => 'dm_entities_userrights_userroles',
),
);
It seems odd I declared the table twice constantly, but it seems not to work if I don't.
So here's my retrieval code for this relation form the perspective of the Userright;
$userrole = new Userrole;
$userrole->get_where(array('name' => 'Administrator'));
$dm_entity = new Dm_entity;
$dm_entity->get_where(array('name' => 'User'));
$userrights = new Userright;
$userrights->where_related($userrole);
$userrights->where_related($dm_entity)->get();
$output = '';
foreach ($userrights as $userright) {
$output .= '<i><b>'.$userright->name.'</b></i> and ';
}
$output = substr($output, 0, -5);
echo '<b>'.$userrole->name.'</b> has the right to '.$output.' the DataMapper entity <b>'.$dm_entity->name.'</b>.'.br();
So this works without any problems either. But now the saving part:
$new_userright = new Userright;
$new_userright->get_where(array('name' => 'Update'));
$new_userright->save(array($userrole, $dm_entity));
Which results in two entries in the dm_entities_userrights_userroles table where 1 entry has dm_entity_id empty and the other the userrole_id empty.
I hope to avoid making a separate Model for the join table to solve this.
Does anyone know how I can get this to work so it makes one correct entry, instead of two scattered ones?
This is not going to work (as you have noticed).
A relation is between two models, and for each Many to Many relation you need a separate join table.
Although technically you can create a join table with 10 FK's to 10 different models, Datamapper will not be aware of that, and will treat the table as different for every relation, causing duplicates to appear.
The only way to make this work, is to define a model for the join table too, and give that one-to-many relations to each of the other models. You can then use that model to manually add relations by assigning or updating the FK values, and still use the many-to-many relations for retrieval and update.
I'm trying to do this:
$this->PickTicketLineModel->PickTicket->SO->CustomerShipToModel
If I go through each piece, I get a model for PickTicketLine, one for PickTicket, and one for SO. When I get to CustomerShipToModel, I get back null. Since this should match an existing row in the database, I'm assuming something is going wrong with the relation. Any suggestions?
Pick Ticket Line Table Relations
'PickTicket' => array(self::BELONGS_TO, 'TblwhPickTicket', 'PickTicketNumber'),
Pick Ticket Table Relations
'SO' => array(self::BELONGS_TO, 'TblsoSO', 'SONumber'),
SO Table Relations
'CustomerShipToModel' => array(self::BELONGS_TO, 'TblarCustomerShipTo', 'CustomerShipTo'),
Customer Ship To Table Relations
'CustomerShipToModel' => array(self::HAS_MANY, 'TblarsoSO', 'CustomerShipTo'),
This is how I got the PickTicketLine model in the first place:
$this->pickTicketLine = TblwhPickTicketLine::model()->with('PickTicket','PickTicket.SO')->FindByPk(array('PickTicketNumber'=>$pickTicketNumber, 'PickTicketLineNumber'=>$pickTicketLineNumber));
Try to eager load all the related data explicitly:
$this->pickTicketLine = TblwhPickTicketLine::model()->with(array(
'PickTicket' => array(
'with' => array(
'SO' => array(
'with' => array('CustomerShipToModel')
)
)
)
))->findByPk(array(
'PickTicketNumber'=>$pickTicketNumber,
'PickTicketLineNumber'=>$pickTicketLineNumber
));
If that does not work, either, log your database requests and inspect the query, as suggested by Pentium10 in the comments. You could just let phpMyAdmin perform the query or use the MySQL "EXPLAIN" keyword for additional information. In order to get the relevant logs, you have to add the following lines to your config/main.php file:
'components'=>array(
'db'=>array(
'enableProfiling' => true,
'enableParamLogging' => true
)
)
I've been quite some time trying to use the Containable Behavior in CakePHP but I can't get to make it work as I expected.
My application is different, but to simplify I'll put this example. Let's say I have a forum with threads and activities, and the activities can be rated. The general relations would be:
Forum: hasMany [Thread]
Thread: belongsTo [Forum], hasMany [Activity]
Activity: belongsTo [Thread], hasMany [Rating]
Rating: belongsTo [Activity]
What I want to achieve is, using the find method, get all the ratings performed on a certain forum. What I suppose should be done is the following:
$this->Rating->find('count', array(
'contain' => array(
'Activity' => array(
'Thread'
)
),
'conditions' => array(
'Thread.forum_id' => 1
)
));
But the result query is:
SELECT COUNT(*) AS `count` FROM `ratings` AS `Rating` LEFT JOIN `activities` AS `Activity` ON (`Rating`.`activity_id` = `Activity`.`id`) WHERE `Thread`.`forum_id` = 1;
I've accomplished this using the 'joins' option, but it's more complex and I have to use this kinda action in many situations.
All the files related with the example can be found here: http://dl.dropbox.com/u/3285746/StackOverflow-ContainableBehavior.rar
Thanks
Update 23/11/2011
After investigating the framework and thanks to the answers of Moz Morris and api55 I found the source of the problem.
The basic problem was that, as I understood CakePHP, I thought it was querying using joins each time. The thing it that it doesn't do that, the real operation it would perform to obtain the result I was looking for would be something like this:
SELECT * FROM Rating JOIN Activity...
SELECT * FROM Activity JOIN Thread...
SELECT * FROM Activity JOIN Thread...
...
Meaning that it would do a query to get all the activities and then, for each activity, perform a query to get the Threads... My approach was failing not because of the Containable Behaviour being used wrong, but because the 'conditions' option was applied to all queries and, on the first one, it crashed because of the absence of the Thread table. After finding this out, there are two possible solutions:
As api55 said, using the conditions inside the 'contain' array it would apply them only to the queries using the Thread table. But doing this the problem persists, because we have way too many queries.
As Moz Morris said, binding the Thread model to Rating would also work, and it would perform a single query, which is what we want. The problem is that I see that as a patch that skips the relations betweem models and doesn't follow CakePHP philosophy.
I marked api55 solution as the correct because It solves the concrete problem I had, but both give a solution to the problem.
First of all, have you put the actAs containable variable in the appModel?? without it this beahaviour won't work at all (i see it is not working correctly since it didn't join with Thread table)
I would do it from the top, i mean from forum, so you choose your forum (im not sure you want forum or thread) and get all its rating, if theres no rating you will end up with the rating key empty.
something like this
appModel
public $actsAs = array('Containable');
rating controller
$this->Rating->Activity->Thread->Forum->find('count', array(
'contain' => array(
'Thread' => array(
'Activity' => array(
'Rating' => array (
'fields' => array ( 'Rating.*' )
)
)
)
),
'conditions' => array(
'Forum.id' => 1
)
));
Then if you need only a value in rating table just use Set:extract to get an array of this value.
As you did it IT SHOULD work anyways but i sugest not to use forum_id there, but in conditions inside contain like this
'contain' => array(
'Activity' => array(
'Thread' => array(
'conditions' => array('Thread.forum_id' => 1)
)
)
),
Also, never forget the actsAs variable in the model using the containable behaviuor (or in app model)
Whist I like api55's solution, I think the results are a little messy - depends on what you intend to do with the data I guess.
I assume that when you said using the 'joins' method you were talking about using this method:
$this->Rating->bindModel(array(
'belongsTo' => array(
'Thread' => array(
'foreignKey' => false,
'conditions' => 'Thread.id = Activity.thread_id',
),
'Forum' => array(
'foreignKey' => false,
'conditions' => 'Forum.id = Thread.forum_id'
)
)
));
$ratings = $this->Rating->find('all', array(
'conditions' => array(
'Forum.id' => 1 // insert forum id here
)
));
This just seems a little cleaner to me, and you don't have to worry about using the containable behaviour in your AppModel. Worth considering.
I am trying to force a join on paginate function of cakephp.
A user has messages which means it will be message belongs to user.
I have to show them in a list so i need to use paginate here.
Problem is it doesnt shows me the record of the model which i intend to bind
My code is:
$userId = $this->Session->read('SESSION_ADMIN.id');
$this->helpers['Paginator'] = array('ajax' => 'Ajax');
$this->Message->bindModel(
array(
'belongsTo'=>array(
'Npo'=>array(
'className' => 'Npo',
'foreignKey' => 'reciever_id',
'fields' => 'Npo.username'
)
)
)
);
$this->paginate = array('conditions'=>array('Message.sender_id'=>$userId,'Message.sender'=>'Admin'),
'order' => array('Message.modified DESC'),
'limit' =>'1'
);
$sentMsg = $this->paginate('Message');
//$sentMsg = $this->Message->find('all');
pr($sentMsg);die();
when i uncomment the FIND statement it shows me record but in case of paginate it doesnt.
Also it doesnt shows me the join in the paginate query but it does in counting record.
Any one have an idea.I dont want to use paginate Join here.Is there a way to enforce a belongs to here?
Regards
Himanshu Sharma
Have you tried:
$this->Message->bindModel(
array(
'belongsTo'=>array(
'Npo'=>array(
'className' => 'Npo',
'foreignKey' => 'reciever_id',
'fields' => 'Npo.username'
)
)
), false // Note the false here!
);
The paginator actually executes two queries: one to count the total number of records, and one to actually fetch the desired records. By default, associations created on the fly using bindModel() are reset after each query. It depends on the Cake version which query comes first, but I believe that in your case it is the count query; leaving the actual results query without the association. Setting false on on the second argument of bindModel() prevents the association from being reset after the first query.
There are 2 Models: Project & Category that are bind with HABTM relationship.
I would like to perform a search from projects controller that can do the following:
FIND all DISTINCT Project.scedule WHERE Category.slug != 'uncategorised'
Apologies for the syntax, I'm no sequel expert.
What I have managed to do is to retrieve all projects that do not belong to Category uncategorised into an array however I'm not sure as to how to search again the array result for DISTINCT Project.schedule values (needed to fill out a form drop down)
Before answer this question,again I suggest you to read the HABTM in cookbook of CAKEPHP carefully,then you can finish jobs like this yourself.
$this->Project->bindModel(array(
'hasOne' => array(
'CategorysProject',
'FilterCategory' => array(
'className' => 'Category',
'foreignKey' => false,
'conditions' => array('FilterCategory.id = CategorysProject.category_id')
))));
$this->Project->find('all', array(
'fields' => array(DISTINCT (Project.scedule)),
'conditions'=>array('FilterCategory.slug !='=>'uncategorised')
));