/**
  * Metodo auxiliar para cargar los objetos asociados en cascada, tanto los hasOne como los hasMany, 
  * verificando previamente si no fueron ya cargados.
  * @param PersistentObject $obj el objeto al que hay que cargarle los objetos asociados.
  */
 private function getCascadeAssocs($obj)
 {
     // Para saber si estaba limpia previamente, para poder limpiar lo que esta operacion ensucia (dirty bits)
     $wasClean = $obj->isClean();
     // TODO: Verificar si para los objetos en hasOne, sus asociaciones son cargadas en cascada.
     // Para cada objeto hasOne, lo trae.
     // Para el objeto hago get para hasOne y getMany para los hasMany.
     $ho_attrs = $obj->getHasOne();
     foreach ($ho_attrs as $attr => $assoc_class) {
         // attr = "email_id" ?
         $ho_instance = new $assoc_class();
         $hasOneAttr = DatabaseNormalization::getSimpleAssocName($attr);
         // email
         $assoc_id = $ho_instance->aGet($attr);
         $assoc_obj = NULL;
         if (ArtifactHolder::getInstance()->existsModel($assoc_class, $assoc_id)) {
             $assoc_obj = ArtifactHolder::getInstance()->getModel($assoc_class, $assoc_id);
         } else {
             $assoc_obj = $this->get($assoc_class, $assoc_id);
             ArtifactHolder::getInstance()->addModel($assoc_obj);
         }
         //$obj->{"set".$attr}( $assoc_obj );
         $obj->aSet($attr, $assoc_obj);
     }
     // Para cada objeto hasMany, lo trae
     // Para el objeto hago get para hasOne y getMany para los hasMany.
     $hm_attrs = $obj->getHasMany();
     foreach ($hm_attrs as $attr => $class) {
         $this->getMany($obj, $attr);
         // Carga los elementos del atributo en la clase.
     }
     // TODO: si carga en cascada, entonces la instancia de obj y sus clases asociadas
     //       se cargan todas en el mismo momento, por lo que no habria nada sucio,
     //       y seria innecesario verificar si estaba sucia antes de la carga.
     // Solo limpia si la clase estaba limpia antes de la operacion
     if ($wasClean) {
         $obj->resetDirty();
     }
     // Apaga las banderas que se prendieron en la carga
 }
 /**
  * Nombre de la tabla que almacena una instancia de un objeto persistente.
  * Si tiene "withTable", elige ese, si no, toma el nombre de la clase normalizado como nombre de la tabla.
  * @param PersistentObject $ins Instancia de la cual derivar el nombre de la tabla en la base de datos.
  */
 public static function tableName($instance_or_class)
 {
     $ins = $instance_or_class;
     // TEST
     if (empty($instance_or_class)) {
         debug_print_backtrace();
     }
     //echo '<pre> tableName '. print_r($instance_or_class, true) .__FILE__.__LINE__.'</pre>';
     if (!is_object($ins)) {
         $ins = new $instance_or_class(array(), true);
     }
     // Si no es instancia, es clase, creo una instancia de esa clase.
     if (!$ins instanceof PersistentObject) {
         throw new Exception("La instancia debe ser de PO y es " . gettype($ins));
     }
     // FIXME: en pila de lados tengo que crear una instancia para poder llamar a este metodo,
     // porque no mejor hacer que pueda recibir tambien el nombre de la clase, y en ese caso,
     // resuelve la tabla como nombre de clase, sin considerar withTable, o directamente crea
     // la instancia internamente. Permitiendo tambien pasarle una instancia, o sea, ambas opciones.
     // FIXME: SI NO TIENE WIHT TABLE TENGO QUE VER POR EL NOMBRE DE LA CLASE, PERO ME VIENE PersistentObject,
     // TENGO CAPAZ QUE SETEARLE EL WITH TABLE A MANO SI NO LO TIENE SETEADO!! NOOOOO!!!
     // Si no tiene withTable, tengo que crear el nombre de la tabla a partir del nombre de la clase.
     // Si no tiene withTable, quiere decir que en ninguna superclase de ella se define, entonces tengo que
     // obtener la superclase de nivel 1 y el nombre de la tabla se saca de el nombre de esa clase.
     if ($ins->getWithTable() != NULL && strcmp($ins->getWithTable(), "") != 0) {
         $tableName = $ins->getWithTable();
     } else {
         $superclaseNivel1 = $ins->getClass();
         while (($parent = get_parent_class($superclaseNivel1)) !== 'PersistentObject') {
             $superclaseNivel1 = $parent;
         }
         $tableName = $superclaseNivel1;
     }
     // Filtro...
     $tableName = DatabaseNormalization::table($tableName);
     // TODO: La funcion de normalizacion esta deberia estar en un core.basic.String.
     return $tableName;
 }
