Example #1
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 
 /**
  * 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);
             }
         }
     }
 }
 public function nullable($attr)
 {
     // Atributos inyectados no son nullables.
     if (self::isInyectedAttribute($attr)) {
         return false;
     }
     if (DatabaseNormalization::isSimpleAssocName($attr)) {
         $attr = DatabaseNormalization::getSimpleAssocName($attr);
     }
     if (isset($this->constraints[$attr])) {
         foreach ($this->constraints[$attr] as $constraint) {
             if (get_class($constraint) === 'Nullable') {
                 return $constraint->getValue();
             }
         }
     }
     return true;
     // Por defecto es nullable. Es mas facil para generar las tablas, ahora se pone en not null solo si hay una restriccion que lo diga.
 }
 /**
  * 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;
 }