Oracle Symfony en PHP Minor

Por Mladen Gogala

Oracle Database 11g y Symfony Web PHP pueden componer música muy agradable juntos, como se muestra en esta simple aplicación de muestra.

Publicado en julio de 2008

Symfony es un marco muy conocido model-view-controller (MVC) para crear aplicaciones Web rápida y eficientemente. Como muchas herramientas parecidas, utiliza un mapper objeto relacional (ORM) denominado Propel. En mi trabajo como DBA, se me pidió recientemente modificar una aplicación que había sido escrita utilizando Symfony y cuyo autor había dejado la empresa desde entonces. En el momento, yo era el único recurso en la empresa que conocía tanto PHP como Oracle, por lo tanto era la opción natural—a pesar de ser un DBA.

Para resumir la historia, aprender Symfony no fue un problema, y el marco me deslumbró con su simplicidad, versatilidad y facilidad de uso. ¿Por dónde debe empezar alguien que quiere aprender Symfony? Project Symfony cuenta con un sitio Web muy informativo y bien conservado, con documentación extensiva. Resulta ser que hay un libro sobre Symfony y también varios tutoriales—desafortunadamente todos fueron creados utilizando una base de datos MySQL. Esta es la razón, en esencia, por la cual decidí escribir este artículo sobre Symfony y Oracle. No se trata de un tutorial auténtico, pero espero que sea útil para aquellos que quieren utilizar Symfony con Oracle.

Luego, crearé una pequeña aplicación Web utilizando Oracle Database 11g y Symfony. La aplicación accederá al esquema SCOTT y a las tablas EMP y DEPT igualmente conocidas. La aplicación será totalmente funcional, aunque algo insustancial.

Puntos Esenciales

Symfony es una herramienta MVC que utiliza un ORM denominado Propel. Esto significa que Symfony hará lo siguiente por usted, durante el desarrollo de la aplicación:

Hay tres topologías comunes de implementación:

  • Mapear las tablas del sistema para la administración de base de datos relacional de Oracle a clases PHP5. También se crearán métodos básicos para crear, recuperar, actualizar y eliminar (CRUD).
  • Crear módulos que nos permitirán ver y controlar los datos, utilizando los métodos CRUD creados por Propel.

La necesidad de programación será mínima, a pesar de que uno debería conocer tanto PHP5 como Oracle antes de atreverse a utilizar PHP5 y el desarrollo de aplicaciones Oracle.

Como con cualquier aplicación Web, usted también necesita un servidor Web. El servidor Web utilizado para este artículo es Apache: httpd-2.2.3-11.el5_1.centos.3. El software de aplicaciones y la prueba Oracle Database 11g fueron instalados en Centos 5.0 por su similitud con Red Hat Linux. PHP5 respalda todas las versiones principales actualmente en uso. Uno puede fácilmente desarrollarse sobre Oracle Database 11g e implementarlo en Oracle Database 10g. Debo decir, sin embargo, que Oracle Database 11g es mucho más adecuado para aplicaciones Web que Oracle Database 10g por la característica Result Cache que permite que los resultados de una consulta ya ejecutada sean reutilizados sin la necesidad de reanalizar y ejecutar la consulta.

Con el fin de trabajar con PHP5, el modulo Apache correspondiente debe estar en un vínculo y debe estar instalado. PHP4 no tiene soporte de Symfony. Una manera fácil de instalar PHP5 con todos los módulos relevantes es descargar e instalar Zend Core para Oracle.

Symfony, por supuesto, necesita instalarse. Este artículo no tratará la instalación Symfony en detalle, ya que está perfectamente explicada en el sitio Web del producto. Es suficiente decir que Symfony se instala utilizando el servicio PEAR estándar. La última versión estable de Symfony es 1.1.0.

Por lo tanto, ¿cómo funciona Symfony? Symfony es un generador de aplicaciones, de manera que hay un elemento descriptivo en él. Usted debe primero describir los objetos Oracle, luego generar una aplicación sobre esos objetos y, finalmente, modificar la aplicación para cubrir nuestras necesidades. Para cada paso, Symfony proporciona una manera fácil de llevarlos a cabo.

