cakephp polymorphic-associations

Asociaciones polimórficas en CakePHP2



polymorphic-associations (1)

Tengo 3 modelos, Page , Course y Content

Page y el Course contienen metadatos y el Content contiene contenido HTML.

Page y Course ambos tienen muchos Content

Content pertenece a la Page y el Course

Para evitar tener los campos page_id y course_id en el Content (porque quiero que esto course_id a más de 2 modelos), estoy buscando utilizar asociaciones polimórficas. Empecé utilizando el comportamiento polimórfico en la panadería pero está generando demasiadas consultas SQL para mi gusto y también está arrojando un error de "compensación ilegal" que no sé cómo solucionar (fue escrito en 2008 y nadie parece haberlo mencionado recientemente, entonces quizás el error se deba a que no se diseñó para Cake 2?)

De todos modos, he descubierto que casi puedo hacer todo lo que necesito al codificar las asociaciones en los modelos como tales:

Modelo de página

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''), ) ); } ?>

Modelo de curso

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''), ) ); } ?>

Modelo de contenido

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'') ) ); } ?>

Lo bueno es que $this->Content->find(''first'') solo genera una sola consulta SQL en lugar de 3 (como fue el caso con el Comportamiento polimórfico) pero el problema es que el conjunto de datos devuelto incluye ambos pertenencias a modelos, mientras que solo debería devolver realmente el que existe. Así es como se ven los datos devueltos:

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'' ) )

Solo quiero que devuelva una Page o Course dependiendo de cuál se especifique en Content.class

ACTUALIZACIÓN: la combinación de los modelos de Page y Course parecería la solución obvia a este problema, pero los esquemas que he mostrado arriba se muestran solo para el propósito de esta pregunta. Los esquemas reales son realmente muy diferentes en términos de sus campos y cada uno tiene un número diferente de asociaciones con otros modelos también.

ACTUALIZACIÓN 2

Aquí está la consulta que resulta de ejecutar $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


Tu consulta está bien. Simplemente filtra los valores vacíos después de encontrar:

public function afterFind($results, $primary = false) { return Set::filter($results, true); }

Eche un vistazo a esta pregunta.

También puede intentar proporcionar conditions para que find consulta que Page.id no es nulo o Course.id no es nulo.