我需要一些CakePHP 2.2.3的帮助。
我现在有以下设置:
Post
hasMany Attachment
它工作正常,页面生成2个查询:
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)
我想将关系扩展如下:
Post
hasMany Attachment
;每个Attachment
属于 Type
我不太热衷于让CakePHP遵循它 基本上,我需要的是:
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)
注意添加了LEFT JOIN types
。
所以我在第二个查询中得到了相应的类型数据。我知道我可以循环获取数据或使用->query()
调用,但我希望它尽可能有效和灵活。
我尝试了Containable,Model Unbinding trick(and this one)但没有成功。我尝试了不同的选项组合,我相信我甚至删除了连接。这就是我PostsController
现在的样子。
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;
}
}
但它只是在循环中每次迭代运行一个额外的查询,并获得 all 附件:
SELECT `Attachment`.`id`, ...
FROM `attachments` AS `Attachment`
WHERE 1 = 1
当我取消注释这个关联的条件时,它会抛出SQLSTATE“Column Post.id not found error” - 我猜是因为这里没有Post表加入。
我需要一手设置。
请帮忙!感谢
更新
我按如下方式更改了控制器。请注意,没有bindModel
/ unbindModel
代码,关系是在模型类中设置的(在这种情况下是正确的吗?)。
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'));
}
}
这是Attachment
型号:
class Attachment extends AppModel {
public $belongsTo = array(
'Type' => array(
'className' => 'Type',
'foreignKey' => 'type_id',
),
'Post' => array(
'className' => 'Post',
'foreignKey' => 'post_id',
),
);
}
以上代码运行此查询:
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
这里的一切都是关于附件的。我的意思是帖子已加入附件,因此如果帖子没有附件,则不会返回。这可能是因为呼叫是Attachment->find()
所以它来自附件的观点。我想它应该是:
// ...
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`)
// ...
但它不会起作用,是吗?您会看到posts
,attachments
和types
,但它们确实具有不同的关系类型。最初,我发布了CakePHP运行的两个单独的查询 - 必须有原因。
UPDATE2
我仍然相信在初始设置中将第二个查询更改为Attachment
模式(请参阅我想要的内容部分)。所以我将获得附件类型以及附件本身。我的意思是在那种情况下,将types
表连接到attachments
并不会破坏任何数据库关系逻辑,是吗?
我只是想确保通过一个复杂但单find()
次呼叫无法做到这一点。
答案 0 :(得分:3)
每当Cake看到hasMany关系时,它都会自动创建多个查询以提取数据。在构造这些查询时,它会查找可以左键连接到它的关系(hasOne和belongsTo)。
由于Cake无法为您执行此操作,因此您需要自己合并它们。
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'));
}
这不是最有效的方法,因为你会多次遍历$attachments
数组,但我相信你明白了。
答案 1 :(得分:0)
在hasMany中尝试finderQuery。 例如: 在Post模型中,
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' => ''
)