Descripción de los Objetos

Para comenzar a describir un esquema Oracle en Symfony, primero debe describir la instancia Oracle utilizada para acceder al esquema. El componente que funciona es Propel, un ORM que mapea las descripciones de objeto relacional en las clases de objeto. Para cada tabla, se genera una clase y todos los CRUD son generados para esa clase. El lenguaje utilizado para describir los componentes en Symfony (y Propel, por supuesto) se denomina YAML, que es una versión simplificada de XML y recientemente ha ganado popularidad con los usuarios de lenguajes de scripting como PHP y Perl. La sintaxis de YAML es extremadamente simple, intuitiva y fácil de utilizar.

Sin más preámbulos, permítanme comenzar con nuestro proyecto. El primer paso es crear el directorio del proyecto e iniciarlo creando la estructura del subdirectorio, utilizando el comando symfony init-proj test_proj:

mgogala@TheBox> mkdir test_proj 
mgogala@TheBox> cd test_proj 
mgogala@TheBox> symfony init-proj test_proj 
>> dir+   /home/mgogala/test_proj/doc 
>> dir+   /home/mgogala/test_proj/log 
>> dir+   /home/mgogala/test_proj/test 
>> dir+   /home/mgogala/test_proj/test/functional 
>> dir+   /home/mgogala/test_proj/test/bootstrap 
>> file+   /home/mgogala/test_proj/test/bootstrap/functional.php 
>> file+   /home/mgogala/test_proj/test/bootstrap/unit.php 
>> dir+   /home/mgogala/test_proj/test/unit 
>> dir+   /home/mgogala/test_proj/config 
>> file+   /home/mgogala/test_proj/config/config.php 
>> file+   /home/mgogala/test_proj/config/databases.yml 
>> file+   /home/mgogala/test_proj/config/properties.ini 
>> file+   /home/mgogala/test_proj/config/rsync_exclude.txt 
>> file+   /home/mgogala/test_proj/config/schema.yml 
>> file+   /home/mgogala/test_proj/config/propel.ini 
>> dir+   /home/mgogala/test_proj/cache 
>> dir+   /home/mgogala/test_proj/plugins 
>> dir+   /home/mgogala/test_proj/data 
>> dir+   /home/mgogala/test_proj/data/model 
>> dir+   /home/mgogala/test_proj/data/sql 
>> file+   /home/mgogala/test_proj/symfony 
>> dir+   /home/mgogala/test_proj/batch 
>> dir+   /home/mgogala/test_proj/web 
>> dir+   /home/mgogala/test_proj/web/images 
>> file+   /home/mgogala/test_proj/web/.htaccess 
>> dir+   /home/mgogala/test_proj/web/js 
>> file+   /home/mgogala/test_proj/web/robots.txt 
>> dir+   /home/mgogala/test_proj/web/uploads 
>> dir+   /home/mgogala/test_proj/web/uploads/assets 
>> dir+   /home/mgogala/test_proj/web/css 
>> file+   /home/mgogala/test_proj/web/css/main.css 
>> dir+   /home/mgogala/test_proj/lib 
>> dir+   /home/mgogala/test_proj/lib/model 
>> dir+   /home/mgogala/test_proj/apps 
>> tokens  /home/mgogala/test_proj/config/properties.ini 
>> tokens  /home/mgogala/test_proj/config/propel.ini 
>> tokens  /home/mgogala/test_proj/config/propel.ini 
>> tokens  /home/mgogala/test_proj/config/config.php 
>> chmod 777 /home/mgogala/test_proj/cache 
>> chmod 777 /home/mgogala/test_proj/log 
>> chmod 777 /home/mgogala/test_proj/web/uploads 
>> chmod 777 /home/mgogala/test_proj/symfony 
>> chmod 777 web/uploads/assets 
mgogala@TheBox>

El primer subdirectorio que necesita nuestra atención es, por supuesto, config. Los archivos importantes en el directorio config son databases.yml, propel.ini, y schema.yml.

