Columnas virtuales ordenables en symfony

La primera vez que usamos el admin generator y creamos el “backend” de nuestro proyecto pensamos “UAU! tengo todas las tablas, los formularios, filtros, etc creados con una sola línea de comando!!”, y es verdad que esto supone un gran avance en la realización del proyecto pero si queremos “pulir” lo que symfony nos produce tenemos por delante una ardua tarea.

Hoy empezaremos a “pulir” nuestro proyecto creando columnas virtuales para las listas del app de admin, que sean también ordenables claro.

Para empezar nos vamos a cargar el sistema por el que symfony coge los datos de un modelo y vamos a crear el nuestro propio que seguro será mas eficiente. Por ejemplo si tenemos las tablas artículo – escritor (un artículo es escrito por un solo escritor y un escritor escribe muchos artículos), lo que queremos obtener es que en la lista de los artículos se nos muestre el nombre del escritor en vez de su código. Para ello abrimos el archivo:
/lib/model/doctrine/ArticulosTable.class.php

y le metemos el método getRowsArticulos, el cual he llamado así porque me ha dado la gana:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
class ArticulosTable extends Doctrine_Table
{
public static function getInstance()
{
return Doctrine_Core::getTable('Articulos');
}
public static function getRowsArticulos(){
    return Doctrine_Query::create()
    ->select('a.titulo, e.nombre as nombre_escritor, a.created_at')
    ->from('Articulos a')
    ->leftJoin('a.Escritor e');
}
}

En la consulta le metemos todos los campos que vamos a querer mostrar en la lista.

A continuación, nos vamos al archivo de generación del módulo:
/apps/backend/modules/articulos/config/generator.yml

PHP
1
2
3
4
5
6
7
8
9
10
11
12
config:
actions: ~
fields:
titulo: { label: "Título" }
    //Muy importante!!! Los campos virtuales hay que decirle que son reales, aunque esto sea una paradoja, para que se puedan ordenar.
nombre_escritor: { label: "Autor", is_real: true }
created_at: { label: "Creado en" }
list:
        //Hasta aquí normal, le decimos que campos debe mostrar
display: [titulo, nombre_escritor, created_at]
        //Y aquí le decimos de que método queremos que los saque
table_method: getRowsArticulos

Vale, pues ya solo nos queda decirle los campos que son ordenables, y se hace en:
/apps/backend/modules/articulos/actions/action.class.php

añadiendo el siguiente método:

PHP
1
2
3
4
5
6
7
8
9
10
11
require_once dirname(__FILE__).'/../lib/articulosGeneratorConfiguration.class.php';
require_once dirname(__FILE__).'/../lib/articulosGeneratorHelper.class.php';
class articulosActions extends autoArticulosActions
{
//El nombre de este método no me lo he inventado yo, debe ser así
protected function isValidSortColumn($column)
{
return in_array($column, array('titulo', 'nombre_escritor', 'created_at'));
}
}

Pues ya está, ya tenemos nuestra columna virtual/real, como querais llamarla, creada y ordenable.

Pago contra reembolso en Magento

Una forma de pago bastante habitual el los comercios electrónicos es el contra reembolso o pago a la entrega. Cuando instalamos Magento podemos sorprendernos al ver que entre las diversas y complejas formas de pago que lleva por defecto no se encuentra el contra reembolso, en este punto lo que hacemos es buscar un módulo que implemente esta forma de pago.

Los módulos más conocidos destinados a este fin y gratuitos son:
1 – IG_CashOnDelivery (IDEALIAGroup)

El primer módulo cuando lo instalamos nos crea un submenú en el menú ventas donde nos permite crear reglas para decidir que porcentaje o valor fijo le cargamos a la compra dependiendo del valor total de la misma. Aquí nos encontramos un problema y es que de forma habitual las agencias de transporte calculan ese porcentaje sobre el total a cobrar al cliente final, es decir, contando portes e impuestos. Es en este punto donde nos falla este módulo ya que solo aplica el cálculo sobre el total del carrito sin iva y sin portes. Un fallo común en este módulo por si a alguien le pasa es que no respeta las reglas creadas y es porque hay que crearlas en local y foreign o extranjero.

