interrelaciones y relaciones

Buenas tardes, soy nueva en esto de usar un framework tipo modelo vista controlador y creacion de paginas web, pero he ido tomandole el modo de trabajo poco a poco.

Mi duda como el titulo lo dice tiene que ver con las relaciones y las interrelaciones, a lo que voy es:

supongo un caso de ejemplo sencillo de "mama tiene hijo"

la interrelacion ‘tiene’ posee las claves de mama y de hijo respectivamente.

la tabla de ‘mama’ tiene una cardinalidad del tipo 0,N, es decir que no necesariamente tiene que tener un hijo

la tabla de ‘hijo’ tiene una cardinalidad del tipo 1,1, es decir que obligatoriamente debe tener una madre

Entonces este tipo de relaciones de 1:N (una madre tiene muchos hijos) como la trabajo? como haria para crear o mostrar los hijos de una madre si en la tabla de ‘mama’ no tengo ninguna clave foranea correspondiente al hijo?

Espero no haberlos confundido y me puedan ayudar con esta duda. Gracias : )

Una mamá puede tener varios hijos o ninguno, pero un hijo solo puede tener una mamá, a ver si entendi el problema

tienes una tabla ‘madre’ y una tabla ‘hijo’, lo que debes tener ahora es una tabla que contenga los dos ids de ‘madre’ e ‘hijo’ tabla a la que llamaremos MadreHijo


|madreHijo|

|_id_madre__|

|id_hijo_|

en esta nueva tabla, ‘id_hijo’ no se puede repetir y estarias trabajando con una relación de muchos a muchos.

para no tener inconvenientes con las relaciones MANY_MANY, te recomiendo el uso de esta extensión: save-relations-ar-behavior

espero sea de ayuda mi respuesta

El dilema es que es una relación one to many no many to many, entonces la tabla hijo debería de tener un atributo del tipo clase madre, si deseas que la visibilidad sea bidireccional entonces tendrías que tener en tu clase madre un array Object Collection con los hijos que tenga esa madre, un ejemplo simple




class madre 

{

   /**

    *

    * @var Array $hijos

    */

    private $hijos;


    /**

     *

     * @method setHijos envia un nuevo hijo a la clase madre

     */

    public function setHijos(Hijo $hijo) {

        $this->hijos[] = $hijo;

        return $this;

    }


    /**

     *

     * @method getHijos obtiene un arreglo con los hijos de la madre

     */

    public function getHijos() {

        return $this->hijos;

    }

}


class Hijo

{

    /**

    *

    * @var Madre $madre

    */

    private $madre;


    /**

     *

     * @method setMadre envia la madre

     */

    public function setMadre(Madre $madre) {

        $this->madre = $madre;

        return $this;

    }


    /**

     *

     * @method getMadre obtiene la clase madre del hijo

     */

    public function getMadre() {

        return $this->madre;

    }

}



hasta donde tengo entendido es el caso one to many, sin embargo tendría que verse bien cual es el caso, porque las relaciones que el framework te provee debería de funcionarte, saludos

si entiendo como es la relación, pero hacer una MANY_MANY me pareció mucho mas facil y creo que soluciona el problema, no?

Claro que soluciona el problema, pero en el diseño de clases es incorrecto (por lo que indicaron ustedes que es one to many) y te explico el porque si yo dejo que sea many to many me dejaría que un hijo pueda tener varias madres porque la relación lo permite, ahora ¿es esto correcto? en el caso particular que se expone y según entiendo creo que no porque te esta dejando la posibilidad del error de que un hijo pueda decir que tiene dos madres por el tipo de diseño que se le implemento y eso en nuestro rubro es grave, porque para los que nosotros desarrollamos no entienden nada de lo que es esto, por eso es que es bueno antes de ponernos a echar nuestro código hacer un buen diseño de clases de manera que se estudie bien el tipo de relaciones y todo esto y el margen de error sea mínimo.