El primer archivo, databases.yml, dice a Symfony la ubicación de la instancia Oracle a la que debe conectarse. El archivo se ve así:

all: 
 propel: 
  class:   sfPropelDatabase 
  param: 
   phptype: oracle 
   host:   localhost 
   database: test11
   username: scott 
   password: tiger 

Este archivo es muy claro y puede utilizarse como plantilla. También necesitamos un archivo de descripción para Propel, nuestro ORM designado. El archivo de configuración se denomina propel.ini y se ve así:

propel.targetPackage    = lib.model 
propel.packageObjectModel = true 
propel.project       = test_proj 
propel.database      = oracle 
propel.database.createUrl = oracle://scott:tiger@localhost/test11
propel.database.url    = oracle://scott:tiger@localhost/test11
propel.addGenericAccessors = true 
propel.addGenericMutators = true 
propel.addTimeStamp    = false 

propel.schema.validate   = false 

; directories 
propel.home          = . 
propel.output.dir       = /home/mgogala/test_proj 
propel.schema.dir       = ${propel.output.dir}/config 
propel.conf.dir        = ${propel.output.dir}/config 
propel.phpconf.dir       = ${propel.output.dir}/config 
propel.sql.dir         = ${propel.output.dir}/data/sql 
propel.runtime.conf.file    = runtime-conf.xml 
propel.php.dir         = ${propel.output.dir} 
propel.default.schema.basename = schema 
.....

Este archivo contiene una descripción de las tablas y columnas que necesitamos para crear nuestra aplicación. El atributo autoIncrement para las columnas de clave primaria darán como resultado la creación de secuencias para implementar la característica autoincrement, presente en algunas otras bases de datos de manera nativa. Obviamente, este atributo tiene sentido solo en el caso de una clave primaria numérica. Symfony puede utilizar información sobre la clave externa para crear automáticamente una lista de valores. Esto trae aparejado una limitación bastante significativa: Symfony todavía no brinda soporte de claves externas multicolumn. Para cada clave primaria, Symfony define un método para buscar los datos en virtud de la clave primaria. Es por lo tanto importante incluir las claves primarias en la descripción del esquema.

Aquí se puede descargar un pequeño Perl script que puede ayudar con la generación del esquema .yml. El script es extremadamente simple y se utiliza así:

propel: 
 _attributes: 
  package: lib.model 
 emp: 
  empno: 
   type: integer 
   size: 22 
   primaryKey: true 
   autoIncrement: true 
  ename: 
   type: varchar(10) 
   size: 10 
  job: 
   type: varchar(9) 
   size: 9 
  mgr: 
   type: integer 
   size: 22 
   foreignTable: emp 
   foreignReference: empno 
  hiredate: 
   type: date 
   size: 7 
  sal: 
   type: numeric 
   size: 22 
  comm: 
   type: numeric 
   size: 22 
  deptno: 
   type: integer 
   size: 22 
   foreignTable: dept 
   foreignReference: deptno 
   onDelete: CASCADE 
  _indexes: 
   emp_deptno_i: 
    deptno 
   emp_mgr_i: 
    mgr 
 dept: 
  deptno: 
  deptno: 
   type: integer 
   size: 22 
   primaryKey: true 
   autoIncrement: true 
  dname: 
   type: varchar(14) 
   size: 14 
  loc: 
   type: varchar(13) 
   size: 13


El resultado es el archivo presentado arriba. Las aplicaciones Web casi nunca utilizan grandes cantidades de tablas; en general acceden a tres o cuatro. Es fácil ponerlos en la lista de indicadores -t. Se brinda ayuda en caso de ser necesaria:

mg> symfony_yaml -h 
symfony_yaml -> Produce YAML description of given tables for Symfony 
        app. generator. The output can be used as "schema.yml" 
        configuration file. 
   USAGE:symfony_yaml -u=<user> -p=<passwd> -t  
        -f <output> 
   OPTIONS: -u Oracle username (standard u/p@d syntax is supported) 
        -p Password for the above. 
        -d Database to connect to. 
        -t Tables to describe. 
        -ns Do not generate "autoIncrement: true" for the primary key. 
        -f Output file. 
        -h This screen. 
      
   ------- 
   Username and password are mandatory arguments. Default 
   for the output file is standard output. For help, 
   try -help or -h. 

