So I have this query that I am trying to convert to cake ORM and I do not know how to go about it.
I have a user table and a word table. Users have many words (thats the relationship). I want to write a query that will give me the users that have added the most words in the system. This is the current query I wrote but I am having trouble converting it to cakephp ORM syntax, any ideas?
SELECT
users.username,
COUNT(word) AS n
FROM
users AS users
INNER JOIN words AS words
ON users.userid=words.userid
GROUP BY
users.username
ORDER BY
n DESC
LIMIT 3
There are many ways to write the query:- In controller you can write as -
$options = array(
'fields' => array(
'User.name',
'COUNT(Word.id) as word_count',
),
'group' => 'Word.user_id',
'order' => 'word_count DESC',
);
$users = $this->User->Word->find('first', $options);
debug($users);
In User model you have to write:
public $hasMany = 'Word';
In Word model you have to write:
public $belongsTo = 'User';
Related
I have a few tables that are joined through a distant relationship - for example:
A.id = B.a_id, B.id = C.b_id, C.id = D.c_id
And given A.id, I want to delete all the rows in D that are associated with A.id.
Since Model::deleteAll() does not accept any joins, only conditions, how do I go about it?
All the models (A, B, C, D) already have belongTo relationships defined.
My last resort would be raw SQL, but I would like to know if there's a way in CakePHP to do it.
I could not find similar questions as they all were about deleting ALL the associated data, rather than just one table's data via an associated key.
Use Containable behavior to find D records
public function deleteD($idA){
$this->ModelA->Behaviors->load('Containable');
$options = array(
'contain' => array(
'ModelB' => array(
'ModelC' = array(
'ModelD'
)
)
),
'conditions' => array('ModelA' => $idA)
);
$findDIds = $this->ModelA->find('all',$options);
debug($findDIds); // find right path to ModelD
$ids = Hash::extract($findDIds,'{n}.ModelD.id');
$this->loadModel('ModelD');
foreach($ids as $id){
$this->ModelD->delete($id);
}
}
Note, I not tested this function.
I have use custom query in cakephp but I dont understand how to run custom join query.
I am using this code
$arrayTemp1 = $this->User->query('SELECT DISTINCT
u.id,u.hunting_association FROM ht_users as u LEFT JOIN
`ht_user_animal_prices` as uap ON uap.user_id=u.id WHERE
uap.animal_type_id='.$this->request->data['User']['animal'].' ');
User is the model for ht_users and UserAnimalPrice is the model for ht_user_animal_prices. How to combine the query?
Please help.
If you want to use custom queries and you want the data of UserAnimalPrice model, you just have to put the fields in the query. Something like:
$arrayTemp1 = $this->User->query('SELECT DISTINCT u.id,u.hunting_association, uap.* FROM ht_users as u LEFT JOIN ht_user_animal_prices as uap ON uap.user_id=u.id WHERE uap.animal_type_id='.$this->request->data['User']['animal'].' ');
If you prefer not to use custom queries:
$fields = array('User.id','User.hunting_association','UserAnimalPrice.*');
$join = array(
array(
'table' => 'ht_user_animal_prices',
'alias' => 'UserAnimalPrice',
'type' => 'LEFT',
'conditions' => array('UserAnimalPrice.user_id = User.id')
)
);
$conditions = array('UserAnimalPrice.animal_type_id' => $this->request->data['User']['animal']);
$group = array('User.id');
arrayTemp1=arrayTemp1->find('all',array('fields'=>$fields,'joins'=>$join,'conditions'=>$conditions,'group'=>$group));
This is Correct Query u used .You can also use it in User Model like
public function getCustomUsersQuery()
{
$arrayTemp1 = $this->query("
SELECT
DISTINCT u.id,u.hunting_association FROM ht_users as u
LEFT JOIN
ht_user_animal_prices as uap ON uap.user_id=u.id
WHERE
uap.animal_type_id='".$this->request->data['User']['animal']."'");
return $arrayTemp1;
}
And call inside Users Controller
$result = $this->getCustomUsersQuery();
Sorry I cannot comment on Rizwan answer. If you received the "Call to a member function..." problem, make sure you have the following code
public $uses = array('User');
this tells that you want to access the user model in that controller. Then you will be allowed to do so.
For example I have 3 tables:
songs(id, song_name)
song_category(id, song_id, category_id)
categories(id, name)
I want to get songs which have categories with id higher than 5. I want to do it using ORM, not with simple SQL query. Is it possible to do it with one query like this:
$songs = ORM::factory("songs")->where("category.id > 5")
No, you cannot do this with a single Kohana ORM call.
The best way I have found to do it is something like this, which makes a modification to the SQL query that the ORM will generate:
// Get the basic "song" model
$songs = ORM::factory("songs");
// Get the information about how it is connected to
// the "category" model using the `through` model
$song_relations = $results->has_many();
$category_relation = $song_relations['categories'];
$through = $category_relation['through'];
// Join on `through` model's target foreign key (far_key) and `target` model's primary key
$join_col1 = $through.'.'.$category_relation['foreign_key'];
$join_col2 = $songs->object_name().'.'.$songs->primary_key();
$songs->join($through)->on($join_col1, '=', $join_col2);
// Now, filter on the
$songs->where($through.'.'.$category_relation['far_key'], '>', 5);
$arr = $results->find_all()->as_array();
You could save some code by hardcoding the values in the join method call, but this way leverages the ORM relation definitions that you already have.
This assumes that your Song model has the following code in it:
protected $_has_many = [
'categories' => [
'model' => 'category',
'through' => 'song_category',
'foreign_key' => 'song_id',
'far_key' => 'category_id',
]
];
So, I'm trying to do a query to get the last entries of each user using CakePHP relationship. In my Map and User model I have:
class Map (and User) extends AppModel {
public $hasMany = 'Geolog';
}
And I'm trying to doing this query to catch the last entry of each user. dg_maps and dg_users have a many to many relation.
SELECT
dg_users.id,
dg_users.email,
dg_maps.id,
dg_maps.latitude,
dg_maps.longitude,
dg_maps.timestamp
FROM
(SELECT
*
FROM
dg_maps
ORDER BY dg_maps.timestamp DESC) dg_maps
JOIN
dg_geologs ON dg_geologs.map_id = dg_maps.id
JOIN
dg_users ON dg_users.id = dg_geologs.user_id
GROUP BY dg_geologs.user_id;
What I'm doing now is:
$this->Map->query('SELECT dg_users.id, dg_users.email, dg_maps.id, dg_maps.latitude, dg_maps.longitude, dg_maps.timestamp FROM (SELECT * FROM dg_maps ORDER BY dg_maps.timestamp DESC) dg_maps JOIN dg_geologs ON dg_geologs.map_id = dg_maps.id JOIN dg_users ON dg_users.id = dg_geologs.user_id GROUP BY dg_geologs.user_id');
But, I know that a better method then this, using Cake methods. So, how can I do this?
Thank you all!
I used this for my project it is very simple
Easy way to generate CakePHP HABTM joins for use in pagination.
So put this in your model (note for function arguments - there is error in function from link )
public function generateHabtmJoin ($joinModel, $joinType = 'INNER') {
// If the relation does not exist, return an empty array.
if (!isset($this->hasAndBelongsToMany[$joinModel])) {
return array();
}
// Init joins, and get HABTM relation.
$joins = array();
$assoc = $this->hasAndBelongsToMany[$joinModel];
// Add the join table.
$bind = "{$assoc['with']}.{$assoc['foreignKey']} = {$this->alias}.{$this->primaryKey}";
$joins[] = array(
'table' => $assoc['joinTable'],
'alias' => $assoc['with'],
'type' => $joinType,
'foreignKey' => false,
'conditions' => array($bind),
);
// Add the next table.
$bind = "{$joinModel}.{$this->{$joinModel}->primaryKey} = {$assoc['with']}.{$assoc['associationForeignKey']}";
$joins[] = array(
'table' => $this->{$joinModel}->table,
'alias' => $joinModel,
'type' => $joinType,
'foreignKey' => false,
'conditions' => array($bind),
);
return $joins;
}
and you can call this function from your controller
You have example in link
I have two models, Plant and Emp, that have a Has And Belongs To Many relationship. I've configured them to be associated and the query to get the data for each is correct, but the problem is Plant and Emp are on different databases. Emp is on Database 1, Plant is on Database 2. Because of this they don't query the join table properly; the join table is only on Database 1.
When the Plant model tries to access the join table it's querying Database 2, which does not have this data.
This is the association Emp has for Plant.
var $hasAndBelongsToMany = array(
'Plant' =>
array(
'className' => 'Plant',
'joinTable' => 'emp_plant',
'foreignKey' => 'employee_id',
'associationForeignKey' => 'LocationID',
'unique' => true,
'conditions' => '',
)
);
Update:I tried to set a "finderQuery" attribute to let me query the join table, but I don't know how to give a raw SQL query like that and allow it to dynamically use the id for the instance of the Model instead of a predefined value.
I can set something like
SELECT * FROM [Plant] AS [Plant] JOIN [DB].[DBO].[empplant] AS
[EmpPlant] ON ([EmpPlant].[employee_id] = **4**
AND [EmpPlant].[ID] = [Plant].[LocationID])
Which will give me the correct data for one employee, but I don't know how to make this finderQuery a dynamic query. There has to be a way for this to work.
Try
var $useDbConfig = 'alternate';
in your Model Class.
I needed to use a custom finderQuery and use the special {$__cakeID__$} identifier in place of the model ID being matched. This is a fixed version of the sample above, set as the finder query in the relationship entry for the $hasAndBelongsToMany array.
'finderQuery'=>'SELECT * FROM [Plant] AS [Plant] JOIN [DB].[DBO].[empplant] AS
[EmpPlant] ON ([EmpPlant].[employee_id] = {$__cakeID__$}
AND [EmpPlant].[ID] = [Plant].[LocationID])'
This works but if anyone knows how to fix this situation without a custom finder query (what I was trying to avoid by using associations) please post an answer and I will mark that correct instead.