Por cierto esta interesante la extensión que compartiste, este tipo de debate es interesante porque a lo mejor llega otra persona con una implementación as limpia y aprendemos un poco más, saludos

Hola Cristina. Te invito a que te unas al grupo de Facebook que por ahora tenemos (sigue la URL que esta en mi firma al pie de esta nota), mañana pondremos en linea un foro oficial de Yii en Español, quiza un poco vacio de contenido por ser nuevo, pero la comunidad es muy activa, toda en español.

Gracias por las respuestas, aunque como dice Carlos Belisario no se puede tener la relacion muchos a muchos porque el problema no lo plantea, pero mi duda sigue con respecto a la parcialidad de la tabla de "mama".

Pongo otro ejemplo porque puede que con este no se entienda bien.

"Persona habita_una casa"

No todas las personas habitan una casa (aqui es donde esta la parcialidad que hacia referencia con el ejemplo de mama tiene hijo)

Una casa es habitada obligatoriamente por una persona. Hasta aqui es muy similar al ejemplo de "mama tiene hijo", ahora bien en el myadmin tendria las dos relaciones "Persona" y "Casa" y ademas la interrelacion de "habita_una" en donde estarian las personas que estan usando una casa, quedandome 3 tablas en el myadmin.

  (0,N)               1:N                  (1,1)

|persona| ----------> |habita_una| <---------- |casa|


                 &quot;interrelacion&quot;

ahora en el myadmin yo no tengo en la tabla de "casa" la clave foranea de persona, porque segun entiendo esto lo puedo hacer cuando las tablas tienen como "cota minima" un 1 y no un 0 en alguna de las tablas, como es el caso de "persona" que tiene el (0,N). Ahora la interrelacion de "habita_una" tendria las claves foraneas de "persona" y "casa". Como seria en el Yii las relaciones entre "persona" y "casa" si no tienen un clave foranea por la cual yo pueda tener una relacion??

pero es que en el ejemplo que colocas ahora la relación es muchos a muchos porque una persona puede habitar varias casas y una casa puede estar habitada por varias personas, como te dije es cuestión de ver bien el esquema de relación y un poco de lógica, saludos

entiendo a que se refieren, pero insisto, debería tener tres tablas ‘Madre’, ‘Hijo’ y ‘Madre_tiene_Hijo’ y en esta ultima tabla guardare las referencias de que hijo corresponde a determinada madre y para evitar que un hijo sea usado por mas de dos madres, entonces establezco que hijo sea ‘UNIQUE’, o estoy cometiendo un error y deberia ponerme a estudiar bases de datos??

se me ocurren otras opciones pero esta me parece la mas adecuada.

Carlos, si la extension es muy buena, me ha sido de mucha utilidad, creo que es indispensable :P

Como te dije es cuestión del planteamiento y del diseño que se haga, si haces que sea many to many es porque un hijo podrá tener dos madres, crear una tabla mas y poner unique el hijo es limitar la relación, para eso esta el otro tipo de relación que es one to many donde si en base de datos hay este tipo de relación en la tabla hijo se crea un foreign key que hace referencia a la clave principal de la tabla madre, son dos tipos distintos de relaciones porque el comportamiento en el diseño es distinto, ahora en el ejemplo que coloca de las casas si es diferente porque ahí si se puede aplicar el many to many porque yo como persona puedo habitar varias casas y una casa puede ser habitada por varias personas ahí si sale la tabla extra casas_personas que tendría un foreign key a la pk de la casa y un fk a la pk de las personas.

Respondiendo la pregunta siempre es bueno y jamás esta demás repasar diseño de base de datos y diseño de clases, cuando vamos a hacer nuestros desarrollos, aunque llega el momento en que ya los diagramas de db y los uml para las clases salen automáticamente, saludos

