Yii 'limit' on related model's scope - php

I have a model called Guesses that has_many Comments. I'm making eager queries to this to then pass on as JSON as response to an API call.
The relations are obviously set between the two models and they are correct(one2many <=> belongs2)
I added a scope to Comments called 'api' like this:
public function scopes()
{
return array(
'api' => array(
'select' => 'id, comment, date',
'limit'=>3,
'order'=>'date DESC',
'together'=>true,
),
);
}
And I'm running the following one-liner query:
$data = Guesses::model()->with('comments:api')->findAll();
The issue here is that when calling the 'api' scope using a with('relation'), the limit property simply doesn't apply.
I added the 'together'=>true there for another type of scope, plus I hear it might help. It doesn't make a difference.
I don't need all the comments of all Guesses. I want the top 3 (or 5). I am also trying to keep the one-liner call intact and simple, manage everything through scopes, relations and parameterized functions so that the API call itself is clean and simple.
Any advice?

There are several methods to achieve this, which i know.
1st. You can add limit to relation, for ex.
'photos' => array(self::HAS_MANY, 'Photo', 'companyId',
'limit'=>3
)
2nd. You can call limit results when call relation, for ex.
$model->photos(array('limit' => 4));
Maybe there are some other methods, but i don't know them.

According to the documentation, that should be ok..
My only suggestions would be:
$data = Guesses::model()->with(
array('comments'=>array('scopes'=>array('api')))
)->findAll();
or:
$data = Guesses::model()->findAll(array(
'with'=>array('comments'=>array('scopes'=>array('api')))
));
but that should be the same logic. I don't think together will help you.. it is true by default.
Update
These examples are taken straight from the relational documentation
$posts=Post::model()->with('comments:recently:approved')->findAll();
or since 1.1.7:
$posts=Post::model()->with(array(
'comments'=>array(
'scopes'=>array('recently','approved')
),
))->findAll();
or since 1.1.7
$posts=Post::model()->findAll(array(
'with'=>array(
'comments'=>array(
'scopes'=>array('recently','approved')
),
),
));
and lazy-loading:
$approvedComments = $post->comments('comments:approved');
Which suggests that your code should work.. what query is being run in your trace?
Do you have any default scopes that you haven't posted? Are their any extra options defined in your relation? I have had trouble with both of these interfering with named scopes.

Related

cakephp 4.x query to get results from database

I came across a problem recently that I have to make a find(all) query in cakephp.
Problem is I was unable to call model function to cakephp controller & add a command in function that would allow me to make a find(all) function in cakephp and retrieve all rows in a database that I was looking for.
I would like to make a simple query to make a call to database table that is in database that retrieves rows in table that are matching query.
public function display(){
$query = $HouseParty->find('all', [
'conditions' => ['HouseParty.popular >' => 0],
'contain' => ['title', 'image', 'popular'],
'limit' => 6
]);
$results = $query->all();
var_dump($results);
}
Also what is code to make call to model in Cakephp Controller. So controller knows that we are making calls to models from controller. So we can interact with models from controller because I intend to make multiple calls to different models from same controller in future. Please be helpful looking for a response been stuck on this problem for days just making a question here that is relevant & resourceful for others as well. I would like to make a question come handy to other community members I would just need to get code apart from what I have mentioned above that what code should I write in order to make a call for calling models in one controller - call to multiple models from one controller. Is the above code correct and is so what makes it from giving an error as below. So what is code to make calls to models from database.
Error i get is below:
Call to a member function find() on null
I think you should start with a simple query based on your model.
I will assume your model is HouseParties (a database table named house_parties).
Then in your HousePartiesController, you can query:
$query = $this->HouseParties->find('all', [
'conditions' => ['popular >' => 0],
'limit' => 6
]);
I think you may be misusing 'contain' as a select. 'contain' is for related tables, not fields in the table you are querying.
If you go through the CMS tutorial in full https://book.cakephp.org/4/en/tutorials-and-examples.html it should be clear.

Yii: Getting distinct field values from a related model using scopes

