CakePHP2中的多态关联

Jos*_*eph 7 cakephp polymorphic-associations

我有3个型号Page,CourseContent

PageCourse包含元数据并Content包含HTML内容.

PageCourse这两个的hasManyContent

Content属于PageCourse

为了避免page_id和和course_id字段Content(因为我希望这可以扩展到超过2个模型)我正在寻找使用多态关联.我开始使用面包店中的多态行为,但它正在为我喜欢产生太多的SQL查询,它也抛出了"非法偏移"错误,我不知道如何解决(它写于2008年,似乎没有人最近提到它也许错误是由于它没有为Cake 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'),
        )
    );
}
?>
Run Code Online (Sandbox Code Playgroud)

课程模型

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'),
        )
    );
}
?>
Run Code Online (Sandbox Code Playgroud)

内容模型

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')
        )
    );
}
?> 
Run Code Online (Sandbox Code Playgroud)

好处是$this->Content->find('first')只生成一个SQL查询而不是3(就像多态行为的情况一样),但问题是返回的数据集包含两个belongsTo模型,而它应该只返回存在的模型.以下是返回数据的外观:

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'
    )
)
Run Code Online (Sandbox Code Playgroud)

我只希望它返回其中一个PageCourse取决于Content.class中指定的那个

更新:组合PageCourse模型似乎是这个问题的明显解决方案,但我上面显示的模式只是出于这个问题的目的.实际的模式在字段方面实际上是非常不同的,并且每个模式与其他模型的关联数也不同.

更新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` 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
Run Code Online (Sandbox Code Playgroud)

ban*_*cer 7

你的查询没问题.只需在找到后过滤空值:

public function afterFind($results, $primary = false) {
    return Set::filter($results, true);
}
Run Code Online (Sandbox Code Playgroud)

看看这个问题.

您也可以尝试提供conditions给您find查询的是Page.id在不为空 Course.id不为空.