2 - Contra reembolso personalizado (DEFCON2)

El segundo módulo nos crea un submenú en Ventas->Impuestos->Reembolso que nos permite crear reglas de contra reembolso para cada tipo de envío. Podemos darle un nombre, especificar a partir de que importe aplicar la regla y aplicar un recargo porcentual o fijo. Este módulo en principio cumpliría los requisitos por lo que el anterior no nos servía pero en mi caso fue instalado en un Magento 1.6.1 con un template personalizado y al realizar el pedido y ir a seleccionar el método de envío aparecía un error y volvía al carrito.

3 - CashOnDelivery (Phoenix Medien)

El tercer módulo probado tan solo nos crea el menú de configuración dentro de métodos de pago igual que el resto de módulos y es desde este único menú desde donde podemos indicarle los métodos de envío que aceptan contra reembolso o que no lo aceptan, el coste adicional y algunas otras opciones menos importantes o más sencillas como mostrar valor en caso de que la tasa sea cero o países donde aceptamos contra reembolso… También encontramos una opción dentro de la configuración en Ventas->Impuestos->Price Display Settings que nos permite mostrar la tasa contra reembolso con impuestos, sin ellos o de ambas formas en la totalización del pedido. El gran problema de este módulo es que no nos permite grabar una tasa porcentual sobre el total de la compra y tan solo acepta cantidades fijas cosa que en la mayoría de los casos no nos servirá.

Después de analizar los módulos llegamos a la conclusión de que ninguno se adapta al funcionamiento habitual del contra reembolso, esto lo decimos sabiendo que que el módulo de DEFCON2 aparenta funcionar como toca pero no ha llegado a funcionar bajo la situación explicada.

La solución más factible ha sido instalar el módulo de Phoenix Medien dada la claridad del código y realizar una pequeña modificación sobre el mismo. En esta modificación tan solo cambiamos el comportamiento de las dos cajas de texto de la configuración del módulo que nos permiten aplicar un valor fijo para que ese valor introducido sea un porcentaje sobre el total incluyendo portes e impuestos.

Abrimos el fichero app/code/community/Phoenix/CashOnDelivery/Model/CashOnDelivery.php y modificamos el método getAddressCosts() que es el encargado de devolver la tasa correspondiente a la forma de pago contra reembolso

Código original (Importe fijo):

PHP
1
2
3
4
5
6
7
public function getAddressCosts(Mage_Customer_Model_Address_Abstract $address){
if ($address->getCountry() == Mage::getStoreConfig('shipping/origin/country_id')) {
return $this->getInlandCosts();
} else {
return $this->getForeignCountryCosts();
}
}

Código modificado (Importe porcentual al total):

PHP
1
2
3
4
5
6
7
8
9
public function getAddressCosts(Mage_Customer_Model_Address_Abstract $address){
if ($address->getCountry() == Mage::getStoreConfig('shipping/origin/country_id')) {
$value = ($address->getSubtotal()+$address->getShippingAmount()+$address->getTaxAmount())*$this->getInlandCosts()/100;
return $value;
} else {
$value = ($address->getSubtotal()+$address->getShippingAmount()+$address->getTaxAmount())*$this->getForeignCountryCosts()/100;
return $value;
}
}

Con esto conseguimos tener un módulo funcional y configurable desde el admin con tan solo añadir algunas líneas al módulo de Phoenix Medien. Dentro del mismo método podríamos implementar de forma muy sencilla con una línea más, una regla para establecer un mínimo y/o máximo en las tasas a aplicar.

Mapear imagen en html

Es posible que alguna vez necesitemos mostrar una imagen en una web con la que necesitemos llevar a diferentes enlaces según en la porción de imagen que realices el click, sea el caso de un mapa con varios puntos donde pinchas, un muñeco que nos mande a diferentes sitios según en que parte del mismo pinchemos o una imagen de una estaría con varios libros que nos manda cada uno a un link diferente con su descripción.

