Como usar select dependientes en Symfony 1.4 con tres tablas

La mayoría de los ejemplos que aparecen en Internet sobre como codificar las funciones javascript y JQuery para select dependientes, suponen el caso de dos tablas. La tabla «padre», donde se selecciona un elemento de la lista desplegable, y luego que se elige este, aparecen las opciones disponibles de la tabla «hijo» en la segunda lista desplegable.
Pero que pasa cuando tengo tres tablas. La tabla 1, contiene campos que la relacionan con las tablas 2 y 3, en una relación «uno a muchos». A su vez, la tabla 2 sería la tabla «padre» de la tabla 3, que pasaría a ser la tabla «hijo».
Veamos la siguiente imagen que ilustra la relación:

symfony

Cuando creo un registro nuevo para la tabla 1, los valores de las tablas 2 y 3, aún no existen, y si se usan las funciones disponibles en la web, estas funcionan bien.
Pero, si quiero editar un registro existente de la tabla 1, el formulario _form.php en Symfony, debe mostrar los valores correctos de las tablas 2 y 3. En estos casos, los ejemplos de la web para dos tablas no funcionan.
Veamos mi caso, y la forma en que modifique una función JQuery, para que trabaje bien, tanto para los registros nuevos como para la edición de un registro existente.

A continuación les presento las tres tablas y sus relaciones:
Tabla 1 (simplificada):

Conflictos1:
 connection: doctrine
 tableName: conflictos_1
 columns:
 id:
 type: integer(4)
 fixed: false
 unsigned: false
 primary: true
 autoincrement: true

 id_sector_actividad:
 type: integer(4)
 fixed: false
 unsigned: false
 primary: false
 notnull: true
 autoincrement: false

 id_subsector_actividad:
 type: integer(4)
 fixed: false
 unsigned: false
 primary: false
 notnull: false
 autoincrement: false

 relations:

 SectorActividadCiuTa7:
 local: id_sector_actividad
 foreign: id
 type: one
 SubsectorActividadTa8:
 local: id_subsector_actividad
 foreign: id
 type: one

Tablas 2 y 3:


SectorActividadCiuTa7:
  connection: doctrine
  tableName: sector_actividad_ciu_ta7
  columns:
    id:
      type: integer(4)
      fixed: false
      unsigned: false
      primary: true
      autoincrement: true
    sector_actividad:
      type: string(255)
      fixed: false
      unsigned: false
      primary: false
      notnull: true
      autoincrement: false
  relations:
    Conflictos1:
      local: id
      foreign: id_sector_actividad
      type: many
    SubsectorActividadTa8:
      local: id
      foreign: id_sector
      type: many
SubsectorActividadTa8:
  connection: doctrine
  tableName: subsector_actividad_ta8
  columns:
    id:
      type: integer(4)
      fixed: false
      unsigned: false
      primary: true
      autoincrement: true
    id_sector:
      type: integer(4)
      fixed: false
      unsigned: false
      primary: false
      notnull: true
      autoincrement: false
    descripcion:
      type: string(255)
      fixed: false
      unsigned: false
      primary: false
      notnull: true
      autoincrement: false
  relations:
    SectorActividadCiuTa7:
      local: id_sector
      foreign: id
      type: one
    Conflictos1:
      local: id
      foreign: id_subsector_actividad
      type: many

La función AJAX – JQuery, en el partial _form.php en el módulo conflictos1, quedaría así:

