public function aGet($attr) { //Logger::getInstance()->po_log("PO:aGet $attr"); // Si no es un atributo simple tengo que ver si hago lazy load... if (!array_key_exists($attr, $this->attributeTypes)) { // Si llega aqui estoy seguro de que no pide un atributo simple, se pide uno complejo. // Podria ser simple pero se paso un nombre normalizado para una columna. // Si el rol tiene el nombre de la assoc declarado, necesito ver cual es el nombre // completo de la key en hasOne o hasMany porque usa attribute__assocName. $attr = $this->getRoleWithAssocName($attr); // Soporte para lazy loading para hasOne y hasMany // No verifico que tenga valor porque deberia venir inicializado //if ( isset($this->attributeValues[$attr]) && $this->attributeValues[$attr] === self::NOT_LOADED_ASSOC ) if ($this->attributeValues[$attr] === self::NOT_LOADED_ASSOC) { // Si no tiene ID todavia no se guardo, entonces no puede cargar lazy algo que no se ha guardado. if (!isset($this->attributeValues['id'])) { return NULL; } $pm = PersistentManager::getInstance(); if (array_key_exists($attr, $this->hasMany)) { $pm->get_many_assoc_lazy($this, $attr); // El atributo se carga, no tengo que setearlo... // Se marca el dirtyMany al pedir hasMany porque no se tiene control // sobre como se van a modificar las instancias de la relacion solicitadas, // si dirtyMany esta en false y las intancias son modificadas, al salvar esta // intancia, las hasMany no se van a salvar en cascada. $this->dirtyMany = true; } else { if (array_key_exists($attr, $this->hasOne)) { // Si hay id de asociacion, lo cargo, si no lo pongo en NULL $assocAttr = DatabaseNormalization::simpleAssoc($attr); // email_id $assocId = $this->attributeValues[$assocAttr]; if ($assocId != NULL) { $this->attributeValues[$attr] = $pm->get_object($this->hasOne[$attr], $assocId); // Se marca el dirtyOne al pedir hasOne porque no se tiene control sobre como se va a modificar la instancia solicitada. $this->dirtyOne = true; } else { $this->attributeValues[$attr] = NULL; } } else { // Aun puede ser simple porque se pide por el nombre de la columna en lugar del nombre del atributo, // entonces primero hay que buscar si no se pide por el nombre de la columna. Idem a lo que hago en aSet. foreach ($this->attributeTypes as $classAttr => $type) { if (DatabaseNormalization::col($classAttr) == $attr) { if (isset($this->attributeValues[$classAttr])) { return $this->attributeValues[$classAttr]; } else { return NULL; } } } throw new Exception("El atributo " . $attr . " no existe en la clase (" . get_class($this) . ")"); } } } // si no esta cargada } // si no es simple // Devuelve atributo hasOne o hasMany (la devolucion de atributos simples se hace arriba). // Si el hasOne o hasMany no estaban cargados, fueron cargados bajo demanda y devueltos aqui. if (isset($this->attributeValues[$attr])) { return $this->attributeValues[$attr]; } return NULL; }
/** * 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 }