La primera opción para resolver este problema que nos puede venir a la cabeza es trocear la imagen, maquetarla con css para que no parezca varios trozos y añadir un link a cada trozo. Otra opción puede ser montarlo en flash, pero para esta función tan sencilla vamos a meternos en flash?

Pues bien la solución es muy sencilla, HTML tiene un tag llamado map que sirve justo para hacer esto, crear varias zonas o secciones sobre una imagen y añadir links a dichas zonas.

La etiqueta map define un mapa sobre nuestra imagen y dentro de esta debemos definir etiquetas area que definen cada división de la imagen que va a contener un link. Veamos un ejemplo en código.

XHTML
1
2
3
4
5
6
7
8
9
10
<!-- Definimos la imagen y le asignamos un map con usemap y el nombre del mismo -->
<img src="estanteria.jpg" alt="estanteria" width="600" height="500" usemap="#estmap"/>
<!-- Definimos el mapa con su nombre -->
<map name="estmap">
<!-- Definimos un área rectangular indicando las coordenadas del vértice superior izquierdo e inferior derecho -->
<area title="Libro azul" shape="rect" coords="348,264,375,288" href="libro-azul.html" alt="Libro azul" />
<!-- Definimos un área circular indicando las coordenadas del centro del círculo y el radio -->
<area title="Libro rojo" shape="circle" coords="150,175,55" href="libro-rojo.html" alt="Libro rojo" />
</map>

También disponemos de webs que nos permiten diseñar las areas de forma gráfica y nos generan el código html como el caso de www.image-maps.com

Login en Symfony sin usar sfGuardPlugin

Hola de nuevo. Vamos a mostraros una forma de llevar un control de accesos y credenciales de usuarios bastante simple, sin tener que utilizar el plugin sfGuardPlugin que nos puede traer mas de un quebradero de cabeza.

Bien, lo primero de todo es crear un form que nos servirá de login:
/lib/form/LoginForm.class.php

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LoginForm extends BaseForm
{
public function configure()
{
    $this->setWidgets(array(
        'user' => new sfWidgetFormInputText(),
        'password' => new sfWidgetFormInputPassword(),
    ));
    $this->setValidators(array(
        'user' => new sfValidatorEmail(array('required' => true), array('required' => 'Escribe tu usuario')),
        'password' => new sfValidatorString(array('required' => true), array('required' => 'Escribe tu password')),
    ));
    $this->widgetSchema->setLabels(array(
        'user' => 'Usuario:',
        'password' => 'Contraseña:',
    ));
}
}

Para continuar tengo que explicar un poco como va el tema de las credenciales y la autenticación en symfony. Entre los archivos settings.yml y security.yml de la carpeta config de la aplicación se puede hacer practicamente todo. Lo primero es dicirle a la aplicación que necesita seguridad para lo que entramos al security.yml y modificamos:
/apps/app/config/security.yml

default:
is_secure: true

Con esto nuestra aplicación es segura, con lo que exigira la autenticación del usuario antes de pasar por cualquier acción, menos por la del login. Esto también se puede hacer para cualquier acción en cualquier módulo, por ejemplo, para solo tener una acción o un módulo protegido:

Creamos el archivo:
/apps/app/modules/prueba/config/security.yml
y escribimos:

default:
is_secure: true

esto para hacer que todo el módulo sea seguro o bien:

index:
is_secure: true

para que solo la acción index sea segura.

Vale para el tema credenciales la misma mecánica, vamos al config.yml de donde queramos controlar las credenciales y escribimos:
/apps/app/modules/prueba/config/security.yml

default:
is_secute: true
credentials: admin

Con esto solo pueden acceder a la aplicación app los usuarios que estén autenticados y tengan credenciales de admin.

default:
is_secure: true
credentials: [alianza, horda]