Ahora que nuestros archivos de descripción están listos, el mapeo de objeto de Propel respecto de las tablas del sistema para la administración de base de datos relacional en clases objeto puede generarse emitiendo el comando symfony propel-build-model:

mgogala@TheBox> cd ~/test_proj/ 
mgogala@TheBox> symfony propel-build-model 
>> schema  converting "/home/mgogala/test_proj/config/schema.yml" to XML 
>> schema  putting /home/mgogala/test_proj/config/generated-schema.xml 
Buildfile: /usr/local/lib/php/symfony/vendor/propel-generator/build.xml 
[resolvepath] Resolved /home/mgogala/test_proj/config to /home/mgogala/
test_proj/config 

propel-project-builder > check-project-or-dir-set: 

propel-project-builder > check-project-set: 

propel-project-builder > set-project-dir: 

propel-project-builder > check-buildprops-exists: 

propel-project-builder > check-buildprops-for-propel-gen: 

propel-project-builder > check-buildprops: 

propel-project-builder > configure: 
   [echo] Loading project-specific props from /home/mgogala/test_proj/
config/propel.ini 
 [property] Loading /home/mgogala/test_proj/config/propel.ini 

propel-project-builder > om: 
 [phing] Calling Buildfile '/usr/local/lib/php/symfony/vendor/
propel-generator/build-propel.xml'
  with target 'om'
 [property] Loading /usr/local/lib/php/symfony/vendor/propel-generator/./
default.properties 

propel > check-run-only-on-schema-change: 

propel > om-check: 

propel > om: 
   [echo] +------------------------------------------+ 
   [echo] |                     | 
   [echo] | Generating Peer-based Object Model for  | 
   [echo] | YOUR Propel project! (NEW OM BUILDERS)! | 
   [echo] |                     | 
   [echo] +------------------------------------------+ 
[phingcall] Calling Buildfile '/usr/local/lib/php/symfony/vendor/
propel-generator/build-propel.xml'
 with target 'om-template' 
[property] Loading /usr/local/lib/php/symfony/vendor/propel-generator/./
default.properties 

propel > om-template: 
[propel-om] Target database type: oracle 
[propel-om] Target package: lib.model 
[propel-om] Using template path: /usr/local/lib/php/symfony/vendor/
propel-generator/templates 
[propel-om] Output directory: /home/mgogala/test_proj 
[propel-om] Processing: generated-schema.xml 
[propel-om] Processing Datamodel : JoinedDataModel 
[propel-om]  - processing database : propel 
[propel-om]   + emp 
[propel-om]       -> BaseEmpPeer [builder: SfPeerBuilder] 
[propel-om]       -> BaseEmp [builder: SfObjectBuilder] 
[propel-om]       -> EmpMapBuilder [builder: SfMapBuilderBuilder] 
[propel-om]       -> EmpPeer [builder: SfExtensionPeerBuilder] 
[propel-om]       -> Emp [builder: SfExtensionObjectBuilder] 
[propel-om]   + dept 
[propel-om]       -> BaseDeptPeer [builder: SfPeerBuilder] 
[propel-om]       -> BaseDept [builder: SfObjectBuilder] 
[propel-om]       -> DeptMapBuilder [builder: SfMapBuilderBuilder] 
[propel-om]       -> DeptPeer [builder: SfExtensionPeerBuilder] 
[propel-om]       -> Dept [builder: SfExtensionObjectBuilder] 

BUILD FINISHED 

Total time: 0.6661 seconds 
>> file-   /home/mgogala/test_proj/config/generated-schema.xml 
mgogala@TheBox>


El modelo se genera. Si observa cuidadosamente el resultado, verá que el primer paso es convertir el archivo YAML schema.yml en un archivo XML, y el último paso es mapear los objetos relacionales en un grupo de clases de objeto. Usted también puede generar el lenguaje para definición de datos necesario para crear los objetos Oracle al emitir el comando symfony propel-build-sql.