I'm working on the Admin view in Yii for my Project model.
One of the columns with a filter is user (owner) of the project.
I can do something like this:
'filter'=> CHtml::dropDownList('Project[user_id]', $model->user_id,
CHtml::listData(User::model()->orderAsc()->findAll(),
'id','username'),array('empty'=>'(select)')),
Which gives me a list of all users in the user table, but I'd like to create something that pulls the distinct users who own a project (I've got 200 users, but only a handful tend to create projects).
Project and User are related via:
'user' => array(self::BELONGS_TO, 'User', 'user_id')
I've tried a bunch of option in the findAll() method, but none worked, I'm not trying to do something with a scope.
So far I've tried this:
'filter'=> CHtml::dropDownList('Project[user_id]', $model->user_id,
CHtml::listData(Project::model()->with('user')->ownerUsernames()->
findAll(), 'id','username'),array('empty'=>'(select)')),
and my Scope is defined as:
'ownerUsernames' => array(
'with' => 'user.username',
'select' => 'user.username',
'distinct' => true,
),
I've tried so many iterations of the above I've lost count 'with'=>'username' 'select'=>'username' etc., but just can't seem to get it to work.
I've tried replace Project::model() with $model just because I thought it might have something to do with it, but no luck.
Any ideas are appreciated. Very new to Yii, but this seems like something it can do.
You have everything ready. Define for the project model a getter function like
public function getUsername()
{
return $this->user->name;
}
Now you should be able to use
CHtml::dropDownList('Project[user_id]', $model->user_id,
CHtml::listData(Project::model()->with('user')->ownerUsernames()->
findAll(), 'id','username'),array('empty'=>'(select)'))
The logic is that CHtml::listData will get the projects as a model, it will create the keys using $project->id and it will create the values using $project->username. Because you created the getted function it will know what $project->username is. Unfortunately CHtml::listData(Project::model()->with('user')->ownerUsernames()->findAll(), 'id','user.name') will not work because it cannot execute 'user.name' or anything like that.
actually you can do many things to accomplish that but some methods would be not appropriate due to time consumption. I prefer to tackle this problem as
Make a table in DB named user_project with attributes
id
user_id(fk to user table)
project_id(fk to project table)
When you create a project then also populate user_project with given fields.
Make its model and you will see relations in it. Now make a function like
public function getName()
{
return user->name;//here i have assumed that the relation name is user
}
Now query this table like
$user=UserProject::mode::findAll();
$list=CHtml::listData($user,'user_id','name');
and use this list to populate the dropDownList. The benefit of this method is that you dont need to query all users of the user table.

CakePHP HABTM Filtering

I've got two tables - users and servers, and for the HABTM relationship, users_servers. Users HABTM servers and vice versa.
I'm trying to find a way for Cake to select the servers that a user is assigned to.
I'm trying things like $this->User->Server->find('all'); which just returns all the servers, regardless of whether they belong to the user.
$this->User->Server->find('all', array('conditions' => array('Server.user_id' => 1))) just gives an unknown column SQL error.
I'm sure I'm missing something obvious but just need someone to point me in the right direction.
Thanks!
Your table names are right. There are many ways to do this:
Use the Containable behavior
In your AppModel, set the following:
var $recursive = -1;
var $actsAs = array('Containable');
Then, use the following code to query your servers:
$userWithServers = $this->User->find('all', array(
'conditions' => array('User.id' => 1),
'contain' => array('Server')
));
Note that we are querying the User model, instead of the Server model to accomplish this.
Use bindModel
$this->Server->bindModel(array('hasOne' => array('UsersServer')));
$this->Server->find('all', array(
'fields' => array('Server.*'),
'conditions' => array('Server.user_id' => 1)
));
I personally don't recommend using bindModel a lot. Eventually, your code becomes a bit unmanagable. You should try using the Containable behavior whenever possible. The code looks cleaner and simpler. More on the bindModel method can be found here.
HTH.
I think you're supposed to name tour table user_servers.

adding hasMany association causes find() to not work well