Con esto solo pueden acceder los usuarios que sean de la alianza y de la horda. Es decir que tengan las dos credenciales.

default:
is_secure: true
credentials: [[alianza, horda]]

Con esto solo pueden acceder los usuarios que sean de la alianza o de la horda. Es decir que tengan cualquiera de las dos credenciales.
Vale a continuación le atacamos al archivo settings.yml de la aplicación:
/apps/app/config/settings.yml
y añadimos:

all:
.actions:
login_module: login
login_action: index

Esto es para decirle a la aplicación que módulo y que acción van a controlar el login, por lo que tendremos acceso siempre, estemos autenticados o no.

Pues todo listo para crear el modulo y la acción de login:
/apps/app/modules/login/actions/action.class.php

PHP
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
40
41
42
43
44
45
46
47
48
49
50
51
class loginActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
    if($request->getParameter('login')){
        //Hacemos la consulta para ver si existe cliente con ese usuario y esa contraseña.
        $query = Doctrine_Query::create()
            ->select('user, idcliente, nombre, apellidos')
            ->from('Cliente')
            ->where('user = ?',$request->getParameter('user'))
            ->andwhere('pass = ?',md5($request->getParameter('password')))
            ->execute();
        //Comprobamos que existe el usuario, si no creamos un mensaje flash y redireccionamos al index
        if(count($query) == 0){
            $this->getUser()->setFlash('error', 'Usuario o contraseña inválidos');
            $this->redirect('login/index');
        }else{
            //Autenticamos al usuario
            $this->getUser()->setAuthenticated(true);
            //Creamos variables de sesión con sus datos personales, para lo que nos pueda valer.
            $this->getUser()->setAttribute('id', $query[0]['idcliente'], 'cliente');
            //En este caso 'nombre' es el nombre de la variable, seguido por el valor y por último el namespace.
            $this->getUser()->setAttribute('nombre', $query[0]['nombre'], 'cliente');
            //Si queremos acceder a estas variables no tenemos mas que poner: $this->getUser()->getAttribute('nombre', null, 'cliente');
            $this->getUser()->setAttribute('apellidos', $query[0]['apellidos'], 'cliente');
            //Le damos las credenciales correspondientes, en este caso de cliente.
            $this->getUser()->addCredential('cliente');
            $this->redirect('moduloquequieras/index');
        }
    }
    /*Aquí nos aseguramos de que no vuelva a entrar al login una vez logeado y tenga credencial de cliente, no es necesario pero a mi me gusta         así. Quedaos con el isAuthenticated() y el hasCredential('credencial'). Para comprobar que se tienen mas de una credencial:
    $this->getUser()->hasCredential(array("admin", "cliente")); Para comprobar que tiene las dos, admin y cliente.
    $this->getUser()->hasCredential(array("admin", "cliente"), false); Para comprobar que tiene cualquiera de las dos.
    */    
    if($this->getUser()->isAuthenticated() && $this->getUser()->hasCredential('cliente'))
        $this->redirect('moduloquetedelagana/index');
    //Instanciamos el formulario que hemos creado en el primer paso.
    $this->form = new LoginForm();
}
//Esta es la acción que se ejecuta cuando queremos hacer logout(deslogearse).
public function executeLogout(sfWebRequest $request){
    //Borramos todas las variables de sesion creadas bajo el namespace de cliente.     
    $this->getUser()->getAttributeHolder()->removeNamespace('cliente');
    //Quitamos la autenticación del usuario.
    $this->getUser()->setAuthenticated(false);
    //Eliminamos la credencial. Para borralas todas de golpe: $this->getUser->clearCredentials();
    $this->getUser()->removeCredential('cliente');
    $this->redirect('login/index');
}
}