mgogala@TheBox> symfony propel-build-sql 
>> schema  converting "/home/mgogala/test_proj/config/schema.yml" to XML 
>> schema  putting /home/mgogala/test_proj/config/generated-schema.xml 
Buildfile: /usr/local/lib/php/symfony/vendor/propel-generator/build.xml 
[resolvepath] Resolved /home/mgogala/test_proj/config to /home/mgogala/
test_proj/config 

propel-project-builder > check-project-or-dir-set: 

propel-project-builder > check-project-set: 

propel-project-builder > set-project-dir: 

propel-project-builder > check-buildprops-exists: 

propel-project-builder > check-buildprops-for-propel-gen: 

propel-project-builder > check-buildprops: 

propel-project-builder > configure: 
   [echo] Loading project-specific props from /home/mgogala/test_proj/
config/propel.ini 
 [property] Loading /home/mgogala/test_proj/config/propel.ini 

propel-project-builder > sql: 
 [phing] Calling Buildfile '/usr/local/lib/php/symfony/vendor/
propel-generator/build-propel.xml'
   with target 'sql' 
 [property] Loading /usr/local/lib/php/symfony/vendor/propel-generator/./
default.properties 

propel > check-run-only-on-schema-change: 

propel > sql-check: 

propel > pgsql-quoting-check: 

propel > sql: 
   [echo] +------------------------------------------+ 
   [echo] |                     | 
   [echo] | Generating SQL for YOUR Propel project! | 
   [echo] |                     | 
   [echo] +------------------------------------------+ 
[phingcall] Calling Buildfile '/usr/local/lib/php/symfony/vendor/
propel-generator/build-propel.xml'
  with target 'sql-template' 
 [property] Loading /usr/local/lib/php/symfony/vendor/propel-generator/./
default.properties 

propel > sql-template: 
[propel-sql] Processing: generated-schema.xml 
[propel-sql] Writing to SQL file: /home/mgogala/test_proj/data/sql/
lib.model.schema.sql 
[propel-sql]  + emp [builder: OracleDDLBuilder] 
[propel-sql]  + dept [builder: OracleDDLBuilder] 

BUILD FINISHED 

Total time: 0.3073 seconds 
>> file-   /home/mgogala/test_proj/config/generated-schema.xml 
mgogala@TheBox>

El archivo generado se denomina /home/mgogala/test_proj/data/sql/lib.model.schema.sql. Se ve así:

/* ----------------------------------------------------------------------- 
  emp 
  ----------------------------------------------------------------------- */ 

DROP TABLE "emp" CASCADE CONSTRAINTS; 

DROP SEQUENCE "emp_SEQ"; 


CREATE TABLE "emp" 
( 
    "empno" NUMBER(22) NOT NULL, 
    "ename" VARCHAR2(10), 
    "job" VARCHAR2(9), 
    "mgr" NUMBER(22), 
    "hiredate" DATE(7), 
    "sal" NUMBER(22), 
    "comm" NUMBER(22), 
    "deptno" NUMBER(22) 
); 
    ALTER TABLE "emp" 
        ADD CONSTRAINT "emp_PK" 
    PRIMARY KEY ("empno"); 
CREATE INDEX "emp_deptno_i" ON "emp" (); 
CREATE INDEX "emp_mgr_i" ON "emp" (); 
CREATE SEQUENCE "emp_SEQ" INCREMENT BY 1 START WITH 1 NOMAXVALUE 
NOCYCLE NOCACHE ORDER; 
CREATE INDEX "emp_deptno_i" ON "emp" (); 
CREATE INDEX "emp_mgr_i" ON "emp" (); 

ALTER TABLE "emp" ADD CONSTRAINT "emp_FK_1" FOREIGN KEY ("mgr") 
REFERENCES "emp" ("empno"); 

ALTER TABLE "emp" ADD CONSTRAINT "emp_FK_2" FOREIGN KEY ("deptno") 
REFERENCES "dept" ("deptno")
 ON DELETE CASCADE;

