I need some help with CakePHP 2.2.3.
What I have
I have the following setup at the moment:
Post hasMany Attachment
It works fine and the page is generated with 2 queries:
SELECT *, `Post`.`id`
FROM `posts` AS `Post`
WHERE 1 = 1
ORDER BY `Post`.`created` DESC
SELECT
`Attachment`.`id`,
`Attachment`.`post_id`,
`Attachment`.`created`
FROM
`attachments` AS `Attachment`
WHERE
`Attachment`.`post_id` IN (1, 2, 3, ..., n)
What I want
I want to extend the relation to be as follows:
Post hasMany Attachment; every Attachment belongsTo Type
And I don't know hot to make CakePHP follow it.
Basically, what I need is:
SELECT *, `Post`.`id`
FROM `posts` AS `Post`
WHERE 1 = 1
ORDER BY `Post`.`created` DESC
SELECT
`Attachment`.`id`,
`Attachment`.`post_id`,
`Attachment`.`created`,
`Type`.`title`, `Type`.`icon`
FROM
`attachments` AS `Attachment`
LEFT JOIN
`types` AS `Type`
ON (`Attachment`.`type_id`=`Type`.`id`)
WHERE
`Attachment`.`post_id` IN (1, 2, 3, ..., n)
Note the LEFT JOIN types added.
So I get the corresponding type data in the second query. I know I could get the data in a loop or using a ->query() call, but I want this to be as much effective and flexible as possible.
The problem
I tried the Containable, Model Unbinding trick (and this one) but no success. I tried different combinations of the options, I believe I've even removed joins. Here's what my PostsController looks like now.
class PostsController extends AppController {
public function index() {
$this->Post->unbindModel(array('hasMany' => array('Attachment')));
$this->Post->Attachment->unbindModel(array('belongsTo' => array('Type')));
$this->Post->bindModel(array(
'hasMany' => array(
'Attachment' => array(
'className' => 'Attachment',
// when uncommented, throws the "Unknown column Post.id" SQLSTATE error
// 'conditions' => array('Post.id' => 'Attachment.post_id'),
'foreignKey' => false,
),
),
));
$this->Post->Attachment->bindModel(array(
'belongsTo' => array(
'Filetype' => array(
'className' => 'Filetype',
// 'conditions' => array('Type.id' => 'Attachment.type_id'),
'foreignKey' => false,
),
),
));
$all = $this->Post->find('all', array(
'joins' => array(
array(
'table' => 'users',
'prefix' => '',
'alias' => 'User',
'type' => 'INNER',
'conditions' => array(
'User.id = Post.user_id',
)
),
),
'contain' => array('Attachment', 'Type'),
'conditions' => array(),
'fields' => array('*'),
'order' => 'Post.created ASC'
));
var_dump($all);exit;
}
}
But it just runs an extra query per each iteration in a loop and gets all the attachments:
SELECT `Attachment`.`id`, ...
FROM `attachments` AS `Attachment`
WHERE 1 = 1
When I uncomment the condition for this association, it throws me the SQLSTATE "Column Post.id not found error" - I guess because there's no Post table joined here.
I need a hand in setting this up.
Please help! Thanks
UPDATE
I've changed the controller as follows. Please note there's no bindModel/unbindModel code, the relation is set in the models classes (is that correct in this case?).
class PostsController extends AppController {
public function index() {
$options = array(
'contain' => array(
'Post',
'Type'
),
'order' => 'Post.created DESC',
'conditions' => array(
// 'Post.title LIKE' => 'my post'
)
);
// The following throws "Fatal error: Call to a member function find() on a non-object"
// $posts = $this->Attachment->find('all', $options);
// So I had to use $this->Post->Attachment instead of $this->Attachment
$posts = $this->Post->Attachment->find('all', $options);
$this->set(compact('posts'));
}
}
This is the Attachment model:
class Attachment extends AppModel {
public $belongsTo = array(
'Type' => array(
'className' => 'Type',
'foreignKey' => 'type_id',
),
'Post' => array(
'className' => 'Post',
'foreignKey' => 'post_id',
),
);
}
The above code runs this query:
SELECT
`Attachment`.`id`, `Attachment`.`type_id`, `Attachment`.`post_id`, `Attachment`.`created`,
`Type`.`id`, `Type`.`title`,
`Post`.`id`, `Post`.`text`, `Post`.`created`
FROM
`attachments` AS `Attachment`
LEFT JOIN `types` AS `Type` ON (`Attachment`.`type_id` = `Type`.`id`)
LEFT JOIN `posts` AS `Post` ON (`Attachment`.`post_id` = `Post`.`id`)
WHERE
1 = 1
ORDER BY
`Post`.`created` ASC
Everything is about the attachments here. I mean the posts are joined to attachments, so if the post has no attachments, it's not returned. This is probably because the call is Attachment->find() so it's from the attachment's point of view. I guess it just should be:
// ...
FROM
`posts` AS `Post`
LEFT JOIN `attachments` AS `Attachment` ON (`Attachment`.`post_id` = `Post`.`id`)
LEFT JOIN `types` AS `Type` ON (`Attachment`.`type_id` = `Type`.`id`)
// ...
But it's not going to work, is it? You see there are posts, attachments and types, but they do have the different relation types. Originally, I've posted those two separate queries CakePHP runs - there must be reasons for that.
UPDATE2
I still believe that it's all about changing the second query to the Attachment model in the initial setup (please see the What I Want section). So I will get attachments types along with attachments themselves. I mean in that case LEFT JOINing the types table to attachments is not going to break any database relation logic, is it?
I just want to make sure there's no way to do that with one complex, but single find() call.
Whenever Cake sees a hasMany relationship, it will automatically create multiple queries to pull the data. While constructing those queries, it looks for relationships that can be LEFT joined to it (hasOne and belongsTo).
Since Cake can't do this for you, you will need to merge them yourself.
public function index() {
$posts = $this->Post->find('all');
// get all attachments for all found posts
$attachments = $this->Post->Attachment->find('all', array(
'contain' => array('Type'),
'conditions' => array('Post.id' => Set::extract('/Post/id', $posts)
));
// now join them to the posts array
foreach ($posts as $key => $data) {
$postId = $data['Post']['id'];
// append any data related to this post to the post's array
$posts[$key] += Set::extract("/Attachment[post_id=$postId]/..", $attachments);
}
$this->set(compact('posts'));
}
This is not the most efficient way to do it since you'll iterate through the $attachments array multiple times, but I'm sure you get the idea.
Try the finderQuery in hasMany.
Eg:
In the Post model,
public $hasMany = array(
'Attachment' => array(
'className' => 'Attachment',
'foreignKey' => 'post_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '
SELECT
`Attachment`.`id`,
`Attachment`.`post_id`,
`Attachment`.`created`,
`Type`.`title`,
`Type`.`icon`
FROM
`attachments` AS `Attachment`
LEFT JOIN
`types` AS `Type`
ON (`Attachment`.`type_id`=`Type`.`id`)
WHERE
`Attachment`.`post_id` IN (1, 2, 3, ..., n)
',
'counterQuery' => ''
)
Related
I am trying to replicate this SQL query result which works:
SELECT r.id, r.day, s.ddbroute_id, s.delivery_id, d.id, d.laststatusid, t.id, t.delivery_id, t.statusstage_id, st.id, st.stage
FROM ddbroutes r
LEFT JOIN ddbrouteslots s on r.id = s.ddbroute_id
LEFT JOIN deliveries d on s.delivery_id = d.id
LEFT JOIN trackingstatuses t on d.laststatusid = t.id
LEFT JOIN statusstages st on t.statusstage_id = st.id
I am using CakePHP 2 Models with
bindModel to change Model associations on the fly
custom Find put the logic in a Model
There is no common field from the bottom table beyond the second level.
The error message is: 'Model "Ddbroute" is not associated with model "Delivery".' I therefore tried it with and without Delivery in the 'contain' array, and neither way brought in the Delivery fields.
I would be happy to use joins if appropriate. I have read most relevant posts on StackOverflow that I could find.
My code with further information is below. Any help gratefully received.
I have five tables (including the following fields):
ddbroutes (id, day)
ddbrouteslots (id, ddbroute_id, delivery_id)
deliveries (id, laststatusid)
trackingstatuses (id, statusstage_id)
statusstages (id, stage)
There are the following relationships set up in the Models:
Ddbroute hasMany Ddbrouteslot (Ddbrouteslot belongsTo Ddbroute)
Delivery hasOne Ddbrouteslot (Ddbrouteslot belongsTo Delivery)
Delivery hasMany Trackingstatus (Trackingstatus belongsTo Delivery)
Statusstage hasMany Trackingstatus (Trackingstatus belongsTo Statusstage)
Though Delivery hasOne Ddbrouteslot (and this will be hasMany - revised - this is now staying as hasOne), for any individual Ddbroute, there is only one Delivery associated which each Ddbrouteslot. Containable is set up in all the Models. I didn't know if I needed to use unbindModel first (it didn't change the error message).
My code in Ddbroute.php Model file (only as far as the Delivery table)
public $findMethods = array('ddbstatuses' => true);
protected function _findDdbstatuses($state, $query, $results = array()) {
if ($state === 'before') {
$ddbrouteslotmodel = ClassRegistry::init('Ddbrouteslot');
$ddbrouteslotmodel->unbindModel(
array('belongsTo' => array('Delivery'))
);
$ddbrouteslotmodel->bindModel(
array('hasOne' => array(
'Delivery' => array(
'className' => 'Delivery',
'foreignKey' => 'id',
'dependent' => false,
'fields' => array(
'id', 'laststatusid'
)
)
))
);
$deliverymodel = ClassRegistry::init('Delivery');
$deliverymodel->unbindModel(
array('hasOne' => array('Ddbrouteslot'))
);
$deliverymodel->bindModel(
array('belongsTo' => array(
'Delivery' => array(
'className' => 'Delivery',
'foreignKey' => 'delivery_id'
)
)
)
);
$query['contain'] = array(
'Ddbrouteslot', 'Delivery'
);
return $query;
}
return $results;
}
In another controller, to run the find operation:
$this->LoadModel('Ddbroute');
$ddbstatuses = $this->Ddbroute->find('ddbstatuses');
$this->set(compact('ddbstatuses')); // to make available in a view
I also had a further attempt with a long join array, but the query did not bring in any Delivery, Trackingstatus or Statusstage information, though the query seems to have run.
public $findMethods = array('ddbstatuses' => true);
protected function _findDdbstatuses($state, $query, $results = array()) {
if ($state === 'before') {
ClassRegistry::init('Delivery'); // not sure these three lines were needed so I tried with and without them
ClassRegistry::init('Trackingstatus');
ClassRegistry::init('Statusstage');
$query['joins'] = array(
array(
'table' => 'ddbrouteslots',
'alias' => 'Ddbrouteslot',
'type' => 'LEFT',
'conditions' => array(
'Ddbroute.id = Ddbrouteslot.ddbroute_id'
)),
array(
'table' => 'deliveries',
'alias' => 'Delivery',
'type' => 'LEFT',
'conditions' => array(
'Ddbrouteslot.id = Delivery.id'
)),
array(
'table' => 'trackingstatuses',
'alias' => 'Trackingstatus',
'type' => 'LEFT',
'conditions' => array(
'Delivery.laststatusid = Trackingstatus.id'
)),
array(
'table' => 'statusstages',
'alias' => 'Statusstage',
'type' => 'LEFT',
'conditions' => array(
'Trackingstatus.statusstage_id = Statusstage.id'
))
);
$query['contain'] = array(
'Ddbrouteslot',
'Delivery', // Not sure I should be adding these other models, so I tried with and without them
'Trackingstatus',
'Statusstage'
);
return $query;
}
return $results;
}
After some help, I now have four solutions to get my data, though really three of them are variants on the first one. I am relatively inexperienced and there were some basic things I didn't appreciate.
1. IN A CONTROLLER
$this->LoadModel("Ddbrouteslot");
$res = $this->Ddbrouteslot->find("all", array(
"conditions" => array(
"Ddbrouteslot.delivery_id > 0",
"Ddbrouteslot.ddbroute_id" => 45
),
"contain" => array(
"Ddbroute",
"Delivery" => array(
"Trackingstatus" => array(
"order" => array(
"Trackingstatus.id" => "desc"
),
"limit" => 1,
"Statusstage"
)
)
)
);
Timings from DebugKit: main query was 20ms; Trackingstatus and Statusstage were additional queries of 18ms each x 4 for four associated deliveries; total time was 164ms. This is quite slow which is not ideal.
This started from the second model, Ddbrouteslot, because this had direct relationships with both Ddbroute and Delivery. There were no changes to any of the associations.
The belongsTo relationship from Ddbrouteslot to Delivery worked fine.
There was already a hasMany relationship between Delivery and Trackingstatus on delivery_id.
2. USING SQL
$this->LoadModel("Ddbroute");
$qres = $this->Ddbroute->query(
"SELECT *
FROM
ddbroutes AS r
LEFT JOIN ddbrouteslots s on r.id = s.ddbroute_id
LEFT JOIN deliveries d on s.delivery_id = d.id
LEFT JOIN trackingstatuses t on d.laststatusid = t.id
LEFT JOIN statusstages st on t.statusstage_id = st.id
WHERE s.delivery_id > 0 AND s.ddbroute_id = 45
;"
debug($qres);
Timings: this took 19ms. This means it was much faster. This is not recommended in the Cake documentation, and clearly it is not as portable between databases as a pure Cake find.
3. CHANGING THE BASE MODEL
$rres = $this->Ddbroute->find("all", array(
"conditions" => array(
"Ddbroute.id" => 45
),
"recursive" => -1,
"contain" => array(
"Ddbrouteslot" => array(
"conditions" => array(
"Ddbrouteslot.delivery_id > 0"
),
"Delivery" => array(
"Trackingstatus" => array(
"order" => array(
"Trackingstatus.id" => "desc"
),
"limit" => 1,
"Statusstage"
)
)
)
)
));
debug($rres);
Timings: Main query was 18ms; Delivery, Trackingstatus and Statusstage were 18ms each x 4 for four associated deliveries; total time was 234ms. It was slower because Delivery needed to be run for each despatch because it was not within the model of Ddbroute.
Changing recursive didn't make a difference.
4. USING A CUSTOM FIND
This was the same query as 1.) above, but just with a custom find method.
public $findMethods = array('ddbstatuses' => true);
protected function _findDdbstatuses($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions'] = array(
"Ddbrouteslot.delivery_id > 0",
"Ddbrouteslot.ddbroute_id" => 45
);
$query['contain'] = array(
"Ddbroute",
"Delivery"=> array(
"Trackingstatus" => array(
"order" => array(
"Trackingstatus.id" => "desc"
),
"limit" => 1,
"Statusstage"
)
)
);
return $query;
}
return $results;
}
In my GridView I want to display all records of my table 'reply'. The table has relations to the tables 'author' and 'task' and not every reply has a task.
The table 'task' has a relation with another table called 'concern'.
Here ist the relations() of my model Reply:
public function relations() {
return array(
'trak' => array(self::HAS_MANY, 'Task', 'reply_id', 'condition' => 'task.deleted<>1'),
'author' => array(self::BELONGS_TO, 'Author', 'author_id'),
);
}
The search() method of my model Reply has the following code:
public function search() {
$criteria = new CDbCriteria;
$criteria->with = array(
'author' => array('select' => 'id, name, role, office_id, abk', 'together' => false),
'author.office' => array('select' => 'id, name'),
'task' => array('select' => 'id, concern_id', 'together' => true),
'task.concern' => array('select' => 'id, classification_id', 'alias' => 'concern'),
);
$criteria->compare('t.id', $this->id);
$criteria->compare('t.create_time', $this->create_time);
$criteria->compare('t.create_date', $this->create_date, true);
$criteria->compare('t.office.id', $this->search_office);
$criteria->compare('t.author_id', $this->author_id);
$criteria->compare('t.rel', $this->rel, true);
$criteria->compare('t.author_id', $this->author_id);
$criteria->compare('t.lektor_id', $this->lektor_id);
$criteria->compare('t.issue_id', $this->issue_id);
$criteria->compare('t.reply_text', $this->reply_text, true);
$criteria->compare('t.deleted', $this->deleted);
if (EWMParam::getValue(EWMParam::MODUL_SCHLAGWORTE))
$criteria->compare('t.tags', $this->tags, true);
$criteria->compare('t.text_name', $this->text_name, true);
$criteria->compare('t.use_count', $this->use_count);
$criteria->compare('concern.classification_id', $this->classification_id);
$criteria->compare('t.update_time', $this->update_time);
$criteria->compare('t.update_user', $this->update_user);
$criteria->compare('t.global', $this->global);
if (EWMParam::getValue(EWMParam::MODUL_confirmationN))
$criteria->compare('t.confirmation', $this->confirmation);
$criteria->compare('t.confirmation_text', $this->confirmation_text, true);
$criteria->compare('t.use', $this->use, true);
$pagination = EWMPageSortFilterHelper::getPagination($this);
$sort = new CSort();
$sort->defaultOrder = 't.id DESC';
$sort->attributes = array(
'global' => 't.global',
'search_office' => 'office.name',
'id' => 't.id',
'text_name' => 't.text_name',
'confirmation' => 't.confirmation',
'author_id' => 'author.name',
'create_date' => 't.create_date',
'tags' => 't.tags',
'use' => 't.use',
'classification_id' => 'classification_id',
);
$sort->applyOrder($criteria);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'pagination' => $pagination,
'sort' => $sort
));
}
In my GridVew only replys with a task are displayed and all filters work fine. But I want to display all replys with a task and all replys without a task. If I
delete in the search() method in the array for $criteria->with the elements 'task' and 'task.concern' all replys are displyed. But the filter for the row
'Classifcation' which comes from the relation 'task.concern' doesn't work. Logically I get the error "Column not found: 1054 Unknown column 'concern.classification_id' in 'where clause'".
Is it possible to display all replys and to filter those replys by the classification? Do you have an idea?
You should look into how Yii relations work.
One thing that comes into my mind is that when adding the relations in the with() property the SQL generated might include INNER JOINs.
That's why when having the relations included you're not getting any reply without tasks.
In order to fix that you should make sure that the SQL generated is using LEFT JOINs.
You can do that by using joinType property of a relation:
http://www.yiiframework.com/doc/api/1.1/CActiveRelation#joinType-detail
And you could specify LEFT JOIN there.
'task' => array('select' => 'id, concern_id', 'together' => true, 'joinType'=>'LEFT JOIN'),
'task.concern' => array('select' => 'id, classification_id', 'alias' => 'concern', 'together' => true, 'joinType'=>'LEFT JOIN'),
That might do the trick. But maybe you need some more tweaking if it is not working just as expected.
If you want to debug what is actually happening, you could put a bad field/table name inside the criteria that would result in a database error and then you can look in the SQL code that was executed and see how tables are joined.
Suggested links:
http://www.yiiframework.com/doc/guide/1.1/en/database.arr
http://www.yiiframework.com/wiki/527/relational-query-lazy-loading-and-eager-loading-with-and-together/
http://www.yiiframework.com/wiki/428/drills-search-by-a-has_many-relation
I have three Models Quotation, QuotationItem and Job.
My main Objective is to do a left join for tables of Quotation and QuotationItem in the Job controller. I cannot achieve it because instead of QuotationItem ,the job table is being used in left join!
$this->Job->unbindModel(
array('belongsTo' => array('Quotation','QuotationItem')), true
);
$options = array(
'fields' => array(
'QuotationItem.id',
'QuotationItem.Quot_id',
'QuotationItem.item_sno',
'QuotationItem.job_desc',
'QuotationItem.selected_qty',
'QuotationItem.paper_id',
'QuotationItem.plate_id',
'QuotationItem.design_id',
'QuotationItem.ink_id',
'QuotationItem.misel_id',
'QuotationItem.plate_size',
'QuotationItem.paper_size',
'QuotationItem.paper_type',
'QuotationItem.paper_gsm',
'QuotationItem.plate_qty',
'QuotationItem.paper_qty',
'QuotationItem.ink_qty',
'QuotationItem.plate_color',
'QuotationItem.ink_color',
'QuotationItem.ink_code',
'QuotationItem.plate_price',
'QuotationItem.paper_price',
'QuotationItem.ink_price',
'QuotationItem.design_price',
'QuotationItem.plate_total',
'QuotationItem.paper_total',
'QuotationItem.ink_total',
'QuotationItem.design_total',
'QuotationItem.printing_cost',
'QuotationItem.prepress_cost',
'QuotationItem.design_cost',
'QuotationItem.press_cost',
'QuotationItem.folding_cost',
'QuotationItem.binding_cost',
'QuotationItem.block_cost',
'QuotationItem.lamination_cost',
'QuotationItem.uv_cost',
'QuotationItem.stamping_cost',
'QuotationItem.diecutting_cost',
'QuotationItem.sewing_cost',
'QuotationItem.perfectbind_cost',
'QuotationItem.saddlestitch_cost',
'QuotationItem.emboss_cost',
'QuotationItem.cutting_cost',
'QuotationItem.labor_charges',
'QuotationItem.others',
'QuotationItem.cost_total',
'QuotationItem.total_item_sum',
'QuotationItem.net_total',
'QuotationItem.created',
'QuotationItem.modified',
'QuotationItem.status',
'Quotation.Quot_id',
'Quotation.job_item',
'Quotation.customer_id',
'Quotation.customer_name',
'Quotation.customer_phone',
'Quotation.customer_email',
'Quotation.customer_address',
'Quotation.salesperson',
'Quotation.pay_terms',
'Quotation.contact_id',
'Quotation.status',
'Quotation.discount',
'Quotation.total',
'Quotation.created',
'Quotation.modified',
),
'joins' => array(
array(
'table' => 'quotation',
'alias' => 'Quotation',
'type' => 'left',
'conditions' => array('QuotationItem.Quot_id = Quotation.Quot_id'),
)
),
'conditions' => array(
'1',
));$data = $this->Job->find('all', $options);`
You need to run the find on Quotation, not on Job. Models are tied to a table, think of them as one-in-the-same. You can't find something from a different table that has nothing to do with the Model you're executing the find function on.
If you're getting sql 42000 error. Table/alias not unique when loading a model, it's either already been loaded or you're trying to alias something with a name that's already been used for something else - which you can't do.
The simplest way to ensure you have access to all 3 Models from the Job Controller if you haven't grasped model associations well yet is to declare public $uses = array('Job','Quotation','QuotationItem'); in your Job Controller.
Whatever way you go about getting access to Quotation aside, what you want to do is then:
$this->Quotation->find('all',array(
'fields' => '*',
'joins' => array(
array(
'table' => 'quotation',
'alias' => 'Quotation',
'type' => 'left',
'conditions' => array(
'QuotationItem.Quot_id = Quotation.Quot_id'
)
)
)
));
Thank you #Tim for making me understand about model and controller. I have found the answer to my Question.
I used the join in my QuotationItem model since the QuotationItem belongs to Quotation .
public $belongsTo = array(
'Quotation' => array(
'className' => 'Quotation',
'foreignKey' => 'Quot_id',
'type'=>'left',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Then I declared public $uses = array('Job','Quotation','QuotationItem'); in my jobs controller
after that in my add function I used
$quotationItem=$this->QuotationItem->find('all');
$this->set(compact( 'quotationItem'));
Finally I got my joins working
I've got three tables
posts
tags
posts_tags
I need to write some controller logic that selects and sets all posts that have the tag work
This means querying the tags to find the id for the queried tag, checking in the associated posts_tags table to find matches, using the post_id to return the correct posts from the posts table.
I'm not sure how to even begin this query, I'm new to CakePHP and could use a hand... Please?
If it helps, here's my posts model relationship:
var $hasAndBelongsToMany = array(
'Tag' => array(
'className' => 'Tag',
'joinTable' => 'posts_tags',
'foreignKey' => 'posts_id',
'associationForeignKey' => 'tag_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
My Tag table is made up of two fields: id and tag
My PostsTag table is made up of three fields: id, tag_id and post_id
My Posts table is made up of four fields: id, title, body and created
I found some code on the CakePHP Book that shows the below code:
$this->Recipe->Tag->find('all', array('conditions'=>array('Tag.name'=>'Dessert')));
They suggested that it was a similar idea, so I attempted to adapt:
$this->Post->Tag->find('all', array('conditions'=>array('Tag.tag'=>'work')));
However, this has not worked. This returns all Posts without filtering.
I took the advice of #Leo and tried to adapt his code to mine:
function getArticleByTagSql($tag) {
$dbo = $this->getDataSource();
$subQuery = $dbo->buildStatement(
array(
'fields' => array('DISTINCT(ArticlesTag.article_id)'),
'table' => "articles_tags",
'joins' => array(
array('table' => 'tags',
'alias' => 'Tag',
'type' => 'INNER',
'conditions' => array('ArticlesTag.tag_id = Tag.id')
)
),
'alias' => "ArticlesTag",
'conditions' => array("Tag.tag" => $tag),
'order' => null,
'group' => "ArticlesTag.article_id",
'limit' => null
),
$this
);
$subQuery = ' Article.id IN (' . $subQuery . ')';
return $dbo->expression($subQuery);
}
Controller:
$this->set('articles', $this->paginate(array(
'conditions' => $this->Article->getArticleByTagSql('work')
)));
However, whatever I type in the paginate() method as a key - in this case 'conditions' appears in the query and I can't figure out why - I keep getting unknown column 'conditions' errors. The error is with the paginate function, the data returns correctly without it, but I cannot use the paginator without it. It's like a catch 22
Cheers,
Dan
if you use:
$this->Post->Tag->find('all'...
you're telling him to find all Tags work... and since its recursive, each tag will contain the related posts..
try doing this instead:
$this->Post->find('all'...
hopefully cake will be smart enough (and if you have correctly set your models relations) to only select posts that have the "work" tag.. and if that doesnt work, you could always set the joins "manually" using a complex find condition
Good Luck
Daniel this question is actually answered in the Cake documentation when describing the HABTM relationship betweens Receipe/Tag
I answered this question for someone who had a similar issue with an Article/Tag models, that answer used a subquery
I had same problem, but I tried this query and it worked for me...May be it will work for you too :)
$claims = $this->Claim->query("SELECT DISTINCT Claim.id,Claim.title, Claim.description FROM
claims as Claim
LEFT JOIN claim_tags as ClaimTag ON Claim.id = ClaimTag.claim_id
LEFT JOIN tags as Tag ON Tag.id =ClaimTag.tag_id
WHERE Tag.id = '$id'");
I have been fighting with this code:
function getNextActionFObyBalance($when) {
$theQuery = $this->find('first', array(
'fields' => array(
'Contract.id',
'Contract.start_balance'
),
'conditions' => array(
'AND' => array(
'Status.next_action_by' => 'frontoffice',
'Status.status_type' => 'active',
'Status.visibility' => 'frontoffice',
'OR' => array(
'Contract.next_action_on' => null,
'Contract.next_action_on <=' => $when
)
)),
'order' => 'Contract.start_balance DESC',
'recursive' => 0,
));
return $theQuery;
}
I have enabled logging on the MySQL server at this is what the server indicates that CakePHP is requesting:
SELECT `Contract`.`id`, `Contract`.`start_balance` FROM `contracts` AS `Contract` LEFT JOIN `statuses` AS `Status` ON (`Contract`.`status_id` = `Status`.`id`) LEFT JOIN `users` AS `User` ON (`Contract`.`user_id` = `User`.`id`) WHERE ((`Status`.`next_action_by` = 'frontoffice') AND (`Status`.`status_type` = 'active') AND (`Status`.`visibility` = 'frontoffice') AND (((`Contract`.`next_action_on` IS NULL) OR (`Contract`.`next_action_on` <= '2010-09-13 10:13:04')))) ORDER BY `Contract`.`start_balance` DESC LIMIT 1
if I use that in the phpmyadmin tool, I get exactly what I was expecting 1 record with two fields. BUT CakePHP just gives me an empty result set.
Can anyone enlighten me?
PS the code was working but I can figure out what changed!
The problem was with a stub to do some post processing afterFind. The problem is that I have completely forgotten to return $results;
I found the error by doing a step by step debugging down the find method in model.php. Found that the after find was called at some point and went to check my afterFind.
Took my about 4 hours for a simple error but I am learning!
Presumably this method is defined in models/contract.php?
The recursive = 0 statement looks a bit suspect to me. Are the models correctly related in their respective model files?
Have you tried loadModel in case the associations aren't working properly?
It would be useful to see the relationship definitions from the respective models.
--EDIT--
I've formatted the code from your comment here as I can't edit your OP
var $belongsTo = array(
'Status' => array(
'className' => 'Status',
'foreignKey' => 'status_id',
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
)
);
var $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'contract_id',
'dependent' => false,
)
);