La seguridad de este login es ridícula, lo he hecho básico para explicarlo mas rápido, no lo toméis como referencia.
Ahora la vista:
/apps/app/modules/login/templates/indexSuccess.php

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php use_stylesheets_for_form($form) ?>
<?php use_javascripts_for_form($form) ?>
<div class="login">
    <h1>Acceso a clientes</h1>
    <form action="<?php echo url_for('login/index')?>" method="post">
        <table>
            <tbody>
                //Mostramos el mensaje flash, en caso de que haya.
                <?php if ($sf_user->hasFlash('error')): ?>
                    <div class="flash_error"><?php echo $sf_user->getFlash('error') ?></div>
                <?php endif; ?>
                //Printamos el formulario.
                <?php echo $form; ?>
                <tr>
                    <th></th>
                    <td>
                        <input type="submit" value="Acceder" name="login">
                    </td>
                </tr>
            </tbody>
        </table>
    </form>
</div>

Pues creo que no se me olvida nada. Bueno solo os queda poner por ahi un botón para hacer logout que se active según el usuario este autenticado o no, en el caso de que este en el layout. Espero que os sirva de ayuda.

Configurar plugin subversion en eclipse (subclipse)

Para quien le guste trabajar con eclipse como IDE multilenguaje vamos a explicar los pasos a seguir para poder instalar un plugin llamado subclipse que nos permite realizar el control de versiones de nuestros proyectos con un repositorio SVN (subversion). Lo primero de todo es tener instalado el eclipse, sea la versión java, pdt…

Con el eclipse abierto nos vamos a la pestaña Help y pinchamos en Install new software…

Pinchando en Add aparecerá una ventana con un par de cajas de texto en las que pondremos los datos siguientes:

Name: Subclipse 1.2.x (Eclipse 3.2+)
Location: http://subclipse.tigris.org/update_1.2.x

Cerramos la ventana pinchando en OKy esperamos a que busque el software, una vez termine de buscar aparecerá en la lista una entrada llamada Subclipse 1.2.x, la desplegamos y seleccionamos únicamente Subclipse Plugin. Ahora pincha en Next para continuar.

Enseguida nos pedirá aceptar los términos de licencia y nos quedará por hacer un Next y seguidamente un Finish.

Ahora empezará la descarga del plugin, alrededor de 7MB, una vez termine eclipse nos pedirá reiniciar, aceptamos y esperamos que se abra de nuevo.

Tenemos el plugin subclipse instalado dentro de eclipse pero nos falta abrir la perspectiva (vista) desde la que podemos configurar nuestros repositorios, para ello, nos vamos al menú Window, pinchamos en Open Perspective -> Open -> SVN Repository Exploring.

Veremos la nueva perspectiva junto con el resto, como por ejemplo la de PHP si trabajamos con la versión PDT de eclipse, en la parte superior derecha. Una vez dentro de la perspectiva tenemos un icono en la parte superior izquierda con un símbolo de svn y un +, Si nos posicionamos encima aparecerá “Add SVN Repository“. Este es el icono que debemos pinchar para añadir un nuevo repositorio. Nos pedirá la URL del repositorio y ya podemos darle a Finish.

Si el repositorio tiene control de usuarios nos pedirá el user y password en este momento, podemos decirle que nos guarde el login seleccionando el check “save password“.

Una vez estamos conectados con el repositorio solo nos queda hacer una última cosa y es descargar por primera vez el proyecto, esto lo hacemos pinchando con el botón derecho encima del repositorio añadido y haciendo Checkout. En la nueva ventana marcamos la segunda opción “Check out as a project in the workspace” y de damos un nombre a nuestro proyecto local.

Ahora volvemos a nuestra perspectiva de trabajo, java, php… y veremos el proyecto creado con todo el contenido que hemos descargado del repositorio. Si hacemos click derecho en el proyecto o cualquier directorio o fichero dentro de él veremos un menu Team donde podemos hacer Update de los cambios que hayan subidos al servidor, Commit de los cambios realizados por nosotros o otras muchas opciones…

Configurar conexiones seguras HTTPS/SSL sobre Apache

Vamos a explicar como configurar apache2 para realizar conexiones seguras con los clientes bajo el protocolo HTTPS con cifrado SSL.