Este script fue generado solo para fines ilustrativos, y es desaconsejable utilizarlo. Debido al uso extensivo de comillas dobles, todos los nombres de tablas y nombres de columnas se generarán en minúscula. En otras palabras, Symfony no es una herramienta CASE; no tiene el fin de desarrollar un esquema Oracle desde un archivo de descripción YAML. Asimismo, por favor fíjese que el SQL generado contiene sentencias DROP, lo cual significa que no debería ejecutarlo en tablas ya existentes. Los archivos PHP que implementan el mapeo son de alguna manera más complejos y serán explicados más adelante.

El único uso práctico de este script es verificar si la visualización de Symfony respecto del modelo de datos coincide con la situación real. En nuestro caso, coincide. Las aplicaciones son adecuadas para ser generadas por un generador de aplicaciones solo si el generador de aplicaciones subyacente puede describir adecuadamente el modelo de datos subyacente. Este principio general se aplica no solo a Symfony sino también a Django, Ruby on Rails, Jifty, o cualquier otra herramienta similar. (si el modelo de datos es muy complejo, se necesitan herramientas CASE profesionales como Oracle Business Process Analysis Suite). Symfony y sus elementos relacionados de código abierto son útiles para generar aplicaciones relativamente simples con mucha rapidez, pero no pueden reemplazar las herramientas CASE significativas necesarias para procesos como planificación de recursos empresariales o un buen modelado financiero. Nuestro esquema, sin embargo, se incluye en la categoría "simple".

Ahora que describimos su esquema, usted puede generar nuestra aplicación.

Generar la Aplicación

Primero, debería generar el marco de la aplicación al emitir symfony init-app <app name>. Llame a la aplicación "frontend".

mgogala@TheBox> symfony init-app frontend 
>> dir+   /home/mgogala/test_proj/apps/frontend/templates 
>> file+   /home/mgogala/test_proj/apps/frontend/templates/layout.php 
>> dir+   /home/mgogala/test_proj/apps/frontend/modules 
>> dir+   /home/mgogala/test_proj/apps/frontend/config 
>> file+   /home/mgogala/test_proj/apps/frontend/config/filters.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/view.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/config.php 
>> file+   /home/mgogala/test_proj/apps/frontend/config/cache.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/settings.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/factories.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/security.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/routing.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/logging.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/app.yml 
>> file+   /home/mgogala/test_proj/apps/frontend/config/i18n.yml 
>> dir+   /home/mgogala/test_proj/apps/frontend/i18n 
>> dir+   /home/mgogala/test_proj/apps/frontend/lib 
>> file+   /home/mgogala/test_proj/apps/frontend/lib/myUser.class.php 
>> tokens  /home/mgogala/test_proj/apps/frontend/config/settings.yml 
>> file+   /home/mgogala/test_proj/web/frontend.php 
>> tokens  /home/mgogala/test_proj/web/frontend_dev.php 
>> tokens  /home/mgogala/test_proj/web/frontend.php 
>> chmod 777 /home/mgogala/test_proj/cache 
>> chmod 777 /home/mgogala/test_proj/log 
>> chmod 777 /home/mgogala/test_proj/web/uploads 
>> chmod 777 /home/mgogala/test_proj/symfony 
>> chmod 777 web/uploads/assets 
mgogala@TheBox>


El comando fue emitido desde la parte superior de la estructura del directorio del proyecto, como es el caso con todos los comandos Symfony. Ahora, usted debe generar módulos. Esto se realiza emitiendo el comando symfony propel-init-admin <app name> <table name> <module name>, para cada tabla (EMP y DEPT, en este caso):

