在CakePHP2中的多态性关联 [英] Polymorphic associations in CakePHP2

查看:161
本文介绍了在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