jajajaja, si claro, en ese ejemplo es una relación de 1 a muchos, pero pues me perdi con el problema y aun no lo entiendo muy bien ‘que mala comprensión de lectura ^^’ y lo que se puede hacer es como lo dije o poner en la tabla de hijo un campo para la llave primaria de la madre y que este pueda ser nulo :S, la vd es que aun estoy perdido con el primer planteamiento, lo primero que se me ocurrio fue el poner la referencia a la madre en la tabla hijo pero me deje llevar por lo que no entendi del problema y trate de solucionar algo que no debia

BIEN Y MAL.

solo necesitas una tabla intermedia que represente a la relacion madre e hijo solo si la relacion tiene atributos…por decirlo de esta manera:

"christian es el hijo #1 varon de rosa". (hijonumero y sexo son los atributos de la relacion). Si los atributos no existen, entonces crear una tabla es violar normas de diseño de DB porque estarias creando una relacion 1:1 la cual termina disolviendose en un indice en la tabla HIJO.

entonces y solo entonces, debes tener una tabla intermedia para guardar los atributos "sexo" "hijonumero". Pero si esos atributos de la relacion no son necesarios entonces violas una norma del diseño de datos al crear tablas 1:1 las cuales se deben desaparecer.

para ese caso madre-hijo, si la relacion no tiene atributos, simplemente hay que crear un indice sobre la tabla HIJO que diga quien es su madre y se resuelve el problema.

En Yii se manejaria con un relations() BELONGS_TO del lado del hijo y con HAS_MANY del lado de la madre.

es un problema simple, comun del diseño del modelo de datos, que veo que se hipercomplicó.

jajajaja tienes razón, yo no se por que se me ocurrió que el id de la madre no podia ir en la tabla hijo desde el principio ^^, por eso que fue que propuse tan mala solución xD, afortunadamente están ustedes que pueden hacerme caer en cuenta de mis errores :P

