在CakePHP2中的多态性关联 [英] Polymorphic associations in CakePHP2
问题描述
我有3个模型,页
,课程
和内容
页面
和课程
包含元数据内容
包含HTML内容。
页
和课程
两个都有内容
belongsTo Page
和课程
为了避免在内容中包含
page_id
和 course_id
(因为我想要这种扩展到超过2个模型)我正在使用多态关联。我开始使用面包店中的多态行为,但它也生成waaay许多SQL查询我的喜好,它也抛出一个非法偏移错误,我不知道如何修复(它是在2008年写的,没有人似乎已经提到它最近,所以也许这个错误是由于它没有是为蛋糕2设计的吗?)
无论如何,我发现几乎可以通过硬编码模型中的关联来完成我需要的一切:
页模型
CREATE TABLE`pages`
`id` int(11)NOT NULL AUTO_INCREMENT,
`title` varchar(255)COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(255)COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY(`id`)
)
<?php
class Page extends AppModel {
var $ name ='Page';
var $ hasMany = array(
'Content'=> array(
'className'=>'Content',
'foreignKey'=>'foreign_id',
'conditions'=> array('Content.class'=>'Page'),
)
);
}
?>
课程模式
CREATE TABLE`courses`(
`id` int(11)NOT NULL AUTO_INCREMENT,
`title` varchar(255)COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(255)COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY(`id`)
)
<?php
类课程扩展AppModel {
var $ name ='Course';
var $ hasMany = array(
'Content'=> array(
'className'=>'Content',
'foreignKey'=>'foreign_id',
'conditions'=> array('Content.class'=>'Course'),
)
);
}
?>
内容模型
CREATE TABLE IF NOT EXISTS`contents`(
`id` int(11)unsigned NOT NULL AUTO_INCREMENT,
`class` varchar(30)COLLATE utf8_unicode_ci NOT NULL,
`foreign_id` int(11)unsigned NOT NULL,
`title` varchar(100)COLLATE utf8_unicode_ci NOT NULL,
`content` text COLLATE utf8_unicode_ci NOT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY(`id`)
)
<?php
class Content extends AppModel {
var $ name ='Content';
var $ belongsTo = array(
'Page'=> array(
'foreignKey'=>'foreign_id',
'conditions'=> array .class'=>'Page')
),
'Course'=> array(
'foreignKey'=>'foreign_id',
'conditions' > array('Content.class'=>'Course')
)
);
}
?>
好处是 $ this-> Content-> find ('first')
只生成单个SQL查询而不是3(与多态行为的情况相同),但是问题是返回的数据集包含两个belongsTo模型,真的返回存在的那个。下面是返回数据的样子:
数组(
'Content'=& id'=>'1',
'class'=>'Course',
'foreign_id'=>'1',
'title'=> this course',
'content'=>'这里的内容',
'created'=> null,
'modified'=> null
),
'Page'=> array(
'id'=> null,
'title'=> null,
'slug'=> null,
'created'=> null,
'updated'=> null
),
'Course'=> array(
'id'=>'1 ',
'title'=>'课程名称',
'slug'=>'课程名称',
'created'=> 10-11 00:00:00',
'updated'=>'2012-10-11 00:00:00'
)
)
我只想让它返回页
或 c> > Page
和课程
模型似乎是这个问题的明显解决方案,但我上面显示的模式只是为了这个问题的目的。
UPDATE 2 (更新2)
以下是运行 $ this-> Content-> find('first');
:
SELECT`Content`.`id`,`Content`.`class`,`Content`.`foreign_id `,`Content`.`title`,
`Content`.`slug`,`Content`.`content`,`Content`.`created`,`Content`.`modified`,
`Page`.`id`,`Page`.`title`,`Page```slug`,`Page`.`created`,`Page`.`updated`,
`Course`.`id `,`Course`.`title`,`Course`.`slug`,`Course`.`created`,
`Course`.`updated` FROM`cakedb`.`contents` AS`Content`
LEFT JOIN`cakedb`.`pages` AS`Page` ON
(`Content`.`foreign_id` =`Page`.`id`和`Content`.`class` ='Page')
LEFT JOIN`cakedb`.`courses` AS`Course` ON(`Content`.`foreign_id`=`Course`.`id`
AND`Content`.`class` ='Course' )WHERE 1 = 1 LIMIT 1
。只需在查找后过滤空值:
public function afterFind($ results,$ primary = false){
return Set :: filter($ results,true);
}
查看此问题。
条件
给您查找查询
Page.id
空 课程
不为空。
I have 3 models, Page
, Course
and Content
Page
and Course
contain meta data and Content
contains HTML content.
Page
and Course
both hasMany Content
Content
belongsTo Page
and Course
To avoid having page_id
and course_id
fields in Content
(because I want this to scale to more than just 2 models) I am looking at using Polymorphic Associations. I started by using the Polymorphic Behavior in the Bakery but it is generating waaay too many SQL queries for my liking and it's also throwing an "Illegal Offset" error which I don't know how to fix (it was written in 2008 and nobody seems to have referred to it recently so perhaps the error is due to it not having been designed for Cake 2?)
Anyway, I've found that I can almost do everything I need by hardcoding the associations in the models as such:
Page Model
CREATE TABLE `pages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
)
<?php
class Page extends AppModel {
var $name = 'Page';
var $hasMany = array(
'Content' => array(
'className' => 'Content',
'foreignKey' => 'foreign_id',
'conditions' => array('Content.class' => 'Page'),
)
);
}
?>
Course Model
CREATE TABLE `courses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
)
<?php
class Course extends AppModel {
var $name = 'Course';
var $hasMany = array(
'Content' => array(
'className' => 'Content',
'foreignKey' => 'foreign_id',
'conditions' => array('Content.class' => 'Course'),
)
);
}
?>
Content model
CREATE TABLE IF NOT EXISTS `contents` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`class` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
`foreign_id` int(11) unsigned NOT NULL,
`title` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`content` text COLLATE utf8_unicode_ci NOT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
)
<?php
class Content extends AppModel {
var $name = 'Content';
var $belongsTo = array(
'Page' => array(
'foreignKey' => 'foreign_id',
'conditions' => array('Content.class' => 'Page')
),
'Course' => array(
'foreignKey' => 'foreign_id',
'conditions' => array('Content.class' => 'Course')
)
);
}
?>
The good thing is that $this->Content->find('first')
only generates a single SQL query instead of 3 (as was the case with the Polymorphic Behavior) but the problem is that the dataset returned includes both of the belongsTo models, whereas it should only really return the one that exists. Here's how the returned data looks:
array(
'Content' => array(
'id' => '1',
'class' => 'Course',
'foreign_id' => '1',
'title' => 'something about this course',
'content' => 'The content here',
'created' => null,
'modified' => null
),
'Page' => array(
'id' => null,
'title' => null,
'slug' => null,
'created' => null,
'updated' => null
),
'Course' => array(
'id' => '1',
'title' => 'Course name',
'slug' => 'name-of-the-course',
'created' => '2012-10-11 00:00:00',
'updated' => '2012-10-11 00:00:00'
)
)
I only want it to return one of either Page
or Course
depending on which one is specified in Content.class
UPDATE: Combining the Page
and Course
models would seem like the obvious solution to this problem but the schemas I have shown above are just shown for the purpose of this question. The actual schemas are actually very different in terms of their fields and the each have a different number of associations with other models too.
UPDATE 2
Here is the query that results from running $this->Content->find('first');
:
SELECT `Content`.`id`, `Content`.`class`, `Content`.`foreign_id`, `Content`.`title`,
`Content`.`slug`, `Content`.`content`, `Content`.`created`, `Content`.`modified`,
`Page`.`id`, `Page`.`title`, `Page`.`slug`, `Page`.`created`, `Page`.`updated`,
`Course`.`id`, `Course`.`title`, `Course`.`slug`, `Course`.`created`,
`Course`.`updated` FROM `cakedb`.`contents` AS `Content`
LEFT JOIN `cakedb`.`pages` AS `Page` ON
(`Content`.`foreign_id` = `Page`.`id` AND `Content`.`class` = 'Page')
LEFT JOIN `cakedb`.`courses` AS `Course` ON (`Content`.`foreign_id` = `Course`.`id`
AND `Content`.`class` = 'Course') WHERE 1 = 1 LIMIT 1
Your query is okay. Just filter empty values after find:
public function afterFind($results, $primary = false) {
return Set::filter($results, true);
}
Take a look at this question.
You can also try to provide conditions
to you find
query that Page.id
in not null or Course.id
is not null.
这篇关于在CakePHP2中的多态性关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!