/** * Se utiliza para obtener una estructura de mapeo de clases de herencia sobre diferentes tablas. * Como se utiliza como auxiliar de generateAll la pongo aca, talvez pueda ir en ModelUtils, pero * en realidad solo se usa para generar el esquema y para salvar. * * @param $inheritanceClasses lista de clases de una estructura de herencia (clase de la instancia * del objeto que se esta manejando y todas sus superclases) (no se asume ningun orden). */ public static function getMultipleTableInheritance($inheritanceClasses) { Logger::getInstance()->dal_log("MTI::getMultipleTableInheritance"); // Ahora depende de una aplicacion. // La estructura de MTI no pueden establecerse entre clases del modelo de distintas aplicaciones. $ctx = YuppContext::getInstance(); $appName = $ctx->getApp(); // 1. clases y sus subclases $e = array(); // array por clave la clase y valor lista de subclases directas de dicha clase foreach ($inheritanceClasses as $class) { // Quiero poner solo las clases que esten en $inheritanceClasses, que pueden // no ser todas las de la estructura de herencia, esto sirve para // implementar getPartialInstancesToSave. // TODO: en que caso una clase no es de la estructura de herencia si lo // que le paso como parametro es solo la estructura de herencia??? // Ver lo que le pasan todas las operaciones que invoque a esta getMultipleTableInheritance. $sclss = ModelUtils::getSubclassesOf($class, $appName); $e[$class] = array_intersect($inheritanceClasses, $sclss); } // 2. Arma array de clases y lista de subclases que se mapean en la misma tabla. $e1 = array(); foreach ($e as $class => $subclasses) { $c_ins = new $class(); if (!array_key_exists($class, $e1) || $e1[$class] === NULL) { $e1[$class] = array(); } // armo otro array con las subclases que no tienen withTable. foreach ($subclasses as $subclass) { //$sc_ins = new $subclass(); //echo $subclass . " " . $sc_ins->getWithTable() . "<br />"; //if ( $sc_ins->getWithTable() === $c_ins->getWithTable() ) $e1[$class][] = $subclass; // solo si los withTable son iguales (o sea, que no lo redefine en la subclase) if (PersistentManager::isMappedOnSameTable($class, $subclass)) { $e1[$class][] = $subclass; //Logger::getInstance()->dal_log("isMapperOnSameTable: $class , $subclass " . __FILE__ . " " . __LINE__ ); } } } //Logger::getInstance()->dal_log("clases y subclases en la misma tabla " . __FILE__ . " " . __LINE__ ); //Logger::struct( $e1 ); // 3. Todas las keys estan en $inheritanceClasses foreach ($e1 as $class => $sameTableFirstLevelSubclasses) { //echo "CLASS: $class<br />"; if ($sameTableFirstLevelSubclasses !== NULL) { if (count($sameTableFirstLevelSubclasses) > 0) { $merge = array_merge($sameTableFirstLevelSubclasses, array()); // copia los valores ??? foreach ($sameTableFirstLevelSubclasses as $subclass) { $classesToMerge = $e1[$subclass]; if ($classesToMerge !== NULL) { $merge = array_merge($merge, $classesToMerge); $e1[$subclass] = NULL; } } $e1[$class] = $merge; } } } /* Esta mal! D, E y B deberian ser NULL tambien. Array ( [B] => [C] => Array ( [0] => E ) [D] => Array ( ) [E] => Array ( ) [F] => Array ( ) [G] => Array ( ) [A] => Array ( [0] => B [1] => D ) ) */ //$sol = array_filter( $e1, 'filter_not_null' ); // no me deja hacer el filter ... lo hago a mano... $sol = array(); foreach ($e1 as $class => $subclasses) { if ($subclasses !== NULL) { $sol[$class] = $subclasses; } } return $sol; }
/** * generateAll * Genera todas las tablas correspondientes al modelo previamente cargado. * * @pre Deberia haber cargado, antes de llamar, todas las clases persistentes. */ public static function generateAll() { Logger::getInstance()->pm_log("TableGen::generateAll ======"); $yupp = new Yupp(); $appNames = $yupp->getAppNames(); foreach ($appNames as $appName) { $dalForApp = new DAL($appName); // No puedo usar this->dal porque esta configurada para 'core' // Todas las clases del primer nivel del modelo. $A = ModelUtils::getSubclassesOf('PersistentObject', $appName); // FIXME> no es recursiva! // Se utiliza luego para generar FKs. $generatedPOs = array(); foreach ($A as $clazz) { $struct = MultipleTableInheritanceSupport::getMultipleTableInheritanceStructureToGenerateModel($clazz); // struct es un mapeo por clave las clases que generan una tabla y valor las clases que se mapean a esa tabla. foreach ($struct as $class => $subclassesOnSameTable) { // Instancia que genera tabla $c_ins = new $class(); // FIXME: supongo que ya tiene withTable, luego veo el caso que no se le ponga WT a la superclase... // FIXME: como tambien tiene los atributos de las superclases y como van en otra tabla, hay que sacarlos. // Para cara subclase que se mapea en la misma tabla foreach ($subclassesOnSameTable as $subclass) { $sc_ins = new $subclass(); // Para setear los atributos. $props = $sc_ins->getAttributeTypes(); $hone = $sc_ins->getHasOne(); $hmany = $sc_ins->getHasMany(); // FIXME: si el artibuto no es de una subclase parece que tambien pone nullable true... // Agrega constraint nullable true, para que los atributos de las subclases // puedan ser nulos en la tabla, para que funcione bien el mapeo de herencia de una tabla. //Logger::getInstance()->pm_log( "Para cada attr de: $subclass " . __FILE__ . " " . __LINE__); foreach ($props as $attr => $type) { // FIXME: esta parte seria mas facil si simplemente cuando la clase tiene la constraint // y le seteo otra del mismo tipo para el mismo atributo, sobreescriba la anterior. $constraint = $sc_ins->getConstraintOfClass($attr, 'Nullable'); if ($constraint !== NULL) { //Logger::getInstance()->log( "CONTRAINT NULLABLE EXISTE!"); // Si hay, setea en true $constraint->setValue(true); } else { // Si no hay, agrega nueva //Logger::getInstance()->log( "CONTRAINT NULLABLE NO EXISTE!, LA AGREGA"); $sc_ins->addConstraints($attr, array(Constraint::nullable(true))); } } //Logger::getInstance()->pm_log( "Termina con las constraints ======= " . __FILE__ . " " . __LINE__); // Se toma luego de modificar las restricciones $constraints = $sc_ins->getConstraints(); foreach ($props as $name => $type) { $c_ins->addAttribute($name, $type); } foreach ($hone as $name => $type) { $c_ins->addHasOne($name, $type); } foreach ($hmany as $name => $type) { $c_ins->addHasMany($name, $type); } // Agrego las constraints al final porque puedo referenciar atributos que todavia no fueron agregados. foreach ($constraints as $attr => $constraintList) { $c_ins->addConstraints($attr, $constraintList); } } $parent_class = get_parent_class($c_ins); if ($parent_class !== 'PersistentObject') { // La superclase de c_ins se mapea en otra tabla, saco esos atributos... $suc_ins = new $parent_class(); $c_ins = PersistentObject::less($c_ins, $suc_ins); // Saco los atributos de la superclase } $tableName = YuppConventions::tableName($c_ins); // FIXME: esta operacion necesita instanciar una DAL por cada aplicacion. // La implementacion esta orientada a la clase, no a la aplicacion, hay que modificarla. // Si la tabla ya existe, no la crea. if (!$dalForApp->tableExists($tableName)) { // FIXME: c_ins no tiene las restricciones sobre los atributos inyectados. self::generate($c_ins, $dalForApp); // Para luego generar FKs. $generatedPOs[] = $c_ins; } } // foreach ($struct as $class => $subclassesOnSameTable) } // foreach( $A as $clazz ) // ====================================================================== // Crear FKs en la base. //Logger::struct( $generatedPOs, "GENERATED OBJS" ); foreach ($generatedPOs as $ins) { $tableName = YuppConventions::tableName($ins); $fks = array(); // FKs hasOne $ho_attrs = $ins->getHasOne(); foreach ($ho_attrs as $attr => $refClass) { // Problema: pasa lo mismo que pasaba en YuppConventions.relTableName, esta tratando // de inyectar la FK en la tabla incorrecta porque la instancia es de una superclase // de la clase donde se declara la relacion HasOne, entonces hay que verificar si una // subclase no tiene ya el atributo hasOne declarado, para asegurarse que es de la // instancia actual y no intentar generar la FK si no lo es. $instConElAtributoHasOne = NULL; $subclasses = ModelUtils::getAllAncestorsOf($ins->getClass()); foreach ($subclasses as $aclass) { $ains = new $aclass(); if ($ains->hasOneOfThis($refClass)) { //Logger::getInstance()->log( $ains->getClass() . " TIENE UNO DE: $refClass" ); $instConElAtributoHasOne = $ains; // EL ATRIBUTO ES DE OTRA INSTANCIA! break; } } // Si el atributo de FK hasOne es de la instancia actual, se genera: if ($instConElAtributoHasOne === NULL) { // Para ChasOne esta generando "chasOne", y el nombre de la tabla que aparece en la tabla es "chasone". $refTableName = YuppConventions::tableName($refClass); $fks[] = array('name' => DatabaseNormalization::simpleAssoc($attr), 'table' => $refTableName, 'refName' => 'id'); } } // FKs tablas intermedias HasMany $hasMany = $ins->getHasMany(); foreach ($hasMany as $attr => $assocClassName) { //Logger::getInstance()->pm_log("AssocClassName: $assocClassName, attr: $attr"); if ($ins->isOwnerOf($attr)) { $hm_fks = array(); $hasManyTableName = YuppConventions::relTableName($ins, $attr, new $assocClassName()); // "owner_id", "ref_id" son FKs. // =============================================================================== // El nombre de la tabla owner para la FK debe ser el de la clase // donde se declara el attr hasMany, // no para el ultimo de la estructura de MTI (como pasaba antes). $classes = ModelUtils::getAllAncestorsOf($ins->getClass()); //Logger::struct( $classes, "Superclases de " . $ins1->getClass() ); $instConElAtributoHasMany = $ins; // En ppio pienso que la instancia es la que tiene el atributo masMany. foreach ($classes as $aclass) { $_ins = new $aclass(); if ($_ins->hasManyOfThis($assocClassName)) { //Logger::getInstance()->log("TIENE MANY DE " . $ins2->getClass()); $instConElAtributoHasMany = $_ins; break; } //Logger::struct( $ins, "Instancia de $aclass" ); } // =============================================================================== $hm_fks[] = array('name' => 'owner_id', 'table' => YuppConventions::tableName($instConElAtributoHasMany->getClass()), 'refName' => 'id'); $hm_fks[] = array('name' => 'ref_id', 'table' => YuppConventions::tableName($assocClassName), 'refName' => 'id'); // Genera FKs $dalForApp->addForeignKeys($hasManyTableName, $hm_fks); } } // foreach hasMany // Genera FKs $dalForApp->addForeignKeys($tableName, $fks, false); } // foreach PO } // foreach app }