Necesitamos generar una clave privada en el servidor para el cifrado asimétrico, esto lo hacemos con el siguiente comando (nos pedirá un password para cifrar la propia clave, podemos usar password.es para generar uno seguro):

Shell
1
openssl genrsa -des3 -out server.key 2048

Ahora generaremos el fichero CSR con el certificado, el cual nos hará falta para que una autoridad certificadora nos pueda firmar nuestro certificado en caso de desearlo.

Shell
1
openssl req -new -key server.key -out server.csr

Una vez tenemos el certificado en el fichero CSR podemos mandarlo a una autoridad certificadora para que sea ella quien nos lo firme previo pago y así los navegadores confiarán en nuestro certificado sin emitir aviso alguno de desconfianza. Si por otro lado queremos firmarlo nosotros mismo también podemos hacerlo sabiendo que el navegador nos avisará de que el certificado es desconocido mientras no lo instalemos en el mismo.

Shell
1
openssl x509 -req -days 365 -set_serial 1 -in server.csr -signkey server.key -out server.crt

En este punto tenemos nuestro certificado firmado en formato CRT pero apache solo funciona si le damos el certificado en formato PEM por lo que lo transformamos con este sencillo paso que no es más que juntar la clave privada y el certificado CRT en un mismo fichero.

Shell
1
cat server.key server.crt > server.pem

Este fichero generado debemos ubicarlo bajo el directorio de apache en: /etc/apache2/ssl/

Ya tenemos nuestro certificado generado y en el lugar correspondiente, ahora solo queda configurar apache2 para que acepte conexiones seguras y use este certificado. Primero activamos el módulo ssl por si antes no lo estaba.

Shell
1
a2enmod ssl

También nos aseguramos que apache2 está escuchando por el puerto 443 (HTTPS). Abrimos el fichero /etc/apache2/ports.conf y buscamos la línea:

Shell
1
Listen 443

Es posible que aparezca entre una condición if, esto sirve para que solo escuche por el puerto 443 si el módulo ssl de apache está activo. Si no aparece tenemos que añadirla.

Por último solo tenemos que indicar en nuestro virtualhost que recoja las peticiones por el puerto 443, activar el cifrado SSL y decirle donde está el certificado, lo hacemos añadiendo estas líneas:

Shell
1
2
3
4
5
6
<VirtualHost *:443>
...
SSLEngine On
SSLCertificateFile /etc/apache2/ssl/server.pem
...
</VirtualHost>

Es necesario reiniciar el servicio para que los cambios sean efectivos. Al reiniciar veremos que apache2 nos pide un password, este es el que hemos introducido al generar la clave privada en el primer paso y es necesario para poder descifrarla.

Shell
1
/etc/init.d/apache2 restart

Uso de Tiny MCE (wysiwyg) en Symfony

Siguiendo con la explicación del uso del plugin sfFormExtraPlugin hoy mostraremos como usar el widget tinyMCE de una forma rápida y sencilla. Si todavía no teneis instalado el plugin id a esta entrada aquí se explica como instalar todos los archivos necesarios para que funcione.

Una vez lo tenemos instalado, debemos descargar TinyMCE desde su web ofical y copiarlo dentro de la carpeta “/web/js/” quedando “/web/js/tiny_mce”.

Para activar el componente TinyMCE dentro de la aplicación entramos en el archivo de configuración view.yml:
“/apps/app/config/view.yml”

y en la línea de los js le añadimos:

javascripts: [jquery-1.6.2.min.js, jquery-ui-1.8.16.custom.min.js, tiny_mce/tiny_mce.js]

luego entramos al fichero settings.yml de la misma carpeta:
“/apps/app/config/settings.yml”

y le añadimos lo siguiente:

all
.settings:
rich_text_js_dir: js/tiny_mce

Vale pues ya solo queda llamarlo en cualquier formulario de esta forma:
/lib/form/doctrine/SargerasForm.class.php