mgogala@TheBox> symfony propel-init-admin frontend emp Emp 
>> dir+   /home/mgogala/test_proj/apps/frontend/modules/emp/config 
>> file+   /home/mgogala/test_proj/apps/fr...odules/emp/config/generator.yml 
>> dir+   /home/mgogala/test_proj/apps/frontend/modules/emp/actions 
>> file+   /home/mgogala/test_proj/apps/fr...s/emp/actions/actions.class.php 
>> tokens  /home/mgogala/test_proj/apps/fr...odules/emp/config/generator.yml 
>> tokens  /home/mgogala/test_proj/apps/fr...s/emp/actions/actions.class.php 
mgogala@TheBox> symfony propel-init-admin frontend dept Dept 
>> dir+   /home/mgogala/test_proj/apps/frontend/modules/dept/config 
>> file+   /home/mgogala/test_proj/apps/fr...dules/dept/config/generator.yml 
>> dir+   /home/mgogala/test_proj/apps/frontend/modules/dept/actions 
>> file+   /home/mgogala/test_proj/apps/fr.../dept/actions/actions.class.php 
>> tokens  /home/mgogala/test_proj/apps/fr...dules/dept/config/generator.yml 
>> tokens  /home/mgogala/test_proj/apps/fr.../dept/actions/actions.class.php 

Eso es todo: ¡Ha generado una aplicación simple! Ahora, puede observarla visitando http://localhost/emp/list en su browser. Esto, por supuesto, es solo el comienzo. Usted puede configurar la paginación, los nombres de columna, los formatos de columna e incluso agregar los widgets de búsqueda al modificar el archivo de configuración para el generador del módulo generator.yml para cada módulo. Este archivo reside en el directorio, en su directorio de proyectos: test_proj/apps/frontend/modules/emp/config. Usted ha generado un modulo por tabla, y por lo tanto tiene un emp. del módulo.

Aquí hay una versión muy simple: la utilizada para generar la aplicación básica de abajo:

generator: 
 class:       sfPropelAdminGenerator 
 param: 
  model_class:   Emp 
  theme:      default 
  list: 
    title: Company Employees 
    max_per_page: 7 
    filters: [ ename ] 
    fields: 
      hiredate: { params: date_format='MM/dd/yy' } 
      ename: { name: Employee Name } 


Tenga cuidado al modificar el archivo generator.yml; es jerárquico y está indentado con espacios. Hay un buen tutorial para utilizar el generador admin en el sitio Web del proyecto Web.

Así se ve el resultado:

Imagen 1

Esta es tan solo una pequeña aplicación básica, pero ahora tiene algo con que divertirse—y solo tomó cinco minutos crearla.

Observe que el título de la página es Company Employees (Empleados de la Empresa), que la columna ENAME se despliega como Nombre de Empleado, y que la columna HIREDATE tiene el formato MM/DD/YY, tal como fue especificado en el archivo generator.yml de arriba. Solo hay siete empleados en la página y hay enlaces para las páginas siguiente, anterior, primera y última. Asimismo, el campo de clave primaria está claramente marcado como enlaces. Hacer click en esos enlaces abrirá la pantalla de modificación, que puede utilizarse para modificar el registro en cuestión. También hay un pequeño widget que permite buscar empleados por nombre. El botón CREATE ofrece la funcionalidad INSERT y ayuda a ingresar nuevos registros.

Modificar el SQL Subyacente

Los mappers objeto relacional mapean sentencias SQL en clases de objeto. Esto significa que, a fin de modificar las sentencias SQL emitidas por nuestra aplicación, debemos modificar el código generado por el generador de la aplicación. Symfony genera el código en el subdirectorio en cache del directorio principal del proyecto de la misma manera metódica. Las acciones de nuestras clases de objeto mapeadas están definidas en el archivo denominado actions.class.php, que reside en el directorio test_proj/cache/frontend/prod/modules/autoEmp/actions—el caché para el módulo emp. Este archivo es un PHP script, con definiciones de clase. Los métodos en estas clases pueden anularse utilizando el archivo provisto por Symfony.

El nombre de archivo es actions.class.php, pero reside en el árbol de definición de la aplicación, en el directorio apps/frontend/modules/emp/actions en lugar de en el árbol en caché. El archivo proporcionado se ve así:

<?php

/**
 * emp actions.
 *
 * @package  test_proj
 * @subpackage emp
 * @author   Your name here
 * @version  SVN: $Id: actions.class.php 2288 2006-10-02 15:22:13Z fabien $
 */
class empActions extends autoempActions
{
}