las tablas tienen mas atributos pero por simplicidad no los menciono :(

entonces en ese caso estarias diciendo que del lado del hijo "muchos hijos tienen una madre" y del lado de la madre "una madre tiene muchos o ningun hijo" correcto? ese "ningun hijo" no afecta en nada? y esa parte en la que especifico las relaciones en el Yii es que tengo confusiones, la clave que va en esas clases es la de cual?.

Es decir:

clase de la madre

‘id_hijo’ => array(self::HAS_MANY, ‘hijo’, ‘que_va_aca?’)

clase del hijo

‘id_madre’ => array(self::BELONGS_TO, ‘madre’, ‘que_va_aca?’)

lo que pasa es que, todos esos datos de lo que yo llamo "interrelacion", si los asignaba con el sql purito a esa tabla(interrelacion) para saber a que madre le pertenecia un hijo, claro esta que eso pasaba porque tengo lo que yo conozco como "participacion parcial" cuando puedo tener (para este caso) una madre (es decir solo mujer) sin hijo, pero con el Yii creo que esas "interrelaciones" no las uso porque el las maneja con "public function relations()".

Entonces cual es la clave que va en esas "funciones publicas de relaciones"??

Naguara que enredo tengo con esto de las relaciones :-[ … disculpen tanta molestia

hola cristina,

esta claro que las tablas tengan mas atributos eso no es el asunto, no me refiero a "atributos de las tablas", ni de madre, ni de hijo, me refiero a los "atributos de la relacion", lo cual es otra cosa.

respecto a que si el hijo no tiene madre etc…eso es algo que tiene una solucion muy simple y se repara es en el modelo de datos, siempre y cuando el modelo de datos soporte el manejo de relaciones, por lo tanto si es MySQL deberás usar el motor INNODB (si no usas innodb no podras controlar relaciones para validar el modelo de integridad relacional). Si es Interbase/Firebird ya traen manejadores de relaciones, si es Access tambien, Oracle tambien, Postgress tambien.

como digo, no se porque el problema se abrio tanto, basta con crear un FOREIGN_KEY entre Madre e Hijo de tipo 1:N ( UNA madre puede tener VARIOS O NINGUN hijo, pero UN hijo no puede NO TENER madre), eso se resuelve en el modelo de datos y no en otro lado porque sino viene alguien y se salta tu sistema luego lanza un query directo a la DB y se raspa a todas las madres dejando a tus hijos sin madre, resultado: CRASH por doquier. Eso es lo que quieres controlar ? entonces debes hacer el control de integridad relacional en el modelo de datos, si es mySql usando INNODB.

Para asegurarse que un hijo no se quede sin madre entonces en el FOREIGN_KEY entre madre-hijo (es decir en la relacion) hay que establecer una NORMA DE INTEGRIDAD que diga que HIJO.IDMADRE es mandatorio (IS MANDATORY), eso causara que un usuario desconsiderado o tu misma por algun error no puedan efectuar un fatal "delete * from madres;", si ese fuese el caso y el foreing_key esta establecido como mandatorio para hijo.idmadre entonces el usuario que intente hacer el script recibira un error desde el modelo de datos.

No hacerlo de esta manera es condenar a tu sistema a una muerte segura.

agrego otra cosa que al principio te respondi pensando que era el caso pero ya veo que no lo es:

Hay dos tipos de cosas entre la madre y el hijo:

[list=1][]los atributos de la relacion en respecto a integridad relacional.[]los atributos de la relacion en respecto a COMO SE RELACIONA madre con HIJO.[/list]

En el primer punto (1), se maneja en el MODELO DE DATOS (en la base de datos) Y NO EN YII como mucha gente asegura. SI, YII PUEDE pero no es el lugar correcto para controlar la integridad_relacional porque si se saltan a YII y van directo a la base de datos y lanzan el fatidico script "delete * from madres;" entonecs ni YII NI DIOS te salvaran del crash. Por eso, el control de integridad relacional se hace en el modelo de datos mediante la parametrizacion del FOREIGN_KEY que para el caso de la madre-hijo debe indicar que HIJO.IDMADRE IS_MANDATORY.

Siguiendo con el punto (2),los atributos de la relacion respecto a COMO se relaciona madre con hijo, es un tema que tiene DOS RAMAS:

[list=1][*]Si la relacion entre madre e hijo es solo eso y nada mas (decir que christian es hijo de rosa) entonces NO HACE FALTA PONER UNA TABLA INTERMEDIA LLAMADA "MADREXHIJO", porque eso viola una norma de diseño de datos porque se esta creando una tabla con relaciones 1:1. En cambio simplemente en este caso a la tabla HIJO se le agrega un campo (y su indice) llamado HIJO.IDMADRE, con su FOREIGN_KEY establecido como IS_MANDATORY. [u]Al hacer esto el modelo de datos denegara cualquier intento por volarse a una madre que tenga hijos anotados.

[/u][*]Si la relacion entre madre e hijo ES PARAMETRIZABLE es decir, "christian es hijo de rosa y es su hijo mayor" , entonces, hay que crear una tabla MADREXHIJO con TRES campos: IDMADRE, IDHIJO, ES_MAYOR (boolean). Entonces ahi, la cosa cambia, porque se hara dificil controlar que alguien no vuele a un registro de MADREXHIJO y que al hacerlo entonces quede un hijo sin madre, para solventar eso se usa un TRIGGER_BEFORE_DELETE para garantizar que un hijo tenga una madre.

Esto puede extenderse a muchos casos. Pero es imperioso que tomes el concepto bien y tu misma lo extiendas.[/list]

a nivel de YII esas relaciones como van?, el crud crea unas relaciones que el asume son las mas idoneas pero, eso es lo que tengo que modificar porque me esta aceptando que un hijo puede tener mas de una madre.

El error está en esas relaciones.

esto sale en la guia del Yii:

‘VarName’=>array(‘RelationType’, ‘ClassName’, ‘ForeignKey’, …additional options)

donde VarName es el nombre de la relación; RelationType especifica el tipo de relación, que puede ser una de las cuatro constantes: self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY y self::MANY_MANY; ClassName es el nombre de la clase relacionada a ésta clase AR; y ForeignKey especifica la(s) clave(s) foránea(s) involucrada(s) en la relación.

Entonces es ahi mi duda, esa clave foranea es la de la cual relacion? la de la tabla en donde estoy parada o la relacionada? y del otro lado cual es la clave??

Cristina, porque no te mueves al foro de yii en español ? http://www.yiiframeworkenespanol.org ? ahi hay un foro y es mas facil por ahora responder por ahi ademas que la comunidad te puede dar buena guia tambien, toda en español y bien activa.

te explico en terminos de modelo de datos y cambiando el ejemplo madre-hijo por otro, Vehiculo Pieza:

[b]Vehiculo {idvehiculo, marca}

Pieza {idpieza, nombrepieza} [/b]

En este caso veras lo que he querido mostrarte durante el post: la existencia de una tabla intermedia, el hecho de que exista o no.

En el caso de madre-hijo, esa tabla intermedia no era necesaria, pero en este ejemplo si, porque una pieza puede estar sola sin ser requerida por un vehiculo y a su vez se requerida por uno o varios vehiculos.

VehiculoPieza {idvehiculo, idpieza, cantidad}

Una pieza puede ir en un vehiculo pero con una cantidad, cosa que se me hacia dificil explicar con el ejemplo madrehijo.

fijate, tu "no debes" volarte una pieza asi como asi…porque dejarias a "VehiculoPieza" apuntando a una pieza que no existe, para ello, en el modelo de datos (en mysql), creas un foreign key. Quiza tu duda esta en como funciona este.





CREATE  TABLE IF NOT EXISTS `cruge`.`vehiculopieza` (

  `idvehiculopieza` INT(11) NOT NULL ,

  `idpieza` INT(11) NOT NULL ,

  `idvehiculo` INT(11) NOT NULL ,

  PRIMARY KEY (`idvehiculopieza`) ,

  INDEX `fk_vehiculopieza_pieza` (`idpieza` ASC) ,

  INDEX `fk_vehiculopieza_vehiculo1` (`idvehiculo` ASC) ,




  CONSTRAINT `fk_vehiculopieza_pieza`

    FOREIGN KEY (`idpieza` )

    REFERENCES `cruge`.`pieza` (`idpieza` )

    ON DELETE NO ACTION

    ON UPDATE NO ACTION,




  CONSTRAINT `fk_vehiculopieza_vehiculo1`

    FOREIGN KEY (`idvehiculo` )

    REFERENCES `cruge`.`vehiculo` (`idvehiculo` )

    ON DELETE RESTRICT

    ON UPDATE NO ACTION)




ENGINE = InnoDB;



Fijate en:

CONSTRAINT fk_vehiculopieza_pieza

FOREIGN KEY (`idpieza` )


REFERENCES `pieza` (`idpieza` )


ON DELETE RESTRICT


ON UPDATE NO ACTION,

Cuando la table vehiculopieza tiene un FK definido (fk_vehiculopieza_pieza) y que apunta a la tabla "pieza" (por eso dice: REFERENCES pieza (idpieza ) ), eso hace que cuando alguien o algo quiera hacer un "delete * from pieza" entonces el MOTOR DE DATOS SABRA que PIEZA tiene en VEHICULOPIEZA una restriccion (Constraint) que le prohibe (ON DELETE RESTRICT) rasparse un registro de PIEZA si este esta referenciado en VEHICULOPIEZA.

Asi funciona el FOREIGN_KEY.

Ahora YII…es otro cosa…otro pastel de otra panaderia…y las personas tienden a pensar que con el CActiveRecord::relations() ya tienen el problema resuelto y listo…y eso no es asi:

Por ejemplo, si con Gii creas los los modelos para Vehiculo, Pieza y VehiculoPieza, veras lo siguiente:

VehiculoPieza.php





class Vehiculopieza extends CActiveRecord

{

....


	public function relations()

	{

		return array(

			'idpieza0' => array(self::BELONGS_TO, 'Pieza', 'idpieza'),

			'idvehiculo0' => array(self::BELONGS_TO, 'Vehiculo', 'idvehiculo'),

		);

	}

}

Si te das cuenta GII (no yii), te ha ayudado a crear las relaciones para que tu puedas navegar por ellas en YII…pero nada mas.

Si te fijas en la tabla Pieza.php, veras que hace una relacion hacia "vehiculopiezas", se usaria asi:




<?php

class Pieza extends CActiveRecord

{

	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'vehiculopiezas' => array(self::HAS_MANY, 'Vehiculopieza', 'idpieza'),

		);

   	}

}