PHP
1
2
3
4
5
6
7
public function configure(){
    $this->widgetSchema['contenido'] = new sfWidgetFormTextareaTinyMCE(array(
        'width' => 450,
        'height' => 350,
        'config' => 'theme_advanced_disable: "anchor,image,cleanup,help"',
    ));
}

Seguiremos otro día con el tutorial para crear recaptchas, autocompletados y selectores de idiomas, también incluidos en el plugin sfFormExtraPlugin.

Gracias por visitarnos.

Uso de sfWidgetFormJQueryDate (datepicker) en Symfony

Amigos, vamos a explicar como poner un datepicker, o “seleccionador de fechas”, mas atractivo que el que nos ofrece symfony. Bien lo primero es instalar el plugin que contiene el widget, que se llama sfFormExtraPlugin, para ello introducimos el siguiente comando en la raiz del proyecto:

php symfony plugin:install sfFormExtraPlugin

Este plugin nos ofrece varios controles a parte del sfWidgetFormJQueryDate, que es el que vamos a gastar, como un widget de autocompletado o un wysiwyg.

Una vez tenemos instalado el plugin debemos descargarnos las librerias de jQuery ui aquí. Ahora hay que añadir los archivos al proyecto por lo que descomprimimos el archivo y copiamos los archivos “jquery-1.3.2.min.js” y “jquery-ui-1.7.2.custom.min.js” en la carpeta “/web/js/”. A continuación, le tenemos que decir a la aplicación que use estos archivos y coja el css para el widget. Entramos en el archivo “/apps/app/modules/config/view.yml” y en la linea de javascripts le decimos:

javascripts: [jquery-1.3.2.min.js, jquery-ui-1.7.2.custom.min.js]

y en la de css:

stylesheets: [main.css, ui-lightness/jquery-ui-1.7.2.custom.css]

Pues ya están los preparativos. Ahora configuramos nuestro form para que utilice el widget y a correr:

/lib/form/doctrine/IllidanForm.class.php

PHP
1
2
3
4
5
6
7
8
9
10
public function configure(){
    $this->widgetSchema['fecha'] = new sfWidgetFormJQueryDate(array(
//Y lo configuramos
        'config' => '{}',
        'culture' => 'es',
        'config' => "{firstDay: 1, dayNamesMin: ['Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'],
            monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']}",
        'date_widget' => new sfWidgetFormDate(array('format' => '%year%-%month%-%day%'))
    ));
}

En mi caso, este widget me dió un problema al utilizarlo y era que al hacer submit me quitaba el día de la selección por lo que daba un error de campo no válido. Conseguí subsanarlo cambiando la siguiente línea dentro de la clase del widget:

plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormJQueryDate (linea 106)

PHP
1
jQuery("#%s option").attr("disabled", "");

por

PHP
1
jQuery(#%s option”).removeAttr(“disabled”);

No me acuerdo en que página encontre la solución, hace mucho tiempo, así que no puedo poner la referencia.

sfDoctrinePager (paginador) en Symfony

Pongamos que tenemos una tabla de productos y queremos hacer una lista que sea paginada, que se pueda ordenar por cualquier campo y añadirle un formulario de filtros. Hoy empezaremos con el paginador.

Crear un paginador para una aplicación de symfony es muy simple, usando la clase sfDoctrinePager se hace prácticamente sólo:
Primero en la acción: (“apps/frontend/modules/productos/actions/action.class.php”)

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class productosActions extends sfActions{
    public function executeIndex(sfWebRequest $request){
        //Creamos la consulta cuyos resultados vamos a paginar.
        $query = Doctrine_Query::create()
            -&gt;select('nombre, part_number, Stock, precio, created_at')
            -&gt;from('Producto');
        //Instanciamos el paginador y le decimos cuantos registros se van a mostrar por página, 20 en este caso.
        $this-&gt;productos = new sfDoctrinePager(
            'Producto',
            '20'
        );
        //Le metemos la consulta.
        $this-&gt;productos-&gt;setQuery($query);
        //Y la página que queremos ver.
        $this-&gt;productos-&gt;setPage($request-&gt;getParameter('pagina', 1));
        $this-&gt;productos-&gt;init();
    }
}

Ahora vamos a modificar la vista para mostrar las páginas y los controles del paginador.

En “apps/frontend/modules/productos/templates/indexSuccess.php”:

PHP
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<h1>Lista de productos</h1>
<table>
    <thead>
        <tr>
            <th>Nombre</th>
            <th>P/N</th>
            <th>Stock</th>
            <th>Precio</th>
            <th>Creado en</th>
        </tr>
    </thead>
    <tbody>
        //sfDoctrinePager-&gt;getResults te devuelve los registros de la página que le has pasado posteriormente al paginador.
        getResults() as $producto): ?&gt;
            <tr>
                <td>getNombre() ?&gt;</td>
                <td>getPart_number() ?&gt;</td>
                <td>getStock() ?&gt;</td>
                <td>getPrecio() ?&gt;</td>
                <td>getCreated_at() ?&gt;</td>
            </tr>
        
    </tbody>
