lunes, 24 de octubre de 2011

sfDependentSelectPlugin en relaciones Muchos a Muchos M:N

Bien.. imaginemos que tenemos 2 tablas que se relacionan de M:N es decir, exite una tabla intermedia con las llaves primarias de las 2 tablas principales. en mi caso: centro y motivo_egreso y la tabla intermedia motivo_egreso_centro.


para usar sfDependentSelectPlugin y mostrar en mi caso, motivo de egreso y que se listen los centros asociados a través de una tabla intermedia , la única forma que conseguí de hacerlo es la siguiente: en lib/form/Doctrine/TuClaseForm.class.php
     public function configure()
     {
     ................. 
     ................. 
     ................. 
      $this->widgetSchema['motivo_egreso_id'] = new sfWidgetFormDoctrineChoice(array(
            'model'     => 'MotivoEgreso',
            'add_empty' => '<- Seleccione ->',
            'method' => 'getDescripcion',
            'label' => 'Motivo de Egreso :',
        ));

        $this->validatorSchema['motivo_egreso_id'] = new sfValidatorDoctrineChoice(array(
            'model' => 'MotivoEgreso',
            'column' => 'id',
            'required' => true
        ),array('required'=>'Campo requerido'));

        $this->widgetSchema['centro_id_egreso'] = new sfWidgetFormArrayDependentSelect(array(
            'callable' => array('CentroTable', 'getCentrosByMotivoEgreso'),
            'depends' => 'motivo_egreso_id',
            'add_empty' => '<- Seleccione ->',
        ));

         $this->validatorSchema['centro_id_egreso'] = new sfValidatorChoice(array(
            'choices' => CentroTable::getCentrosByMotivoEgreso(),
            'required' => true
        ),array('required'=>'Campo requerido'));
     ................. 
     ................. 
     ................. 
Luego defines el metodo getCentrosByMotivoEgreso() en mi caso en lib/model/doctrine/CentroTable.class.php
class CentroTable extends Doctrine_Table
{
    public static function getInstance()
    {
        return Doctrine_Core::getTable('Centro');
    }
     ................. 
     ................. 
     .................    
    
    public static function getCentrosByMotivoEgreso()
    {
        $motivos = Doctrine_Core::getTable('MotivoEgreso')
                    ->createQuery('m')
                    ->innerJoin('m.MotivoEgresoCentro mc')
                    ->innerJoin('mc.Centro c')
                    ->orderBy('m.id,c.id asc' )
                    ->execute();
        $arr = array();$arr_centro=array();
        foreach($motivos as $motivo){
            foreach($motivo->getCentro() as $centro)
                $arr_centro = $arr_centro + array($centro->getId() => $centro->getDescripcion());    
            $arr = $arr + array($motivo->getId()=>$arr_centro);
            $arr_centro=array();
        }
        return $arr;
    }
     ................. 
     ................. 
     ................. 
}
Bien, la idea que conseguí la obtuve de http://www.symfony-project.org/plugins/sfDependentSelectPlugin, en la seccion de "Using arrays only" decidí crear un array con la consulta que necesitaba para realizar los combos dependientes... repito es la única forma que conseguí de hacer eso, quizás exista una mejor manera, espero sus comentarios! Pd: el widget que deben usar en el form es sfWidgetFormArrayDependentSelect y ahi si utilizas el arreglo generado! Listo a probar...

lunes, 25 de julio de 2011

sfDependentSelectPlugin - problema con nombre de los campos

Cuando el combo superior no cumple con el estandar de nombre....

Ejemplo:
si tenemos un campo de referencia en una tabla que no cumple con el nombre estandar ej. direccion_id y por el contrario tenemos algo como receptor_direccion_id
los combos dependientes no funcionan.. al menos no a mi... hay q hacer 3 sencillos pasos...


1.- debes modificar 2 cosas proy/plugins/sfDependentSelectPlugin/sfWidgetFormDependentSelect.class

// en la funcion configure agregar una nueva opcion widget
protected function configure($options = array(), $attributes = array())
{
$this->addOption('depends', '');
$this->addOption('widget', '');//nueva opcion widget
$this->addOption('add_empty', true);
// ajax
$this->addOption('ajax', false);
$this->addOption('cache', true);
$this->addOption('params', array());
$this->addOption('url', sfContext::getInstance()->getController()->genUrl('sfDependentSelectAuto/_ajax'));
// source
$this->addRequiredOption('source_class', '');
$this->addOption('source_params', array());

parent::configure($options, $attributes);
}
2.- luego
//en la funcion render modificar la opcion 'dependiente'
public function render($name, $value = null, $attributes = array(), $errors = array()) 
{
.....
......
...........
$config = array(
'id'          => $this->generateId($name),
'opciones'    => $values,
'vacio'       => true === $this->getOption('add_empty') ? '' : $this->getOption('add_empty'),
'ajax'        => $this->getOption('ajax'),
'url'         => $this->getOption('url'),
'cache'       => $this->getOption('cache'),
'dependiente' => ($this->getOption('widget') == '' ? $this->getOption('depends') : $this->getOption('widget')),//validamos el valor q viene en la opcion
'varref'      => '_ds_ref',
'varsoloref'  => '_ds_get_ref_value',
'params'      => array_merge($this->getOption('params'), array(
'_ds_id'            => $this->generateId($name),
'_ds_source_class'  => $sourceClass,                                    
'_ds_source_params' => $this->getOption('source_params'),
)),            
);

.....
......
...........

return $widget . $script;
}
3.- por ultimo en /proy/lib/form/doctrine/CorrespondenciaForm.class agregas la opcion q acabas de crear... y listo! cuando no necesites
eso solo usas la opcion depends
$this->validatorSchema['receptor_direccion_id'] = new sfValidatorDoctrineChoice(array(
'model' => $this->getRelatedModelName('Direccion'),
'required' => true), array('required' => 'Campo Obligatorio'));

$this->widgetSchema['receptor_persona'] = new sfWidgetFormDoctrineDependentSelect(array(
'model'     => 'Persona',
'depends'   => 'Direccion',
'widget'   => 'correspondencia_receptor_direccion_id', // nombre del campo renderizado en el form (combo superior)
'add_empty' => '<- Seleccione receptor ->',
'method' => '__toString',
));

y listo!!

miércoles, 29 de junio de 2011

Unificar Documentos de identidad en un solo campo

Luego de varios intentos de registrar en BD documentos de identidad un buen amigo me paso un código que probablemente solucione el tema de unificar criterios en cuanto a números de documentos de identificación para una estructura de datos que contendrá información de personas.. Típico que necesitas registrar en tu aplicación datos personales, incluyendo documentos legales de identificación, normalmente podrías generar un solo campo tipo text para meter ahí lo que sea, o crear diferentes campos para guardar la info del documento de identidad (Cédula de identidad, pasaporte, etc)

Bien hace un tiempo me mandaron esta solución pero no sabia donde la había dejado, la volví a encontrar me pareció interesante y la voy a aplicar en un proyecto nuevo.. prueben este PHP y verifiquen lo generado, la idea es generar un único campo para guardar el dato de la identificación de la persona que se genera...
$str = "V158202995";
echo 'cadena enviada= '.$str;
$arr2 = str_split($str, 1);
foreach ($arr2 as $array):
       echo 'valor= '.$array.' codigo ascii= '.ord($array);
       $asci.=ord($array);
endforeach;
echo 'cadena resultante en codigo ascii= '.$asci;
$asci2 = str_split($asci, 2);
foreach ($asci2 as $array):
       echo 'valor en codigo ascii= '.$array.' valor normal= '.chr($array);
       $asc.=chr($array);
endforeach;
echo 'cadena reconstruida= '.$asc; 

el resultado de lo ejecutado sera asi:
cadena enviada= V158202995
valor= V codigo ascii= 86
valor= 1 codigo ascii= 49
valor= 5 codigo ascii= 53
valor= 8 codigo ascii= 56
valor= 2 codigo ascii= 50
valor= 0 codigo ascii= 48
valor= 2 codigo ascii= 50
valor= 9 codigo ascii= 57
valor= 9 codigo ascii= 57
valor= 5 codigo ascii= 53
cadena resultante en codigo ascii= 86495356504850575753
valor en codigo ascii= 86 valor normal= V
valor en codigo ascii= 49 valor normal= 1
valor en codigo ascii= 53 valor normal= 5
valor en codigo ascii= 56 valor normal= 8
valor en codigo ascii= 50 valor normal= 2
valor en codigo ascii= 48 valor normal= 0
valor en codigo ascii= 50 valor normal= 2
valor en codigo ascii= 57 valor normal= 9
valor en codigo ascii= 57 valor normal= 9
valor en codigo ascii= 53 valor normal= 5
cadena reconstruida= V158202995
Luego se guarda en BD el ascii generado... Podrias generar un metodo para hacer la transformacion y el reverso del numero y lo invocas antes de llamar al metodo save()

Y listo!

martes, 14 de junio de 2011

Exportar a Excel en Symfony

Típico que necesitas exportar a una hoja de calculo el resultado de alguna consulta..
bien, es simple, solo debes indicar en el action que no usaras el layout y que vas a cambiar el tipo de salida de la respuesta..
Primero, supongamos que vamos a enviar a una hoja de calculo nuestro Index, podriamos generar un action para realizar esta tarea.
En el index agragamos un vinculo a nuestra accion

<a class="ods" href="<?php echo url_for('item/indexExport') ?>">Exportar</a>

Y la accion seteamos el layout en false y modificamos el contentype del response...
public function executeIndexExport(sfWebRequest $request)
  {
   $this->items = Doctrine_Core::getTable('Item')
      ->createQuery('a')
      ->orderBy('a.categoria_id,a.descripcion')
      ->execute();
   $this->setLayout(false);
   $this->getResponse()->setContentType('application/msexcel');
   $this->setTemplate('index');
  }

luego... ejecutas, pruebas y guardas el archivo generado y listo!

lunes, 6 de junio de 2011

Comenzar un proyecto en Symfony 1.4 (part.2)

bien.. ya sabemos como comenzar un proyecto Symfony
Ahora veamos el resto de los pasos.. comencemos desde el inicio:
$ cd /var/www/
$ mkdir -p proy
$ cd proy/
$ mkdir -p lib/vendor
$ cd lib/vendor
$ tar zxpf /home/usuario/Escritorio/symfony-1.4.4.tgz 
$ mv symfony-1.4.4/ symfony
$ symfony -V
$ symfony generate:project proy
Verifica el post para crear el Comendo Symfony
PARA SEGURIDAD CONTRA ATAQUES XSS Y CSRF
symfony generate:app --escaping-strategy=on --csrf-secret=ClaveUnicaSecreta frontend
Ahora creamos el Virtual Host para nuestro proyecto proy

En el navegador
http://localhost.proy/
y para ver la aplicacion funcionando en modo desarrollo
http://localhost.proy/frontend_dev.php

Creando al estructura de datos
$ sudo su
# su - postgres
$ psql
postgres=#  create database proy_db with owner u_test;
postgres=#  CREATE TABLE persona
(
  id serial NOT NULL,
  nacionalidad character(1),
  ci numeric(10),
  nombre character varying(100) NOT NULL,
  apellido character varying(100) NOT NULL,
  sexo character(1),
  fecha_nac date,
  created_at timestamp without time zone,
  updated_at timestamp without time zone,
  CONSTRAINT persona_pkey PRIMARY KEY (id),
  CONSTRAINT persona_ci_key UNIQUE (ci)
);
ALTER TABLE persona OWNER TO u_test;
postgres=#  \q
$ exit
# exit
/// Estando nuevamente en /var/www/proy ///
$ symfony configure:database "pgsql:host=localhost;dbname=proy_db" u_test 123456
$ symfony doctrine:build-schema
//SI TIENES CAMPOS AUTO ADMINSTRABLES created_at updated_at
//debes ejecutar el comando para el schema
//buscar en el schema los campos created_at updated_at ELIMINALOS y colocar al inicio de la clase del schema actAs: { Timestampable: ~ }
Persona:
connection: doctrine
tableName: persona
actAs: { Timestampable: ~ }
columns:
id:.....
.....
luego ejecutas:
$ symfony doctrine:build --model
$ symfony doctrine:build --filters
$ symfony doctrine:build --forms
o en todo caso
$ symfony doctrine:build --all --no-confirmation
//modificar proy/lib/form/doctrine/PersonaForm.class.php y agregas lo siguiente dentro del metodo configure()
public function configure()
  {
 unset($this['created_at']);
        unset($this['updated_at']);
        .....
        ...
        ..
  }

De la sigueinte manera se crean CRUD (CREATE,RETRIEVE,UPDATE,DELETE) para los modulos de la aplicacion
// symfony doctrine:generate-module --with-show --non-verbose-templates aplicacion modulo Modelo
en nuestro caso el CRUD para la clase persona
$ symfony doctrine:generate-module --with-show --non-verbose-templates frontend persona Persona

y ahora a probar...
http://proy.localhost/frontend_dev.php/persona

Comenzar un proyecto en Symfony 1.4

Aqui dejo unos sencillos pasos para comenzar a configurar un proyecto con symfony 1.4 con ORM Doctrine

Descargar la version de symfony que desee utilizar
ej. symfony 1.4.4

Url de consulta
http://www.symfony-project.org/jobeet/1_4/Doctrine/es/01

Para descargar la version 1.4.4
http://www.symfony-project.org/installation/1_4
Descargar symfony-1.4.4.tgz

Crear proyecto
$ cd /var/www/
$ mkdir proy
$ cd /proy
$ mkdir -p lib/vendor
$ tar zxpf /home/usuario/Escritorio/symfony-1.4.4.tgz
$ mv symfony-1.4.4/ lib/vendor/symfony
Verifica la version y la configuracion
$ php lib/vendor/symfony/data/bin/symfony -V
$ php lib/vendor/symfony/data/bin/check_configuration.php

Para acceder al symfony q estamos instalando es necerio ejecutar
$ php lib/vendor/symfony/data/bin/symfony -- COMANDOS --
Para mejorar esto crearemos un comando en el shell:
$ vim $HOME/.bashrc
// o usas gedit
$ gedit $HOME/.bashrc

agregamos al final del archivo
alias symfony='./lib/vendor/symfony/data/bin/symfony'
Guardamos los cambios y vamos a OTRA CONSOLA.. para verificar los cambios
NOTA: esta linea de comando funciona solo para los proyectos q posean la carpeta lib/vendor/symfony

para probar el comando
$ cd /var/www/proy
$ symfony -V
Ahora puedes comenzar a hacer todo lo correspondiente con tu proyecto...

NOTA: la versión q se instala por defecto utiliza el ORM Doctrine

jueves, 3 de marzo de 2011

Relaciones Muchos a Muchos (M:N) en symfony 1.4 y Doctrine

Este es mi primer post de Symfony, mas adelante publicare muchos mas tips relacionados a este excelente framework de desarrollo en PHP.

Probando algunas cosas en symfony necesite hacer relaciones M:N en sf 1.4 con doctrine.. propel en la verison df 1.2 resolvia el asunto el solo, aparentemente 1.4 no lo hace asi... bien, supongamos que necesitamos realizar este tipo de relacion en nuestro projecto M:N, autor y libro, un autor tieme muchos libros y un libro tiene muchos autores necesariamente necesitamos una tabla intermedia parar representar este tipo de relacion..
En la vista de autor o de libro necesitamos tener un listado de libros o autores dependiendo del caso, una lista multiple, o un listado con checkboxes,

para realizar este ejemplo necesitamos la estructura (PostgreSQL):

CREATE TABLE autor
(
  id serial NOT NULL,
  descripcion text NOT NULL,
  CONSTRAINT autor_pkey PRIMARY KEY (id)
);

CREATE TABLE libro
(
  id serial NOT NULL,
  descripcion text NOT NULL,
  CONSTRAINT libro_pkey PRIMARY KEY (id)
);

CREATE TABLE autor_libro
(
  autor_id integer NOT NULL,
  libro_id integer NOT NULL,
  CONSTRAINT autor_libro_pkey PRIMARY KEY (autor_id, libro_id),
  CONSTRAINT autor_libro_autor_id_fkey FOREIGN KEY (autor_id)
      REFERENCES autor (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT autor_libro_libro_id_fkey FOREIGN KEY (libro_id)
      REFERENCES libro (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);
creamos nuestro proyecto, aplicacion, realizamos la conexion a BD y generamos el modelo, filtros y formularios, notamos que efectivamente en la vista de autor y de libro no existe referencia a la tabla intermedia...
symfony generate:project proy
symfony generate:app app
symfony configure:database "pgsql:host=localhost;dbname=proy_db" u_test 123456
symfony doctrine:build-schema
symfony doctrine:build --model
symfony doctrine:build --filters
symfony doctrine:build --forms
symfony doctrine:generate-module --with-show  app libro Libro
symfony doctrine:generate-module --with-show  app autor Autor
symfony cc
como solucionamos esto.. bien.. el schema original de la primera corrida es algo asi...

lo q tenemos q mejorar es lo referente a la relacion que representa el schema de la siguiente manera...
Autor:
  connection: doctrine
  tableName: autor
  columns:
    id:
      ....
    descripcion:
      ....
  relations:
    AutorLibro:
      local: id
      foreign: autor_id
      type: many

si lo que queremos es que en la vista de autor nos muestre un listado de libros para hacer una seleccion multiple tenemos que modificar la parte de la relacion de la tabla autor.
ahora quedara asi
Autor:
  connection: doctrine
  tableName: autor
  columns:
    id:
      ....
    descripcion:
      ....
  relations:
    Libro:
      class: Libro
      refClass: AutorLibro
      local: autor_id
      foreign: libro_id

posterior a esto lo q debemos hacer es ejecutar modelo, filtros, formulario y regenerar el modulo autor
symfony doctrine:build --model
symfony doctrine:build --filters
symfony doctrine:build --forms
symfony doctrine:generate-module --with-show  app autor Autor
symfony cc
Chequea nuevamente la vista y observamos una lista multiple donde se listaran todos los libros..
OJO, si modificas la estructura de datos y necesitas generar nuevamente el schema se perderan los cambios realizados en la tabla autor... algo malo a mi parecer.. debe haber algun plugin que realice algo de esto.. pero bueee...

si necesitas cambiar esa lista multiple solo deberas cambiar el widget y listo...

si tienes el plugin sfFormExtraPlugin instalado, solo agrega en /lib/form/doctrine/AutorForm.class.php:
$this->widgetSchema['libro_list']->setOption('renderer_class', 'sfWidgetFormSelectDoubleList');

tambien puedes cambiar lo que muestra en la lista agregando algunas validaciones de la consulta o mostrar checkboxes.. pero eso lo veremos luego en otro articulo..
la referencia de todo esto q explico lo puedes conseguir Aqui

jueves, 24 de febrero de 2011

host virtuales para tus proyectos PHP

Para crear host virtuales para tus proyectos PHP..
# cd /etc/apache2/sites-available/
aqui dentro encontraras varios archivos, necesitamos default, copiamos este archivo y lo editamos
# cp default sitios
# vim sitios
encontraremos un contenido similar al siguiente
< VirtualHost *:80>
 ServerAdmin webmaster@localhost

 DocumentRoot /var/www
 < Directory />
  Options FollowSymLinks
  AllowOverride All
 < /Directory>
 < Directory /var/www/>
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  allow from all
 < /Directory>

 ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
 < Directory "/usr/lib/cgi-bin">
  AllowOverride None
  Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
  Order allow,deny
  Allow from all
 < /Directory>

 ErrorLog /var/log/apache2/error.log

 # Possible values include: debug, info, notice, warn, error, crit,
 # alert, emerg.
 LogLevel warn

 CustomLog /var/log/apache2/access.log combined

    Alias /doc/ "/usr/share/doc/"
    < Directory "/usr/share/doc/">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride None
        Order deny,allow
        Deny from all
        Allow from 127.0.0.0/255.0.0.0 ::1/128
    < /Directory>

< /VirtualHost>
lo editamos y terminamos con un archivo similar a este
< VirtualHost *:80>
 ServerAdmin webmaster@localhost
        ServerName  ingreso.localhost
 DocumentRoot /var/www/ingreso/web/
 < Directory />
  Options FollowSymLinks
  AllowOverride All
 < /Directory>
 < Directory /var/www/ingreso/web/>
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  allow from all
 < /Directory>

 ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
 < Directory "/usr/lib/cgi-bin">
  AllowOverride None
  Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
  Order allow,deny
  Allow from all
 < /Directory>

 ErrorLog /var/log/apache2/error.log

 # Possible values include: debug, info, notice, warn, error, crit,
 # alert, emerg.
 LogLevel warn

 CustomLog /var/log/apache2/access.log combined

    Alias /doc/ "/usr/share/doc/"
    < Directory "/usr/share/doc/">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride None
        Order deny,allow
        Deny from all
        Allow from 127.0.0.0/255.0.0.0 ::1/128
    < /Directory>

< /VirtualHost>
al inicio agregamos el ServerName, modificamos el Documentroot y cambiamos tambien el 2do < Directory ...>
< VirtualHost *:80>
 ServerAdmin webmaster@localhost
        ServerName  ingreso.localhost
 DocumentRoot /var/www/ingreso/web/
 < Directory />
  Options FollowSymLinks
  AllowOverride All
 < /Directory>
 < Directory /var/www/ingreso/web/>
  ....
  ....
  .... 
en este archivo tambien podemos definir la ruta hacia donde se almacenaran los logs este proyecto
si necesitas hacer mas de 1 virtual host modifica este mismo archivo # vim /etc/apache2/sites-available/sitios
y agrega un nuevo tag < VirtualHost *:80> con la misma caracteristica y solo modificas las rutas y el nombre del ServerName

luego de realizar tus modificaciones debes habilitar el virtualhost creado y releer la confuiguracion del apache
# a2ensite sitios
# /etc/init.d/apache2 reload
No olvides agregar la linea correspondiente el /etc/hosts
# vim /etc/hosts
127.0.0.1       localhost
127.0.1.1       fmnh-d550
127.0.0.1       ingreso.localhost
192.168.1.14    desarrollo
192.168.1.11    intranet
y probar en tu navegador

http://ingreso.localhost/

y listo...!