En otras palabras, es un fragmento vacío. A fin de modificar el SQL y cambiar el comportamiento, usted necesita modificar este archivo. A continuación, mostramos cómo se ve el archivo modificado, para nuestra aplicación de muestra:

<?php
// auto-generated by sfPropelAdmin
// date: 2008/04/11 12:44:09
?>

 * @version  SVN: $Id: actions.class.php 7997 2008-03-20 12:29:34Z noel $
 */
class empActions extends autoempActions
{
 public function executeList()
 {
  $this->processSort();

  $this->processFilters();

  $this->filters = $this->getUser()->getAttributeHolder()->
getAll('sf_admin/emp/filters');

  // pager
  $this->pager = new sfPropelPager('Emp', 20);
  $c = new Criteria();
  $c ->addAscendingOrderByColumn(EmpPeer::DEPTNO);
  $this->addSortCriteria($c);
  $this->addFiltersCriteria($c);
  $this->pager->setCriteria($c);
  $this->pager->setPage($this->getRequestParameter('page', 1));
  $this->pager->init();
 }

 public function addFiltersCriteria($c)
 {
  if (isset($this->filters['ename_is_empty']))
  {
   $criterion = $c->getNewCriterion(EmpPeer::ENAME, '');
   $criterion->addOr($c->getNewCriterion(EmpPeer::ENAME, null, 
Criteria::ISNULL));
   $c->add($criterion);
  }
  else if (isset($this->filters['ename']) && $this->filters['ename'] !== '')
  {
   $c->add(EmpPeer::ENAME, "ENAME LIKE UPPER('".$this->filters['ename']."%')",
 Criteria::CUSTOM);
  }
 }
}

El código es copiado de la versión en el árbol en caché y es modificado para ofrecer un comportamiento diferente. El código mostrado en negrita clasifica los datos por deptno y se asegura de que la búsqueda por columna ENAME no sea sensible a mayúsculas y minúsculas. Modificar la clase de criterio no es tan complicado como parece. Hay muchas funciones bien documentadas para ingresar nuestro propio SQL, como hice con la clase filtro, para modificar las columnas de clasificación y hacer muchas cosas diferentes. Ahora, hay solo algo más por hacer:

cd ~/test_proj; symfony cc 

Esto limpiará el caché (eliminará todo del árbol de caché) y lo regenerará desde el árbol de aplicaciones en la próxima invocación. Voila, nuestra aplicación está lista. Hacer referencia a la URL http://localhost/emp/list regenerará automáticamente la aplicación y reemplazará el comportamiento por defecto por el de arriba.

Conclusión

Symfony es un generador amplio y complejo de aplicaciones con muchas opciones, y describirlo en un artículo relativamente breve no es fácil. La mayor parte del trabajo se realiza con la creación de los archivos de descripción YAML, pero ciertamente hay algunas partes "que requieren armado". Esta herramienta funciona perfectamente con control de versiones, hay numerosas opciones adicionales, y es bastante fácil de aprender. La versión actual, 1.0.15, tiene dos grandes limitaciones: la complejidad de Propel ORM y la falta de soporte para claves externas multicolumn.

En un futuro cercano, Propel ORM en Symfony será reemplazado por Doctrine. Doctrine fue inspirado por el marco Java Hibernate y su Hibernate Query Language (HQL).

Symfony versión 1.1 está actualmente en la fase RC1 (candidato de primer lanzamiento). Oracle Database 11g y Symfony son una combinación excelente, porque juntos facilitan la creación de una aplicación de alto desempeño que tendrá un buen aspecto, buena escala, y cubrirá las necesidades de los clientes con muy poco esfuerzo. Espero que sepa aprovechar ambos.


Mladen Gogala es DBA de Oracle hace mucho tiempo, y es el autor de Easy Oracle PHP (2006). Tiene una vasta experiencia en UNIX scripting, optimización Oracle y administración de sistemas UNIX, y utiliza todos los posibles dialectos de UNIX y Linux. Mladen Gogala también tiene una participación active en los foros Usenet de Oracle, donde brinda su ayuda para responder preguntas sobre Oracle.