I'm using Cake version 2.5.4
I have two tables.
A call arbols which consists of the following fields:
id (int 11)
nombre (varchar (255)
especie:id (int 11)
The second table called fotos consists of the following fields:
id (int 11)
foto (varchar 255)
foto_dir (varchar 255)
arbol_id (int 11)
Although the Arbol model is related to the Especie model,
I leave it aside to the latter because it is not a reason for consultation.
The Arbol Model has a hasMany relationship with the Foto model.
In the Arbol model I have the following:
public $hasMany = array(
'Foto'=> array(
'className' => 'Foto',
'foreignKey' => 'arbol_id',
'dependent' => true
)
);
In the Foto model I have the following:
public $belongsTo = array(
'Arbol'=> array(
'className'=>'Arbol',
'foreign_key'=>'arbol_id'
)
);
Now, inside ArbolsController in public function view ($ id = null)
I want to do the following SQL query:
SELECT * FROM arboles as a join fotos as f on a.id=f.arbol_id
So I return all the photos related to an id of a particular tree passed as parameter in view
If this query is done using MySQL
$registros=mysqli_query($conexion," select * from arboles as a join fotos as f on a.id=f.arbol_id")
it works.
But if I want to do it using the query method in such ways:
$registros = $this->Arbol->query("select * from arboles as a INNER JOIN fotos as f ON a.id=f.arbol_id");
$registros = $this->Arbol->query("select * from arboles as a INNER JOIN fotos as f ON a.id=f.arbol_id");
It does not work
Reading the Cookbook I see there is a way to make joins.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
I dont understand her
I would appreciate it if you can explain it to me.
From already thank you very much!
You shouldn't be using the query method directly if you can avoid it.
What you can do is using a standard find, joining your tables with containable (or linkable).
Something like this should work:
$registros = $this->Arbol->find('all', array(
'contain' => 'Foto'
));
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
There is another way by you can join the tables in cakephp i.e customized joins
Create an array of tables whom you want to join, such as
$joins = array(
array(
'table' => 'fotos',//Table name
'alias' => 'Foto', //Model name
'type' => 'INNER', // type of join
'conditions' => array(
'Arbol.id = Foto.arbol_id'
) //Condition for join
)
);
$this->Arbol->find('all', array(
'joins' => $joins,
'conditions'=> array(Arbol.id => $id),
'fields' => array()
))
This will return all the photos information related to an id
Related
I have 3 blog tables in my CakePHP application that are linked with a HABTM association following the CakePHP naming conventions: posts - post_tag_links - tags.
I try to get an array with all the posts that are linked to a specific tag (e.g. "design"). This query works for me:
$this->Post->query("
SELECT
Post.id, Post.title FROM posts AS Post
LEFT JOIN
post_tag_links AS PostTagLink ON Post.id = PostTagLink.post_id
LEFT JOIN
tags AS Tag ON Tag.id = PostTagLink.tag_id
WHERE
Tag.slug = 'design'
GROUP BY
Post.id"
);
CakePHP then generates the following query and gave me 4 results:
SELECT
Post.id,
Post.title
FROM
posts AS Post
LEFT JOIN
post_tag_links AS PostTagLink
ON Post.id = PostTagLink.post_id
LEFT JOIN
tags AS Tag
ON Tag.id = PostTagLink.tag_id
WHERE
Tag.slug = 'design'
GROUP BY
Post.id
BUT... to do some best practice, it's better to not use the "query" method. So I tried the "find all" method:
$this->Post->find('all', array(
'fields' => array(
'Post.id',
'Post.title'
),
'joins' => array(
array(
'table' => 'post_tag_links',
'alias' => 'PostTagLink',
'type' => 'LEFT',
'conditions' => array(
'Post.id' => 'PostTagLink.post_id'
)
),
array(
'table' => 'tags',
'alias' => 'Tag',
'type' => 'LEFT',
'conditions' => array(
'Tag.id' => 'PostTagLink.tag_id',
)
)
),
'conditions' => array(
'Tag.slug' => 'design'
),
'group' => 'Post.id'
)
));
CakePHP then generates the following query and gave NO single result:
SELECT
`Post`.`id`,
`Post`.`title`
FROM
`kattenbelletjes`.`posts` AS `Post`
LEFT JOIN
`kattenbelletjes`.`post_tag_links` AS `PostTagLink`
ON (
`Post`.`id` = 'PostTagLink.post_id'
)
LEFT JOIN
`kattenbelletjes`.`tags` AS `Tag`
ON (
`Tag`.`id` = 'PostTagLink.tag_id'
)
WHERE
`Tag`.`slug` = 'design'
GROUP BY
`Post`.`id
After a lot of trial and error, I discovered the problem is the backticks that CakePHP creates when building up that last query.
My question is: what's the difference between the query with the backticks and the one without the backticks? And how can you leave those backticks in CakePHP?
Thanks ;)
The backticks most probably aren't the problem, as all they do is escaping the identifiers. This is a pretty easy find btw.
http://dev.mysql.com/doc/refman/5.7/en/identifiers.html
Using backticks around field names
The actual problem is more likely that you've defined the conditions in the wrong way, what you are doing there is creating string literal comparision conditions, ie
`Post`.`id` = 'PostTagLink.post_id'
Comparing the id column value to the string PostTagLink.post_id will of course fail.
The correct way to define identifier comparisons is to supply the conditon fragment as a single value instead of a key => value set, ie
'conditions' => array(
'Post.id = PostTagLink.post_id'
)
and
'conditions' => array(
'Tag.id = PostTagLink.tag_id'
)
See also
Cookbook > Models > Associations: Linking Models Together > Joining tables
I am using CakePHP 2.5. I am having following table
CompanyMaster:
company_master_id [PK]
Name and other columns
CompanySignatoryDetails: (has many owners for single company)
company_signatory_details_id [PK]
company_master_id [FK]
Name and other columns
Now, I want to get company details with all owners of that company. Here is what I have tried.
$this->CompanyMaster->bindModel(
array(
'hasMany' => array(
'CompanySignatoryDetails' => array(
'className' => 'CompanySignatoryDetails',
'foreignKey' => false,
'conditions' => array(
'CompanySignatoryDetails.company_master_id = CompanyMaster.company_master_id'
),
),
)
)
);
$this->CompanyMaster->recursive = 2;
$company = $this->CompanyMaster->find('first', array(
'fields' => array('CompanyMaster.*'),
'conditions' => $conditions, //company id in condition
));
I am getting following error:
Database Error
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'CompanyMaster.id' in 'field list'
SQL Query:
SELECT `CompanyMaster`.*, `CompanyMaster`.`id` FROM `crawler_output`.`company_master` AS `CompanyMaster` WHERE `CompanyMaster`.`company_master_id` = 1 LIMIT 1
Please let me know how can I bind model without id as column name.
CakePHP will produce a separate query when dealing with hasMany relationships, and therefore you won't be able to reference a field from another table. Only belongsTo and hasOne relationships produce a JOIN.
However, you don't need to add conditions to the relationship. The following should just work fine:
$this->CompanyMaster->bindModel(array(
'hasMany' => array(
'CompanySignatoryDetails' => array(
'className' => 'CompanySignatoryDetails',
'foreignKey' => 'company_master_id',
),
)
));
Don't forget to define your primary keys for CompanyMaster:
class CompanyMaster extends AppModel
{
public $primaryKey = 'company_master_id';
}
and for CompanySignatoryDetails:
class CompanySignatoryDetails extends AppModel
{
public $primaryKey = 'company_signatory_details_id';
}
Well, for instance, let your query looks like this:
select CompanyMaster.*,CompanySignatoryDetails.* from
CompanyMaster as cm inner join CompanySignatoryDetails as cd on
cm.company_master_id=cd.company_master_id
order by cm.company_master_id;
You will get all fields from two tables, ordered by company_master_id field. You may reduce number of fields, displayed by this query, by explicitly designate them like this:
select cm.company_master_id, cd.name from....
HNY!(Happy New Year!!)
How do I convert this MySql query to CakePhp's find.
And please tell me how can i practice writing Find queries in cakephp
select distinct trips.fk_userid from spots, trips
where spots.fk_tripid = trips.id
and trips.isapproved = 1
and spots.id in (".$row[$first_index]['spot_list'].")
The model can be Trip and you can query like this
$this->Trip->query("select distinct trips.fk_userid from spots, trips where spots.fk_tripid = trips.id and trips.isapproved = 1 and spots.id in (".$row[$first_index]['spot_list'].")");
or
You should create Trip and Spot model and in Trip model, you have to have this in Spot model:
public $belongsTo = array(
'Trip' => array(
'className' => 'Trip',
'foreignKey' => 'fk_tripid'
)
);
and query it like this:
$this->Spot->find('all', array(
'fields' => array("distinct Trip.fk_userid"),
'conditions' => array(
'Trip.isapproved' => 1,
'Spot.id' => $row[$first_index]['spot_list']
)
));
I have a User model and a Message model.
The Message model is linked to the User model twice like this:
public $belongsTo = array(
'UserSender' => array(
'className' => 'User',
'foreignKey' => 'sender_id',
'counterCache' => array(
'messages_sent_count' => array(
'is_deleted' => FALSE
)
)
),
'UserRecipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id',
'counterCache' => array(
'messages_received_count' => array(
'is_deleted' => FALSE
),
'messages_unread_count' => array(
'is_deleted' => FALSE,
'is_read' => FALSE
)
)
),
'Operator' => array(
'className' => 'Operator',
'foreignKey' => 'operator_id'
)
);
Besides the User model, the Message model also $belongsTo the Operator model. The Operator model is irrelevant to the message count for the users, but its table is still being joined in the count query, as debug shows:
'query' => 'SELECT COUNT(*) AS `count` FROM `database`.`messages` AS `Message` LEFT JOIN `database`.`operators` AS `Operator` ON (`Message`.`operator_id` = `Operator`.`id`) LEFT JOIN `database`.`users` AS `UserSender` ON (`Message`.`sender_id` = `UserSender`.`id`) LEFT JOIN `database`.`users` AS `UserRecipient` ON (`Message`.`recipient_id` = `UserRecipient`.`id`) WHERE `Message`.`is_deleted` = '0' AND `Message`.`sender_id` = 389',
'params' => array(),
'affected' => (int) 1,
'numRows' => (int) 1,
'took' => (float) 394
For the sake of simplicity I've actually excluded one more model that the Message model $belongsTo, but the above query shows the problem.
The counterCache function does a quite expensive query just to update the counter. Is there a way to maybe override or adjust the counterCache method to not join irrelevant tables in the query?
I can't test it right now, but since the recursive setting used by Model::updateCounterCache() is hard-coded based on whether conditions are defined for the counter cache field, the only way to change this (besides completely reimplementing Model::updateCounterCache()) is probably to modify the count query in Model::_findCount() or Model::beforeFind() of your Message model.
public function beforeFind($query) {
// ... figure whether this is the count query for updateCounterCache,
// maybe even try to analyze whether the passed conditions require
// joins or not.
if(/* ... */) {
$query['recursive'] = -1;
}
return $query;
}
Depending on how much control you'll actually need the containable behavior might do the trick too, it sets recursive to -1 in case no containments are being passed
$Message->contain(); // === recursive is being set to -1 in before find callback
$Message->delete(123);
I am retrieving data:
$mydata = $this->ProductList->find('all', array('order' => 'rand()', 'conditions' => array('name' => 'we love')));
I have set up a HABTM relationship to the Product model. As you can see, I am fetching all products in the 'we love'-list. Now, I want those Products I am retrieving to be randomised. But they are not, instead the MySQL is randomised on the ProductList model as you can see in the SQL. Why is that? How can I get the random fetch on the Products instead?
Resulting MySQL query:
SELECT `ProductList`.`id`, `ProductList`.`name` FROM `database`.`product_lists` AS `ProductList` WHERE `name` = 'we love' ORDER BY rand() ASC
SELECT `Product`.`id`, `Product`.`category_id`, `Product`.`name`, `Product`.`price`, `Product`.`description`, `ProductListsProduct`.`product_list_id`, `ProductListsProduct`.`product_id` FROM `database`.`products` AS `Product` JOIN `database`.`product_lists_products` AS `ProductListsProduct` ON (`ProductListsProduct`.`product_list_id` = 3 AND `ProductListsProduct`.`product_id` = `Product`.`id`)
EDIT:
There are so many different ways to approach this; to get a random product from a user's product list. You could do it with PHP - just find all of the products and then use rand() to pick on from the returned array. You could set a Model query condition. The list goes on...
I would probably create an alias to the Product model in ProductList called RandomProduct. You could set the query for the retrieved product when you set the relationship:
public $hasMany = array(
'RandomProduct' => array(
'className' => 'Product',
'foreignKey' => 'product_list_id',
'order' => 'Rand()',
'limit' => '1',
'dependent' => true
)
);
You can then use the containable behavior so that this model is only retrieved when you need it. (You wouldn't need to do this if recursive finds are greater than -1, but I usually do that as best practice so that my models only query for the data that they need.) The following would return any ProductList called 'we love' and a "random" product associated with that list.
$mydata = $this->ProductList->find(
'all',
array(
'conditions' => array(
'name' => 'we love'
)
),
'contain' => array(
'RandomProduct'
)
);