<script type="text/javascript">
$(document).ready(function()
{
    $("#conflictos1_id_sector_actividad").change(function()
    {
        var id_sub = $(this).val();
        if(id_sub != '')
        {
            $.ajax
            ({
                type: "POST",
                url: '<?php echo url_for('conflictos/subsector'); ?>'+ '?id=' + id_sub,
                cache: false,
                data: "id_sub="+ id_sub,
                success: function(data)
                {
                    $("#conflictos1_id_subsector_actividad").html(data); // but it does not select the value of dropdown list.
                }
            });
        }
        else
        {
            $("#conflictos1_id_subsector_actividad").html("
<option value=''>-- No se ha seleccionado subsector --</option>

");
        }
        return false;
    });
});
</script>

En el formulario del partial _form.php en el módulo conflictos1, la parte correspondiente al campo id_subsector_actividad, quedaría así:

<?php if (!$form->getObject()->isNew()): ?>
         <?php echo $form['id_subsector_actividad'] ?>
    <?php endif; ?>
 <?php if ($form->getObject()->isNew()): ?>
             <select name="conflictos1[id_subsector_actividad]" id="conflictos1_id_subsector_actividad">

              <option value="" selected="selected">Seleccione sub-sector</option>
  <?php endif; ?>

La función que se llama desde la url: conflictos/subsector sería como sigue:

  public function executeSubsector()

    {
        $id_sub = $_POST['id_sub'];

       $this->subsec= Doctrine_Core::getTable('SubsectorActividadTa8')
                ->createQuery('a')
                ->where('a.id_sector = ?', $id_sub)
                ->execute();
    }

Luego, para los casos de edición de un registro existente, necesitamos algo de código adicional. Agregamos para el campo 'id_subsector_actividad'en el widget de conflictos1 la propiedad 'table_method'=>'Subsector' asociada a la función  public function Subsector()para ello creamos en el archivo SubsectorActividadTa8Table.class.php la siguiente función:

public function Subsector()

    {
         global $elsector;

         if (!is_null($elsector)){
       $id_sub=$elsector;
       $query= Doctrine_Query::create()
            ->select('a.id')
            ->from('SubsectorActividadTa8 a')
         ->innerJoin('a.Conflictos1 c')

           ->where('a.id_sector = ?', $id_sub);
               return $query->execute();
    }
    }

En la función executeEdit del archivo actions.class.php del módulo conflictos1 quedaría así:

public function executeEdit(sfWebRequest $request)
  {
 global $elsector;
    $this->forward404Unless($conflictos1 = Doctrine_Core::getTable('Conflictos1')->find(array($request->getParameter('id'))), sprintf('Object conflictos1 does not exist (%s).', $request->getParameter('id')));

    $elsector= $conflictos1->getIdSectorActividad();

    $this->form = new Conflictos1Form($conflictos1);

  }

Aquí capturamos en la variable $elsector el valor del campo id_sector_actividad que luego utilizamos en la función Subsector()

Instalar Symfony 1.4 en Windows XP

Obviamente que el primer paso para poder utilizar Symfony 1.4, es instalarlo en nuestra PC. Explicaré, paso a paso, como lo hice yo. Utilizo los siguientes programas, como herramientas complementarias (todos son gratuitos y se encuentran en la Web):

  1. NetBeans 7.2 (es suficiente para Symfony descargar la versión para PHP solo)
  2. MySQL Workbench
  3. Wamp
Detalle del proceso de instalación:

Primero que nada, instalemos Wamp (o cualquier otro servidor Apache que corra en Windows XP). Una vez que Wamp, queda instalado, se creó automáticamente, una carpeta de nombre C:\wamp\www

Para saber si Wamp quedó bien instalado y no hay conflictos, arrancamos el programa y aparecerá un icono con una letra W como el que muestra la figura siguiente. Cuando Wamp arranca el icono comienza mostrándose en color rojo, luego cambia al color naranja, y finalmente, si todo anda bien, queda en color verde.

wamp_inst

Luego, se descarga el archivo de instalación de Symfony desde:

http://http://symfony.com/legacy

A continuación, buscamos en este sitio, la versión que necesitamos (yo utilizo la 1.4.18) y la descargamos.

En nuestra PC, se crea una carpeta con el nombre del proyecto, una sub-carpeta de nombre “lib” dentro de esta otra sub-carpeta de nombre “vendor”

Ejemplo: C:\miproyecto\lib\vendor

En este lugar de mi disco duro, copio el archivo sympony 1.4.18.tgz y procedo a descomprimirlo (con WinZip o WinRAR, hacer: extraer aquí)

Luego se renombra la carpeta “symfony1.4.18” que se ha creado automáticamente, como “symfony

Para comprobar que mi servidor reúne los requisitos mínimos para correr aplicaciones desarrolladas con Symfony 1.4, debemos ejecutar el archivo:

check_configuration.php

Para ello voy a la carpeta: symfony\data\bin y allí encuentro el archivo check_configuration.php, lo copio a:

C:\wamp\www (para los que tienen wamp como servidor )

Escribimos en la barra de direcciones de nuestro navegador: http://localhost/check_configuration.php

Se deben cumplir los “Mandatory requirements

Instalamos ahora NetBeans. Este programa, es un recurso muy cómodo y eficiente, para desarrollar aplicaciones con Symfony 1.4.

Iniciamos NetBeans:

Vamos a: Tools|options|PHP elegir pestaña que dice “symfony” solo.

Le digo a NetBeans donde está el archivo “symfony” de mi proyecto. Está en:

C:\miproyecto\lib\vendor\symfony\data\bin\

Luego voy a la pestaña “general” y debo seleccionar el interprete de PHP:

Ejemplo (suponiendo el uso de Wamp):

C:\wamp\bin\php\php5.3.10\php.exe

Hacemos click en “ok”

Vamos ahora a “File|New Proyect

Seleccionamos: “PHP Aplication” luego “Next

Damos nombre al proyecto.

Sources Folder: Seleccionamos la carpeta creada para el proyecto

Default encoding: dejar ISO-8859-1 o UTF-8

Marcar: “Put NetBeans metadata…”

Next

Run As:

Project URL: por ejemplo: http://miproyecto.local

Next

Marcamos: Symfony PHP web Framework…

Click en Finish

Los usuarios de Wamp van a:

Apache|httpd.conf

Abrimos el archivo, vamos al final y agregamos las líneas que se muestran abajo. Se debe elegir un puerto específico para el proyecto por ejemplo: 8080


# Asegúrate de tener sólo una vez esta línea en su configuración
NameVirtualHost 127.0.0.1:8080

# Esta es la configuración de Jobeet
Listen 127.0.0.1:8080

DocumentRoot «E:\proyectos\conflictosnew\web»

DirectoryIndex index.php
<Directory «E:\proyectos\conflictosnew\web»>

AllowOverride All
Allow from All
</Directory>
Alias /sf E:/proyectos/conflictosnew/lib/vendor/symfony/data/web/sf
<Directory «E:\proyectos\conflictosnew\lib\vendor\symfony\data\web\sf»>
AllowOverride All
Allow from All
</Directory>

Luego procedemos a Grabar
Si se utiliza Xampp como servidor, el código a incorporar sería:


# Asegúrate de tener sólo una vez esta línea en su configuración
NameVirtualHost 127.0.0.1:8080

# Esta es la configuración de Jobeet
Listen 127.0.0.1:8080

DocumentRoot «E:/proyectos/conflictosnew/web»

DirectoryIndex index.php
<Directory «E:/proyectos/conflictosnew/web»>
AllowOverride All
Allow from All
Require all granted
</Directory>

Alias /sf E:/proyectos/conflictosnew/lib/vendor/symfony/data/web/sf
<Directory «E:/proyectos/conflictosnew/lib/vendor/symfony/data/web/sf»>
AllowOverride All
Allow from All
Require all granted
</Directory>

Luego, editar el archivo “hosts” de windows ubicado en:

C:\windows\system32\drivers\etc

Agregar:

127.0.0.1 miproyecto

Reiniciar Apache. Con Wamp sería: “Restart All Service

Para saber si todo el proceso de instalación de Symfony 1.4 ha sido correcto, iniciamos Wamp, abrimos un browser tal como Google Chrome, y en la barra de direcciones escribimos la dirección y el puerto que asignamos a nuestro proyecto en la PC. Por ejemplo: http://127.0.0.1:8050

Si todo se ha instalado bien, deberá aparecer una pantalla similar a la siguiente:

success

Slots y java script: la combinación perfecta

¿Que es un «slot» en Symfony 1.4?

Un slot es simplemente un recurso de Symfony que permite insertar fragmentos de código HTML o java script, en determinados módulos y plantillas.

El caso más simple (sin uso de JavaScript): los títulos de las páginas

Si tuviéramos un simple archivo HTML por fuera de Symfony, y quisiéramos que la página aparezca en el navegador con un título determinado, por ejemplo «Página principal«, escribiríamos el código del siguiente modo:

<html>
<head>
<title>Página principal</title>
</head>
<body>

..........

</body>
</html>

Para poder aplicar el caso del punto anterior, a una plantilla determinada, en Symfony 1.4 debemos utilizar los “slots”.

En la literatura desarrollada por los creadores de Symfony, se despliega el ejemplo de un slot para insertar el título de cada página, que como ya vimos, debe ir dentro de <head> <title>…….</title></head> Además, se contempla el caso en que  queremos un título específico para ciertas y determinadas páginas y uno general, para las demás páginas. Los pasos son los siguientes:

Procedemos a definir, bajo que sección y usando cuales etiquetas, deseamos que aparezca un slot en el archivo layout.php de la aplicación frontend El código quedaría  como sigue:

// Archivo: apps\frontend\templates\layout.php

<head>
<title>
<?php if (!include_slot('title')): ?>
Portal de Conflictividad de la Provincia de Córdoba
<?php endif; ?>
</title>

<title><?php include_slot('title') ?></title>

Vemos que en las líneas de la 4 a la 8, le decimos a Symfony que, si no existe un título definido a través de un slot llamado ‘title‘ directamente use como título por defecto: Portal de Conflictividad de la Provincia de Córdoba

Luego, en la línea 10, queda definido el lugar donde irá el slot para los títulos, llamado ‘title‘ Vemos que el contenido del slot queda encerrado entre los tags <title> </title> .

En la página donde quiero que aparezca un título específico, por ejemplo el título «Listado de Conflictos» en  indexSuccess.php escribo lo siguiente:

// Archivo: apps\frontend\modules\conflictos\templates\indexSuccess.php

<?php slot('title') ?>
<?php echo ('Listado de Conflictos') ?>
<?php end_slot(); ?>

Automáticamente, Symfony sabe que este slot debe ir en el encabezado de la página dentro de las etiquetas <title> </title> porque así lo dejamos definido en layout.php

¿Cómo insertar código java script en plantillas de Symfony 1.4?

La mayoría de los desarrollos en javascript,  constan por un lado de un archivo de extensión js  que contiene las funciones para un uso determinado, que generalmente se guarda en la carpeta web/js Luego debe hacerse el llamado a las funciones, desde una página o plantilla determinada. ¿Cómo insertar este llamado a la función si las plantillas de Symfony 1.4, no tienen una sección <head> </head> ?

En el mismo template indexSuccess.php necesitaba usar una función de javascript que me permitiera ordenar una tabla por una columna determinada, haciendo clic sobre el encabezado de dicha columna. Para ello, necesitaba usar en el template (pero solo en este template) una función incorporada en el archivo jquery.tablesorter.js

Copiamos el archivo que contiene las funciones de java script, en web/js de nuestra aplicación. Como requiere del uso de jQuery, debo también incorporar el archivo de esta aplicación.
Cargamos el archivo desde view.yml

// Archivo: apps\frontend\modules\config\view.yml
stylesheets:    [jquery-ui-1.8.16.custom.css]
javascripts:    jquery-ui-1.9.2.custom.min.js, jquery.tablesorter.js]

Incluímos la siguiente línea, dentro del head en el archivo layout.php :

<?php include_slot('ordenar_tabla') ?>

Finalmente, en el template indexSuccess.php escribimos el llamado a la función, mediante el slot ‘ordenar_tabla‘ Cuando Symfony encuentre este slot definido en indexSuccess.php  sabe que lo debe incorporar al <head> de la página, porque así lo especificamos en el archivo layout.php :

// Archivo: apps\frontend\modules\conflictos\templates\indexSuccess.php

<?php slot('ordenar_tabla') ?>
<script type="text/javascript">
$(document).ready(function()
{
$("#myTable").tablesorter({widgets: ['zebra']});
}
);
</script>
<?php end_slot(); ?>