Noviembre 10, 2009 - 1:48 pm - Posted by Juan Pablo Romero Bernal
The command line interface (known as cli) is a good tool for most developers, due ten fingers make more things than two. In fact, when we develop web applicactions using the Symfony framework several tasks are made through the command line, for instance: model generation, creation of database schemas, cache clear and so on. This tasks simplify the work in a significant way (if you are a symfony user, you know what i’m talking about).
Of course, this functionality is offered for the php command line interface (the php-cli), that sometimes is not enough to execute some actions such as: watch the object state, perform database inserts (and the typical crud operations), call methods in a interactive way, etc,. I know that some people says: the php cli has a interactive mode (-a option), but i think it is very limited. Talking with others developers, the php interpreter needs an interactive terminal (like python) and it keeps to work with our symfony projects.
The good new is that the facebook team has released (under BSD license) an interactive shell for php interpreter (as their own creators says: ” … ironically written in python”) called phpsh and it’s a great work !. I’ve perform some tests to integrate it with Symfony and i think, it can be a great tool for support and debugging.
Some simple examples:
After we installed phpsh (see the README file), we can work with php sentences and use the common functions:
test$ phpsh
Starting php
type 'h' or 'help' to see instructions & features
php> echo "Hello world"
Hello world
php> $a = 2+3; echo $a
5
php> $f = array('one' => 1, 'two' => 2, 'three' => 3);
php> foreach ($f as $n) { echo $n; }
123
php> foreach ($f as $n) { echo $n."\n"; }
1
2
3
php> foreach ($f as $n) {
... echo $n."\n";
... }
1
2
3
php> echo count($f);
3
Well, but right now, i’ll show you something more interesting: a symfony application using the interactive shell. It not requires additional configuration, just load a file: the frontal controller of your app (in the web directory), for example:
test$ phpsh web/index.php
Starting php with extra includes: ['web/index.php']
.......................
php>
To hide the html output, we can create a copy of this file and avoid the call to dispatch method:
require_once(dirname(__FILE__).'/config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend','prod', false);
sfContext::createInstance($configuration);
Now, load again (i’ve called shell.php):
test$ phpsh shell.php
Starting php with extra includes: ['shell.php']
type 'h' or 'help' to see instructions & features
php> $c = CityPeer::doSelect(new Criteria);
php> foreach ($c as $city) {
... echo $city."\n";
... }
Barranquilla
Bogota
Berlin
Medellin
php>
As you can see, it can be access to the model classes (i’m sorry with doctrine users) and the Symfony classes. For example, if my file app.yml looks like:
all:
results_per_page: 15
sf_phpmailer:
mailer: true
smtp_auth: true
smtp_secure: "ssl"
host: "smtp.example.com"
port: 600
username: "user"
password: "password"
from: "nobody@example.com"
from_name: "NoBody"
test$ phpsh shell.php
Starting php with extra includes: ['shell.php']
type 'h' or 'help' to see instructions & features
php> $conf = sfConfig::get('app_sf_phpmailer_port');php> echo $conf;
600
php> echo sfConfig::get('app_results_per_page');
15
php>
Well, it’s easy and very interesting. I hope work in detail and make more experiments. That’s all folks !.
Etiquetas: Php, Symfony | Comente »
Noviembre 9, 2009 - 1:23 am - Posted by Juan Pablo Romero Bernal
Para algunos desarrolladores nos es muy cómodo trabajar desde la línea de comandos, ya que diez dedos hacen más que dos. De hecho cuando se desarrollan aplicaciones con Symfony, el uso de la terminal de comandos es fundamental para muchas tareas (generación del modelo, creación de esquemas de base de datos, limpiar la cache, etc,.) que simplifican el trabajo significativamente.
Desde luego, todo esto es gracias a la interfaz en línea de comandos del ínterprete php (más conocido como php-cli), que en algunos casos se queda corto para ejecutar algunas pruebas: ver el estado de un objeto, insertar datos en la base de datos, probar métodos de manera interactiva, etc,. Sé que algunos dirán que php-cli tiene un modo interactivo, pero en realidad desde mi punto de vista es muy limitado. Sería muy interesante contar con algo similar a la terminal interactiva de python y desde luego que se pueda trabajar con nuestros proyectos en Symfony.
Lo mejor de todo, es que la gente de facebook liberó bajo licencia BSD una shell interactiva para php y como sus mismos credadores dicen: “… irónicamente escrita en python ….”; se llama phpsh y realmente es un gran trabajo. He realizado algunas pruebas de integración con Symfony y creo que puede ser de gran apoyo para pruebas y aún más para las personas que están aprendiendo a trabajar con el framework.
A continuación van algunos de los sencillos experimentos realizados:
Una vez se haya instalado phpsh (leer el README), podemos trabajar con sentencias de php y usar las funciones:
test$ phpsh
Starting php
type 'h' or 'help' to see instructions & features
php> echo "Hola mundo"
Hola mundo
php> $a = 2+3; echo $a
5
php> $f = array('uno' => 1, 'dos' => 2, 'tres' => 3);
php> foreach ($f as $n) { echo $n; }
123
php> foreach ($f as $n) { echo $n."\n"; }
1
2
3
php> foreach ($f as $n) {
... echo $n."\n";
... }
1
2
3
php> echo count($f);
3
Bien, pero ahora viene algo un poco más interesante: usar a través del shell interactivo nuestra aplicación hecha en Symfony.
No requiere ninguna configuración especial, simplemente cargar un archivo y listo; se trata del controlador frontal de nuestra aplicación (alguno de los que se encuentra en web), así:
test$ phpsh web/index.php
Starting php with extra includes: ['web/index.php']
.......................
php>
Para obviar la salida html, podemos crear una copia de ese archivo y evitar la llamada al método dispatch(), así:
require_once(dirname(__FILE__).'/config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend','prod', false);
sfContext::createInstance($configuration);
Ahora, de nuevo lo cargamos (ese archivo lo he llamado shell.php):
test$ phpsh shell.php
Starting php with extra includes: ['shell.php']
type 'h' or 'help' to see instructions & features
php> $c = CiudadPeer::doSelect(new Criteria);
php> foreach ($c as $ciudad) {
... echo $ciudad."\n";
... }
Barranquilla
Bogota
Constantinopla
Medellin
php>
Como se puede ver, puedo acceder a las clases de mi modelo (disculpas a los usuarios de Doctrine) y desde luego a las clases que nos proporciona Symfony, por ejemplo si mi archivo apps/frontend/config/app.yml es:
all:
resultados_por_pagina: 15
sf_phpmailer:
mailer: true
smtp_auth: true
smtp_secure: "ssl"
host: "smtp.mihost"
port: 600
username: "miusuario"
password: "miclave"
from: "nosesabe@example.com"
from_name: "Anonimo"
test$ phpsh shell.php
Starting php with extra includes: ['shell.php']
type 'h' or 'help' to see instructions & features
php> $conf = sfConfig::get('app_sf_phpmailer_port');
php> echo $conf;
600
php> echo sfConfig::get('app_resultados_por_pagina');
15
php>
Bueno, esos han sido los experimentos que he alcanzado a realizar. Espero trabajar más en detalle y publicar los resultados. Hasta una próxima.
Etiquetas: Php, Python, Symfony, Web | 2 Comentarios »
Septiembre 20, 2009 - 6:36 pm - Posted by Juan Pablo Romero Bernal
Esta es una segunda entrega acerca del trabajo con los formularios de filtro en Symfony. He tenido en cuenta los comentarios y sugerencias de los visitantes del blog, así que esta entrega integrará el uso de Doctrine.
Antes de comenzar, aclaro que mucha de la información aquí consignada está basada en el código generado por el generador de administración tanto de Propel como de Doctrine y desde luego en la documentación del API de Symfony 1.2.
Filtros con Doctrine
En la entrada anterior hablé del uso de los formularios con Propel de manera básica y aunque las forma de trabajar los formularios de filtros con Doctrine no es muy diferente, haré algunas aclaraciones importantes.
Al momento de generar el modelo con Doctrine, con la tarea:
1
| php symfony doctrine:build-all |
al igual con con Propel, se genera en el directorio lib/filter/doctrine/ el conjunto de clases de formularios de filtro. Tomando como referencia el esquema de datos del tutorial de Jobeet (Día 3 – http://www.symfony-project.org/jobeet/1_2/Doctrine/en/03) los filtros generados son:
1
2
3
4
5
6
| BaseFormFilterDoctrine.class.php
JobeetAffiliateFormFilter.class.php
JobeetCategoryAffiliateFormFilter.class.php
JobeetCategoryFormFilter.class.php
JobeetJobFormFilter.class.php
base/ |
Estas clases podemos modificarlas según nuestras necesidades, al igual que con los formularios. Ahora, vamos a generar el módulo que permita el CRUD de las ofertas de trabajo (Tabla Jobeet_Job):
1
| php symfony doctrine:generate-module frontend job JobeetJob |
Bien, ahora con el módulo generado, vamos a revisar las acciones y hacer los ajustes para el uso de los filtros. En primera forma, debemos modificar la acción index, para que nos presente el formulario de filtros:
1
2
3
4
5
6
7
8
9
10
11
12
| class jobActions extends sfActions
// apps/frontend/modules/job/actions/actions.class.php
{
public function executeIndex(sfWebRequest $request)
{
$this->jobeet_job_list = Doctrine::getTable('JobeetJob')
$this->createQuery('a')
$this->execute();
$this->filtro = new JobeetJobFormFilter();
}
// más código ..... |
Al igual que con Propel, debemos crear una instancia del filtro, para que sea visualizada en la plantilla:
1
2
3
4
5
6
7
8
9
10
11
12
13
| // apps/frontend/modules/job/templates/indexSuccess.php
<h1>Lista de trabajos</h1>
<h1>Lista de trabajos</h1>
<form action="<?php echo url_for('job/filter'); ?>" method="post">
<?php echo $filtro ?>
<p>
<input class="ui-state-default ui-corner-all" type="submit" value="Filtrar" />
<?php echo link_to('Quitar filtro', url_for('job/filter'),
array('query_string' => '_reset', 'method' => 'post')); ?>
</p>
</form>
<! -- más código html ... --> |
Creamos nuestra acción encargada de filtrar los resultados (filter):
1
2
3
4
5
6
7
8
9
| // apps/frontend/modules/job/actions/actions.class.php
public function executeFilter(sfWebRequest $request)
{
$this->filtro = new JobeetJobFormFilter();
$this->consulta = $this->filtro->buildQuery($request->getParameter('jobeet_job_filters'));
$this->jobeet_job_list = $this->consulta->execute();
$this->setTemplate('index');
} |
Una de las diferencias importantes con Propel, es el método que se encarga de construir la consulta según el valor de los filtros, que es buildQuery y que recibe una arreglo de valores a partir de los cuales se construirá la consulta. Este método devuelve un objeto de tipo Doctrine_Query, por eso llamamos al método execute en forma posterior.
Con lo anterior tenemos un sistema de filtros muy básico usando Doctrine como ORM.
Adaptando el filtro
Hasta el momento, ya hemos visto como establecer un filtro, tanto si usamos Propel como Doctrine. Sin embargo, la mayoría de las veces los campos por los cuales deseamos filtrar información no corresponden con todos los de la tabla. El filtro por defecto que se genera contempla todos los campos y por ello es necesario que hagamos algunas modificaciones, para filtrar por los campos deseados.
Siguiendo con el ejemplo del tutorial de Jobeet, supongamos que sólo queremos filtrar la lista de trabajos, por categoría y tipo (campos category y type, respectivamente), para ello debemos modificar el método configure de la clase JobeetJobFormFilter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // lib/form/doctrine/JobeetJobFormFilter.class.php
class JobeetJobFormFilter extends BaseJobeetJobFormFilter
{
public function configure()
{
$this->setWidgets(array(
'category_id' => new sfWidgetFormDoctrineChoice(array('model' => 'JobeetCategory', 'add_empty' => true)),
'type' => new sfWidgetFormFilterInput()
));
$this->setValidators(array(
'category_id' => new sfValidatorDoctrineChoice(array('required' => false, 'model' => 'JobeetCategory', 'column' => 'id')),
'type' => new sfValidatorPass(array('required' => false))
));
$this->widgetSchema->setNameFormat('jobeet_job_filters[%s]');
}
} |
Cómo se puede observar, un formulario de filtro es muy similar a un formulario, con algunas diferencias en cuanto a los widgets que se usan y algunos de los métodos. Con esta modificación, cuando imprimamos el filtro en la plantilla, ahora sólo los campos de categoría y tipo estarán disponibles. También podemos usar los métodos setLabel y setLabels para cambiar el nombre de las etiquetas que aparecerán al momento de imprimir el formulario de filtros, por ejemplo:
1
2
3
4
| $this->getWidgetSchema()->setLabels(array(
'category_id' => 'Categoria',
'type' => 'Tipo'
)); |
Uno de los widgets de filtros que considero más útiles es sfWidgetFormFilterDate, ya que con él es posible filtrar información por rangos de fecha, aquí un ejemplo de su uso para el campo de expiración de la oferta de trabajo (campo expires_at):
1
2
3
4
5
6
| 'expires_at' => new sfWidgetFormFilterDate(array(
'from_date' => new sfWidgetFormDate(),
'to_date' => new sfWidgetFormDate(),
'with_empty' => 0, // Para que no salga el checkbox
'template' => 'desde %from_date% hasta %to_date%'
)) |
Bien, hasta aquí esta segunda entrega del trabajo con filtros en Symfony 1.2. Espero que sea de utilidad.
Etiquetas: Desarrollo, Doctrine, Symfony | 15 Comentarios »
Septiembre 13, 2009 - 10:12 pm - Posted by Juan Pablo Romero Bernal
Esta es una entrada bastante rápida, pero que busca mostrar la forma básica en que se pueden usar los formularios de filtros en Symfony (versión 1.2) para facilitar a los usuarios la clasificación y consulta de información. El manejo de formularios de filtros es muy similar a los formularios que se usan para el ingreso y/o edición de información, con algunas variaciones. Hago claridad que las pruebas que he realizado han sido usando Propel como ORM.
Bien, empecemos por saber que en Symfony existe una tarea que nos permite construir en forma automática los formularios de filtros, de la siguiente forma:
1
| $ php symfony propel:build-filters |
Después de la ejecución de esta tarea, en el directorio lib/filters de nuestro proyecto, se habrán creado (de acuerdo a nuestro modelo) un conjunto de clases cuyo nombre sigue la siguiente nomenclatura:
1
| ClaseFormFilter.class.php |
y al igual que con el modelo y los formularios, tendremos unas clases Base y unas clases que extenderán de esas clases base, las cuales podemos adaptar según nuestras necesidades. Una vez creadas nuestras clases de formularios de filtros, ya podemos usarlas dentro de nuestras acciones. Tomaré como ejemplo, que tenemos un modulo llamado clientes que permite trabajar con el CRUD normal y que cada cliente tiene un nombre, telefono y una ciudad, de modo que nuestra clase base de formulario de filtro lucirá así:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| class BaseClienteFormFilter extends BaseFormFilterPropel
{
public function setup()
{
$this->setWidgets(array(
'nombre' => new sfWidgetFormFilterInput(),
'telefono' => new sfWidgetFormFilterInput(),
'ciudad_id' => new sfWidgetFormPropelChoice(array(
'model' => 'Ciudad', 'add_empty' => true))
));
$this->setValidators(array(
'nombre' => new sfValidatorPass(array('required' => false)),
'telefono' => new sfValidatorPass(array('required' => false)),
'ciudad_id' => new sfValidatorPropelChoice(array('required' => false,
'model' => 'Ciudad', 'column' => 'id')),
));
$this->widgetSchema->setNameFormat('cliente_filters[%s]');
$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);
parent::setup();
}
public function getModelName()
{
return 'Cliente';
}
public function getFields()
{
return array(
'id' => 'Number',
'nombre' => 'Text',
'telefono' => 'Text',
'ciudad_id' => 'ForeignKey',
);
}
} |
Tengamos en cuenta que esta clase es generada automáticamente y podemos hacer las modificaciones necesarias en la clase ClienteFormFilter, como se hace con los formularios. Ahora bien, vamos a utilizar este formulario de filtro en nuestra acción index de nuestro módulo de clientes, de la siguiente forma:
1
2
3
4
5
6
7
8
9
10
11
12
| class clientesActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
// creamos el filtro ....
$this->filtro = new ClienteFormFilter();
// .....
// más código ....
// Típica consulta ....
$this->clientes = CientePeer::doSelect(new Criteria());
}
} |
En nuestra plantilla: (indexSuccess.php)
1
2
3
4
5
6
7
8
9
| <form action="<?php echo url_for('clientes/filtrar'); ?>" method="post">
<table>
<tbody>
<?php echo $filtro; ?>
</tbody>
</table>
<input type="submit" value="Filtrar" name="filtrar"/>
</form>
// otro codigo html .... |
Bien, hasta aquí nada nuevo, simplemente un formulario más, que aparecerá en nuestra acción index de nuestro módulo de clientes, pero con la diferencia que será procesado por la acción filtrar (o como decida llamarse). En este punto es importante mencionar, que para la ejecución del filtrado, se empleará el método buildCriteria, el cuál recibe como parámetro un arreglo (los campos por los cuales se desea hacer el filtro) y se encarga de construir las consultas:
1
2
3
4
5
6
7
8
| public function executeFiltrar(sfWebRequest $request)
{
$this->filtro = new ClienteFormFilter();
// Construye un objeto criteria con el valor de los filtros
$criteria = $this->filtro->buildCriteria($request->getParameter('cliente_filters'));
$this->clientes = ClientePeer::doSelect($criteria);
$this->setTemplate('index');
} |
El método buildCriteria es el encargado de construir la consulta según el valor de los filtros (revise la clase sfFormFilterPropel y los métodos addXXXCriteria() de la misma clase). Posteriormente pasamos ese objeto criteria a el método doSelect para devolver la consulta filtrada usando la plantilla de la acción index. Sencillo, no ??
En una próxima entrada se explicará lo referente al manejo de formularios de filtro y el mantenimiento de los resultados obtenidos por el filtro entre páginas.
Etiquetas: Desarrollo, Symfony | 8 Comentarios »