Esto ultimo te permitiria navegar en Yii por las Asignaciones de Esta Pieza que se hicieron a un Vehiculo:





// listas las asignaciones de cada pieza:


$piezaSelec = Pieza::model()->findByAttribuyes(array('idpieza'=>'123'));


foreach($piezaSelec->vehiculopiezas as $vehiculopiezarecord){

 	echo "pieza asignada a ".$vehiculopiezarecord->idvehiculo

             .", en cantidad: ".$vehiculopiezarecord->cantidad

             .", y el nombre del vehiculo es: ".$vehiculopiezarecord->idvehiculo0->marca;


// nota que : $vehiculopiezarecord->idvehiculo0, es el apuntador al objeto Vehiculo que Yii ha cargado para ti para ser accedido mediante la relacion.

}



nota que : $vehiculopiezarecord->idvehiculo0, es el apuntador al objeto Vehiculo que Yii ha cargado para ti para ser accedido mediante la relacion, por tanto y como habras de suponer, $vehiculopiezarecord->idvehiculo0 es la relacion definida mas arriba bajo el array key ‘idvehiculo0’,

esto se puede interpretar asi (tomado de VehiculoPieza.php mas arriba):

‘idvehiculo0’ => array(self::BELONGS_TO, ‘Vehiculo’, ‘idvehiculo’),