</table>
//Aquí es donde esta la miga.
//Comprueba que se puede paginar
haveToPaginate()): ?&gt;
    <div>
        //Botón de primera página.
        &lt;a href=&quot;?pagina=1"&gt;
            <img src="/images/first.png" alt="Primera página" />
        </a>
        //Botón de página anterior.
        &lt;a href=&quot;?pagina=getPreviousPage() ?&gt;"&gt;
            <img src="/images/previous.png" alt="Página anterior" />
        </a>
        //Páginas adyacentes a la actual.
        getLinks() as $page): ?&gt;
            getPage()): ?&gt;
                
            
                &lt;a href=&quot;?pagina="&gt;</a>
            
        
        //Botón siguiente página.
        &lt;a href=&quot;?pagina=getNextPage() ?&gt;"&gt;
            <img src="/images/next.png" alt="Siguiente página" />
        </a>
        //Botón última página.
        &lt;a href=&quot;?pagina=getLastPage() ?&gt;"&gt;
            <img src="/images/last.png" alt="Última página" />
        </a>
    </div>
//Número total de registros.
<div class="pagination_desc">
    <strong>getNbResults() ?&gt;</strong> productos
    //Página actual y total de páginas.
    haveToPaginate()): ?&gt;
        - página <strong>getPage() ?&gt;/getLastPage() ?&gt;</strong>
    
</div>
&lt;a href=&quot;"&gt;Nuevo</a>

Las imagenes de las flechas del paginador son las mismas que se utilizan para los modulos de administración.

Bien, esto ya está, así de simple te puedes hacer un paginador. En siguientes ejemplos mostraremos como añadirle campos “ordenables” y filtros.

PD: Os habréis fijado que el ejemplo solo está para el ORM Doctrine, Symfony te permite utilizar también Propel, pero en nuestro caso este último se nos quedaba muy limitado, no lo estamos infravalorando, pero no es el más adecuado para desarrollar nuestros proyectos.

Precisión decimal en Magento

Después de montar un comercio electrónico sobre Magento se ha dado el caso de que era necesario eliminar los decimales de la tienda online. Buscando por internet encontramos varias soluciones que van a parar directamente a un fichero de configuración del propio framework con el que está desarrollado Magento que no es otro que Zend Framework.

La modificación en cuestión es en el fichero /lib/Zend/Currency.php. Pues bien, esta solución es válida para versiones antiguas de Magento pero en las últimas (1.5-1.6) no funciona.

La solución para estas versiones es cambiar la precisión decimal en el fichero /app/code/core/Mage/Directory/Model/Currency.php, concretamente en la línea 197 dentro de la función format, el segundo parámetro corresponde a la precisión decimal.

Por ejemplo, para eliminar los decimales debemos cambiar esta línea:

PHP
1
return $this->formatPrecision($price, 2, $options, $includeContainer, $addBrackets);

Por esta otra:

PHP
1
return $this->formatPrecision($price, 0, $options, $includeContainer, $addBrackets);