Esempio n. 3
0
 /**
  * Se recibe un objecto a la que ya se ha verificado que debe insertarse en la base de datos.
  * @param $object POs a salvar.
  */
 private function insert_query($object, $tableName = NULL)
 {
     Logger::getInstance()->dal_log("DAL:insert_query " . __FILE__ . " " . __LINE__);
     // INSERT INTO hello_world_persona ( nombre ,edad ,class ,id ,deleted ) VALUES ('pepe' ,'12' ,'Persona' ,'6' ,'' );
     if (!$tableName) {
         $tableName = YuppConventions::tableName($object);
     }
     $q = "INSERT INTO " . $tableName . " ( ";
     // DBSTD
     $attrs = $object->getAttributeTypes();
     // Recorro todos los atributos simples...
     $tableAttrs = "";
     foreach ($attrs as $attr => $type) {
         $tableAttrs .= DatabaseNormalization::col($attr) . " ,";
         // DBSTD
     }
     $tableAttrs = substr($tableAttrs, 0, sizeof($tableAttrs) - 2);
     $q .= $tableAttrs;
     $q .= ") VALUES (";
     // El codigo es distinto al de update porque la forma de la consulta es distinta.
     // TODO: Si el valor es null tengo que poner null en la tabla, no el string vacio.
     // TODO: Verificar atributos no nullables en null en la instancia de la clase, esto falta agregar cosas a la clase persistente, "las restricciones"
     $tableVals = "";
     foreach ($attrs as $attr => $type) {
         $value = $object->aGet($attr);
         // Valor del atributo simple.
         if ($value === NULL) {
             $tableVals .= "NULL ,";
         } else {
             if (is_string($value)) {
                 $tableVals .= "'" . addslashes($value) . "' ,";
             } else {
                 if (is_bool($value)) {
                     $tableVals .= "'" . ($value === true ? "1" : "0") . "' ,";
                 } else {
                     $tableVals .= "'" . $value . "' ,";
                 }
             }
         }
         // FIXME: OJO, si no es literal no deberia poner comillas !!!!  y si es null deberia guardar null
         //echo $attr . " tiene tipo: " . gettype($value) . " y valor '" . $value . "'<br/>";
     }
     $tableVals = substr($tableVals, 0, sizeof($tableVals) - 2);
     $q .= $tableVals;
     $q .= ");";
     // Si hay una excepcion, llega hasta la capa superior.
     $this->db->execute($q);
 }
Esempio n. 4
0
    echo $attr;
    ?>
      </th>
    <?php 
}
?>
  </tr>
  <?php 