indica que:

dispones de un atributo virtual llamado: idvehiculo0 , accesible mediante: $vehiculoPiezaInst->idvehiculo0

que, apunta a (BELONGS_TO) a un objeto de instancia de "Vehiculo", que previamente fue buscado por YII mediante el atributo: "idvehiculo".

Yii hizo esto por ti por debajo cuando invocaste a "idvehiculo0":

"select * from VEHICULO where IDVEHICULO = ".$this->idvehiculo;

Puedes guiarte de estos ejemplos bien definidos:

http://www.yiiframeworkenespanol.org/index.php?r=site/ejemplos

mi duda es ams corta o almeno lo creo asi … a mi gii no me genera ninguna ralacion entre tablas … me pudieras decir que herramienta tu tienes para generar los sql para que te los genera asi :

CREATE TABLE IF NOT EXISTS cruge.vehiculopieza (

idvehiculopieza INT(11) NOT NULL ,

idpieza INT(11) NOT NULL ,

idvehiculo INT(11) NOT NULL ,

PRIMARY KEY (idvehiculopieza) ,

INDEX fk_vehiculopieza_pieza (idpieza ASC) ,

INDEX fk_vehiculopieza_vehiculo1 (idvehiculo ASC) ,

CONSTRAINT fk_vehiculopieza_pieza

FOREIGN KEY (`idpieza` )


REFERENCES `cruge`.`pieza` (`idpieza` )


ON DELETE NO ACTION


ON UPDATE NO ACTION,

CONSTRAINT fk_vehiculopieza_vehiculo1

FOREIGN KEY (`idvehiculo` )


REFERENCES `cruge`.`vehiculo` (`idvehiculo` )


ON DELETE RESTRICT


ON UPDATE NO ACTION)

ENGINE = InnoDB;