OK, I am a little bit lost...
I am pretty new to PHP, and I am trying to use CakePHP for my web-site.
My DB is composed of two tables:
users with user_id, name columns
copies with copy_id, copy_name, user_id (as foreign key to users) columns.
and I have the matching CakePHP elements:
User and Copy as a model
UserController as controller
I don't use a view since I just send the json from the controller.
I have added hasMany relation between the user model and the copy model see below.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Without the association every find() query on the users table works well, but after adding the hasMany to the model, the same find() queries on the users stop working (print_r doesn't show anything), and every find() query I am applying on the Copy model
$copy = $this->User->Copy->find('all', array(
'condition' => array('Copy.user_id' => '2')
));
ignores the condition part and just return the whole data base.
How can I debug the code execution? When I add debug($var) nothing happens.
I'm not an expert, but you can start with the following tips:
Try to follow the CakePHP database naming conventions. You don't have to, but it's so much easier to let the automagic happen... Change the primary keys in your tabel to 'id', e.g. users.user_is --> users.id, copies.copy_id -->copies.id.
Define a view, just for the sake of debugging. Pass whatever info from model to view with $this->set('users', $users); and display that in a <pre></pre> block
If this is your first php and/or CakePHP attempt, make sure you do at least the blog tutorial
Make CakePHP generate (bake) a working set of model/view/controllers for users and copies and examine the resulting code
There's good documentation about find: the multifunctional workhorseof all model data-retrieval functions
I think the main problem is this:
'condition' => array('Copy.user_id' => '2')
It should be "conditions".
Also, stick to the naming conventions. Thankfully Cake lets you override pretty much all its assumed names, but it's easier to just do what they expect by default.
The primary keys should be all named id
The controller should be pluralised: UsersController
First off, try as much as possible to follow CakePHP convention.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Your association name is 'Copy' which is a different table and model then on your classname, you have 'Friendship'.
Why not
var $hasMany = array(
'Copy' => array('className'=>'Copy')
);
or
var $hasMany = array(
'Friendship' => array('className'=>'Friendship')
);
or
var $hasMany = array(
'Copy' => array('className'=>'Copy'),
'Friendship' => array('className'=>'Friendship')
);
Also, check typo errors like conditions instead of condition
Your table name might be the problem too. I had a table named "Class" and that gave cake fits. I changed it to something like Myclass and it worked. Class was a reserved word and Copy might be one too.

How to limit the the number of row retrieved from an associated table in CakePHP?

Assuming two database tables: Funds and Prices, in which Funds hasMany Prices.
What I wanted to do is to retrieve the latest 15 prices of a particular fund in a certain scenario. Is there a means in CakePHP to make a $this->Fund->find('all') call that would allow me to limit the number of rows to be retrieved from the associated Price table?
Note that I prefer not setting the 'limit' option in the Fund model's $hasMany variable.
Note on accepted answer [Nov 2]:
In Jason's answer which I had accepted, I personally opt for the bindModel solution as I felt despite feeling a bit “hack-y”, it bodes much better with me as to make a one-off override on the default Model bindings.
The code I used is as follows:
$this->Fund->bindModel(array(
'hasMany' => array(
'Price' => array(
'limit' => 15,
'order' => 'Price.date DESC'
)
)
);
No unbindModel is necessary. More information could be read from “3.7.6.6 Creating and Destroying Associations on the Fly” in the CakePHP manual.
You can use the Containable behavior to accomplish this easily.
in your AppModel or Fund model add :
var $actsAs = array('Containable');
then in your controller you can add the 'contain' option to your find('all') :
$this->Fund->find('all', array(
'contain' => array(
'Price' => array(
'limit' => 15,
'order' => 'Price.date DESC')
)));
More information is available in the book : http://book.cakephp.org/view/474/Containable
As I understand the question, you don't want to set the limit statically in the model. If you wish to use a hasMany association, there really isn't any other way that I'm aware of other than changing that limit one way or another.
Here are a few ways to dynamically change it:
You could change the limit with a call to Funds->bindModel(). (This may require an unbindModel() first, but I can't remember for certain)
You could add a function to Funds to change the limit directly. (See example below.)
If you're feeling ambitious, you could write a Behavior to add the functionality to specify it per find() call similarly to the Containable behavior.
Example for #2
<?php
function setPriceLimit($limit = 10) {
// Assuming the association is 'Price'
$this->hasMany['Price']['limit'] = $limit;
}
?>
Your $this->Model->find('all') could include conditions. ie.
$this->Model->find('all', array('conditions' => array('Model.field' => $value), 'limit' => 15));
For more info check the cake docs.
Retrieving Your Data

Categories