foreach ($list as $po) {
    ?>
    <tr>
      <?php 
    foreach ($attrs as $attr => $type) {
        ?>
        <?php 
        if ($attr === 'deleted' || DatabaseNormalization::isSimpleAssocName($attr)) {
            continue;
        }
        // No quiero mostrar la columna 'deleted' o hasone attr_id
        ?>
        <td>
          <?php 
        if ($attr == "id") {
            ?>
            <?php 
            // Si en la aplicacion actual existe el controlador para esta clase de dominio,
            // que vaya a la aplicacion actual y a ese controller.
            // Si no, va a la app y controller "core".
            if ($theApp->hasController($po->aGet('class'))) {
                ?>
                <?php 
 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;
 }
 /**
  * generate
  * Genera la tabla para una clase y todas las tablas intermedias 
  * para sus relaciones hasMany de la que son suyas.
  * 
  * Si dalForApp es NULL se usa this->dal, de lo contrario se usa esa DAL.
  */
 private static function generate($ins, $dalForApp = NULL)
 {
     Logger::getInstance()->pm_log("TableGen::generate");
     // La DAL que se va a usar
     //$dal = $this->dal;
     //if ($dalForApp !== NULL) $dal = $dalForApp;
     $dal = $dalForApp;
     // FIXME: creo que siempre se pasa dalForApp, no hay porque setear null por defecto.
     // TODO: Si la tabla existe deberia hacer un respaldo y borrarla y generarla de nuevo.
     //DROP TABLE IF EXISTS `acceso`;
     // Si la clase tiene un nombre de tabla, uso ese, si no el nombre de la clase.
     $tableName = YuppConventions::tableName($ins);
     // Ya se sabe que id es el identificador de la tabla, es un atributo inyectado por PO.
     $pks = array(array('name' => 'id', 'type' => Datatypes::INT_NUMBER, 'default' => 1));
     /* EJEMPLO de la estructura que se debe crear.
        $cols = array(
                       array('name'     => 'name',
                             'type'     => Datatypes :: TEXT,
                             'nullable' => false),
                       // FK
                       array('name'     => 'ent_id',
                             'type'     => Datatypes :: INT_NUMBER,
                             'nullable' => true)
                     );
        */
     // =====================================================================================================
     //      $nullable = NULL; // Hay que determinar si el atributo es nullable.
     // Si es una clase de nivel 2 o superior y esta mapeado en la misma tabla que su superclase,
     // todos sus atributos (declarados en ella) deben ser nullables.
     // TODO: ahora no tengo una funcionalidad que me diga que atributos estan declarados en que
     // clase, por ahora le pongo que todos sus atributos sean nullables.
     // =====================================================================================================
     // FIXME: no sirve chekear por la clase porque la instancia que me pasan es un merge de todas las
     // subclases que se mapean en la misma tabla, asi que puede ser que parent_class sea POe igual
     // tenga que declarar nullables.
     // >> Solucion rapida <<, para los atributos de las subclases, en generateAll inyectarles
     //                         contraints nullables true.
     // Son iguales, no se sobreescribe el valor de "class" por el de la instancia real porque no interesa,
     // solo son instancias de merges de POs para una tabla.
     //Logger::getInstance()->log( "getClass: " . $ins->getClass() );
     //Logger::getInstance()->log( "GET_CLASS: " . get_class($ins) );
     //      if ( get_parent_class($ins) != PersistentObject &&
     //           self::isMappedOnSameTable($ins->getClass(), get_parent_class($ins)) )
     //      {
     //         $nullable = true;
     //      }
     // =====================================================================================================
     $cols = array();
     $attrs = $ins->getAttributeTypes();
     // Ya tiene los MTI attrs!
     foreach ($attrs as $attr => $type) {
         if ($attr !== 'id') {
             $cols[] = array('name' => $attr, 'type' => $type, 'nullable' => DatabaseNormalization::isSimpleAssocName($attr) ? true : $ins->nullable($attr));
         }
     }
     // ====================================================================================================
     // Sigue fallando, genera esto: (el vacio en nullable es el false)
     //  [5] => Array
     //  (
     //      [name] => entrada_id
     //      [type] => type_int32
     //      [nullable] =>
     //  )
     // Mientras que tengo esto en el objeto: (o sea la constraint nullable esta en true)
     //          [entrada_id] => Array
     //          (
     //              [0] => Nullable Object
     //                  (
     //                      [nullable:private] => 1
     //                  )
     //          )
     // El problema es que PO.nullable cuando es un atributo de referencia hasOne,
     // se va a fijar si el atributo hasOne es nullable, y en este caso el atributo
     // NO es nullable, lo que hace a la referencia no nullable.
     // SOLUCION!: Lo resuelvo fijandome si es un atributo de referencia, lo hago
     // nullable, si no me fijo en si es nullable en el PO.
     // =========================================================
     //Logger::struct( $cols, "=== COLS ===" );
     $dal->createTable2($tableName, $pks, $cols, $ins->getConstraints());
     // Crea tablas intermedias para las relaciones hasMany.
     // Estas tablas deberan ser creadas por las partes que no tienen el belongsTo, o sea la clase duenia de la relacion.
     // FIXME: si la relacion hasMany esta declarada en una superClase, la clase actual tiene la
     //        relacion pero no deberia generar la tabla de JOIN a partir de ella, si no de la
     //        tabla en la que se declara la relacion.
     $hasMany = $ins->getHasMany();
     foreach ($hasMany as $attr => $assocClassName) {
         //Logger::getInstance()->pm_log("AssocClassName: $assocClassName, attr: $attr");
         //if ($ins->isOwnerOf( $attr )) Logger::show("isOwner: $attr", "h3");
         //if ($ins->attributeDeclaredOnThisClass( $attr )) Logger::show("attributeDeclaredOnThisClass: $attr", "h3");
         // VERIFY, FIXME, TODO: Toma la asuncion de que el belongsTo es por clase.
         // Podria generar un problema si tengo dos atributos de la misma clase pero
         // pertenezco a uno y no al otro porque el modelo es asi.
         // Para casos donde no es n-n el hasMany, lo que importa es donde se declara la relacion,
         // no que lado es el owner. Para la n-n si es importante el owner.
         // Verifico si la relacion es hasMany n-n
         if ($ins->getClass() !== $assocClassName) {
             $hmRelObj = new $assocClassName(NULL, true);
             if ($hmRelObj->hasManyOfThis($ins->getClass())) {
                 if ($ins->isOwnerOf($attr)) {
                     self::generateHasManyJoinTable($ins, $attr, $assocClassName, $dal);
                 }
             } else {
                 if ($ins->attributeDeclaredOnThisClass($attr)) {
                     self::generateHasManyJoinTable($ins, $attr, $assocClassName, $dal);
                 }
             }
         } else {
             if ($ins->attributeDeclaredOnThisClass($attr)) {
                 self::generateHasManyJoinTable($ins, $attr, $assocClassName, $dal);
             }
         }
     }
 }
 /**
  * Devuelve HTML para edicion de un objeto como una tabla con 2 columnas, la primera de nombres de campos la segunda con campos con valores para modificar.
  */
 private static function display_edit(PersistentObject $po)
 {
     $res = '<table>';
     $attrs = $po->getAttributeTypes();
     foreach ($attrs as $attr => $type) {
         // Los atributos inyectados no se deberian poder editar!
         $res .= '<tr><td>';
         $res .= $attr;
         // TODO: Habria que ver si esto es i18n, deberia haber algun "display name" asociado al nombre del campo.
         $res .= '</td><td>';
         if ($po->isInyectedAttribute($attr)) {
             //$res .= $po->aGet($attr);
             $res .= self::field_to_html_show($attr, $type, $po->aGet($attr));
         } else {
             if (DatabaseNormalization::isSimpleAssocName($attr)) {
                 // Si es un fk a un id de un hasOne, quiero mostrar una lista de los posibles ids
                 // de la clase de la relacion HO que le puedo asignar, y como esto es create o edit,
                 // si tiene un valor actual, deberia quedar seleccionado en el select.
                 $currentValue = $po->aGet($attr);
                 // Puede ser NULL
                 $role = DatabaseNormalization::getSimpleAssocName($attr);
                 // email_id -> email
                 $relClass = $po->getAttributeType($role);
                 // Clase de la relacion HO
                 // Objetos que puedo tener relacionadoss
                 // Se puede en PHP 5.3.0...
                 //$list = $relClass::listAll(new ArrayObject()); // Objetos que podria tener asociados
                 // ... pero por las dudas ...
                 $list = call_user_func_array(array($relClass, 'listAll'), array(new ArrayObject()));
                 $select = '<select name="' . $attr . '"><option value=""></option>';
                 foreach ($list as $relObj) {
                     $sel = $currentValue == $relObj->getId() ? ' selected="true"' : '';
                     $select .= '<option value="' . $relObj->getId() . '"' . $sel . '>' . $relClass . '[' . $relObj->getId() . ']</option>';
                     // TODO: Si se tuviera un toString en la clase se mostraria mejor
                 }
                 $select .= '</select>';
                 $res .= $select;
             } else {
                 $maxStringLength = NULL;
                 if ($type === Datatypes::TEXT) {
                     $maxLengthConstraint = $po->getConstraintOfClass($attr, 'MaxLengthConstraint');
                     if ($maxLengthConstraint !== NULL) {
                         $maxStringLength = $maxLengthConstraint->getValue();
                     }
                 }
                 $res .= self::field_to_html_edit($attr, $type, $po->aGet($attr), $maxStringLength);
                 // Si el campo tiene errores, los muestro
                 if ($po->getErrors()->hasFieldErrors($attr)) {
                     $res .= '<div class="errors">';
                     $res .= self::fieldErrors($po, $attr);
                     $res .= '</div>';
                 }
             }
         }
         $res .= '</td></tr>';
     }
     $res .= '</table>';
     return $res;
 }