<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Desarrollosweb.net &#187; Zend Framework</title>
	<atom:link href="http://www.desarrollosweb.net/tag/zend-framework/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.desarrollosweb.net</link>
	<description>Desarrollo de webs y aplicaciones</description>
	<lastBuildDate>Fri, 26 Mar 2010 10:53:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>ExtJS CookBook: Código base</title>
		<link>http://www.desarrollosweb.net/2008/12/03/extjs-cookbook-codigo-base/</link>
		<comments>http://www.desarrollosweb.net/2008/12/03/extjs-cookbook-codigo-base/#comments</comments>
		<pubDate>Wed, 03 Dec 2008 09:16:17 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=49</guid>
		<description><![CDATA[Voy a iniciar una serie de artículos a los que voy a denominar &#8216;ExtJS Cookbook&#8217;, en los que iré explicando pequeñas acciones o trucos de ExtJS pero que por ser muy comunes merecen un poco de atención La primera parte de esta serie de artículos se van a basar en el código de una miniaplicación [...]]]></description>
			<content:encoded><![CDATA[<p>Voy a iniciar una serie de artículos a los que voy a denominar &#8216;ExtJS Cookbook&#8217;, en los que iré explicando pequeñas acciones o trucos de ExtJS pero que por ser muy comunes merecen un poco de atención</p>
<p>La primera parte de esta serie de artículos se van a basar en el código de una miniaplicación para gestión de un rudimentario libro de cuentas y que <a href="http://appsrv01.desarrollosweb.net/blog/extjs/cuentas/" target="_blank">tenéis disponible</a> para evaluar</p>
<p>Este ejemplo muestra cómo hacer varias cosas: cargar datos desde PHP en Extjs, cargar los datos de un FormPanel desde un GridPanel, filtrar un GridPanel desde un formulario externo, cómo obtener los datos de los campos de un FormPanel, etc.
<p>Espero que los artículos que publique estos días os sean de utilidad</p>
<h2>Fichero 1: Código Javascript de la miniaplicación</h2>
<pre name="code" class="javascript">
/*
 * Definimos nuestros propios validadores de datos
 * para los formularios
 */
Ext.apply(Ext.form.VTypes, {
    daterange : function(val, field) {
        var date = field.parseDate(val);

        if(!date){
            return;
        }
        if (field.startDateField &#038;&#038; (!this.dateRangeMax || (date.getTime() != this.dateRangeMax.getTime()))) {
            var start = Ext.getCmp(field.startDateField);
            start.setMaxValue(date);
            start.validate();
            this.dateRangeMax = date;
        }
        else if (field.endDateField &#038;&#038; (!this.dateRangeMin || (date.getTime() != this.dateRangeMin.getTime()))) {
            var end = Ext.getCmp(field.endDateField);
            end.setMinValue(date);
            end.validate();
            this.dateRangeMin = date;
        }
        return true;
    },
    /**
     * Definimos un nuevo validador: exclusionNotZero
     *
     * Valida un par de campos numéricos para que no puedan tener valor positivo los dos ni
     * valor cero los dos.
     *
     **/
    exclusionNotZero: function(val, field) {
        if (field.exclusionPair) {
            var pair = Ext.getCmp(field.exclusionPair);
            var pairValue = pair.getValue();

            if ((pairValue > 0) &#038;&#038; (val > 0)) {
                field.markInvalid('Un movimiento no puede tener a la vez haber y deber');
                return false;
            } else if ((pairValue == 0) &#038;&#038; (val == 0)){
                field.markInvalid('Un movimiento no puede tener haber y deber a cero');
                return false;
            } else {
                field.clearInvalid();
                pair.clearInvalid();
                return true;
            }
        }
        return true;
    }
});

Ext.onReady(function() {
    /*
     * Creamos un espacio de nombres
     */
    Ext.namespace('GestorCuentas');

    /*
     * Definimos el registro para un movimiento
     */
    GestorCuentas.movimientoRecord = new Ext.data.Record.create([
        {name: 'mov_id', type: 'int'},
        {name: 'mov_concepto', type: 'string'},
        {name: 'mov_fecha', type: 'date', dateFormat: 'Y-m-d'},
        {name: 'mov_deber', type: 'float'},
        {name: 'mov_haber', type: 'float'}
    ]);

    /*
     * Creamos el reader para el Grid de movimientos
     */
    GestorCuentas.movimientosGridReader = new Ext.data.JsonReader({
        root: 'data',
        totalProperty: 'total',
        id: 'mov_id'},
        GestorCuentas.movimientoRecord
    );

    /*
     * Creamos el DataProxy para carga remota de los datos
     */
    GestorCuentas.movimientosDataProxy = new Ext.data.HttpProxy({
        url: 'cargaMovimientos.php',
        method: 'POST'
    });

    GestorCuentas.movimientosDataStore = new Ext.data.Store({
        id: 'movimientosDS',
        proxy: GestorCuentas.movimientosDataProxy,
        reader: GestorCuentas.movimientosGridReader
    });

    /*
     * Creamos el modelo de columnas para el grid de movimientos de la cuenta
     */
    GestorCuentas.movimientosColumnMode = new Ext.grid.ColumnModel(
        [{
            header: 'Fecha',
            dataIndex: 'mov_fecha',
            width: 80,
            renderer: Ext.util.Format.dateRenderer('d/m/Y')
        },{
            header: 'Concepto',
            dataIndex: 'mov_concepto',
            width: 200
        },{
            header: 'Haber',
            dataIndex: 'mov_haber',
            width: 90
        },{
            header: 'Deber',
            dataIndex: 'mov_deber',
            width: 90
        }]
    );

    /*
     * Creamos el reader para el formulario de alta/modificación de movimientos
     */
    GestorCuentas.movimientosFormReader = new Ext.data.JsonReader({
        root : 'data',
        successProperty : 'success',
        totalProperty: 'total',
        id: 'mov_id'
        },GestorCuentas.movimientoRecord
    );

    /*
     * Creamos el grid de movimientos
     */
    GestorCuentas.movimientosGrid = new Ext.grid.GridPanel({
        id : 'mov-movimientos-grid',
        store : GestorCuentas.movimientosDataStore,
        cm : GestorCuentas.movimientosColumnMode,
        enableColLock : false,
        width : 750,
        height : 550,
        bbar : new Ext.PagingToolbar({
            pageSize: 20,
            store: GestorCuentas.movimientosDataStore,
            displayInfo: true
        }),
        selModel : new Ext.grid.RowSelectionModel({singleSelect:false})
    });

    /*
     * Creamos el formulario para filtrar el grid
     */
    GestorCuentas.filterForm = new Ext.FormPanel({
        id: 'form-filtro',
        region: 'north',
        split: false,
        frame: true,
        height: 100,
        width: 800,
        items: [{
            xtype: 'datefield',
            width: 200,
            fieldLabel: 'Desde',
            name: 'startdt',
            id: 'startdt',
            vtype: 'daterange',
            endDateField: 'enddt' // id of the end date field
        },{
            xtype: 'datefield',
            width: 200,
            fieldLabel: 'Hasta',
            name: 'enddt',
            id: 'enddt',
            vtype: 'daterange',
            startDateField: 'startdt' // id of the start date field
        }]
    });

    /*
     * Añadimos el botón para borrar el filtro
     */
    GestorCuentas.filterForm.addButton({
        text : 'Borrar filtro',
        disabled : false,
        handler : function() {
            GestorCuentas.filterForm.getForm().reset();
            GestorCuentas.movimientosDataStore.baseParams = {
                dateStart: '',
                dateEnd: ''
            };
            GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
        }
    });

    /*
     * Añadimos el botón para filtrar
     */
    GestorCuentas.filterForm.addButton({
        text : 'Filtrar',
        disabled : false,
        handler : function() {
            var startDate = GestorCuentas.filterForm.findById('startdt').getValue();
            var endDate = GestorCuentas.filterForm.findById('enddt').getValue();

            GestorCuentas.movimientosDataStore.baseParams = {
                dateStart: startDate.dateFormat('Y-m-d'),
                dateEnd: endDate.dateFormat('Y-m-d')
            };
            GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
        }
    });

    /*
     * Creamos el formulario de alta/modificación de movimientos
     */
    GestorCuentas.movForm = new Ext.FormPanel({
        id: 'form-movimientos',
        region: 'west',
        split: false,
        collapsible: true,
        frame: true,
        width: 300,
        minWidth: 300,
        height: 500,
        waitMsgTarget: true,
        reader: GestorCuentas.movimientosFormReader,
        items: [{
            fieldLabel : 'Haber',
            xtype: 'numberfield',
            id: 'frm_mov_haber',
            name : 'mov_haber',
            exclusionPair: 'frm_mov_deber',
            vtype: 'exclusionNotZero',
            allowBlank:false,
            width : 175
        },{
            fieldLabel : 'Deber',
            xtype: 'numberfield',
            id: 'frm_mov_deber',
            name : 'mov_deber',
            exclusionPair: 'frm_mov_haber',
            vtype: 'exclusionNotZero',
            allowBlank:false,
            width : 175
        }, {
            fieldLabel : 'Concepto',
            id: 'frm_mov_concepto',
            name : 'mov_concepto',
            allowBlank:false,
            xtype: 'textarea',
            width : 175,
            height: 225
        }, {
            fieldLabel : 'Fecha',
            id: 'frm_mov_fecha',
            name : 'mov_fecha',
            allowBlank:false,
            xtype: 'datefield',
            renderer: Ext.util.Format.dateRenderer('d/m/Y'),
            width : 175
        }, {
            id: 'frm_mov_id',
            name : 'mov_id',
            xtype: 'hidden'
        }]
    });

    /*
     * Añadimos el botón para borrar el formulario
     */
    GestorCuentas.movForm.addButton({
        text : 'Borrar formulario',
        disabled : false,
        handler : function() {
            GestorCuentas.movForm.getForm().reset();
        }
    });

    /*
     * Añadimos el botón para guardar los datos del formulario
     */
    GestorCuentas.movForm.addButton({
        text : 'Guardar',
        disabled : false,
        handler : function() {
            GestorCuentas.movForm.getForm().submit({
                url : 'salvaMovimientos.php',
                waitMsg : 'Salvando datos...',
                failure: function (form, action) {
                    Ext.MessageBox.show({
                        title: 'Error al salvar los datos',
                        msg: 'Error al salvar los datos.',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.ERROR
                    });
                },
                success: function (form, request) {
                    Ext.MessageBox.show({
                        title: 'Datos salvados correctamente',
                        msg: 'Datos salvados correctamente',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.INFO
                    });
                    responseData = Ext.util.JSON.decode(request.response.responseText);
                    GestorCuentas.movForm.getForm().reset();
                    GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
                }
            });
        }
    });

    /*
     * Añadimos el evento doble click en una fila para editar el registro correspondiente
     */
    GestorCuentas.movimientosGrid.on('rowdblclick',function( grid, row, evt) {
        var movRecord = GestorCuentas.movimientosDataStore.getAt(row);
        GestorCuentas.movForm.getForm().load({
            url : 'cargaMovimiento.php',
            method: 'POST',
            params: {
                mov_id: movRecord.data.mov_id
            },
            waitMsg : 'Espere por favor'
        });
    });

    /*
     * Creamos la ventana
     */
    GestorCuentas.contentWindow = new Ext.Window({
            id: 'libro-cuentas',
            title:'Gestor Libro de Cuentas',
            width:860,
            height:600,
            iconCls: 'icon-grid',
            shim:false,
            animCollapse:false,
            constrainHeader:true,
            layout: 'border',
            renderTo: document.body,
            items:[GestorCuentas.filterForm, GestorCuentas.movForm, {
                    id: 'col-grid-movimientos',
                    xtype: 'panel',
                    region: 'center',
                    width: 560,
                    layout: 'fit',
                    items:[GestorCuentas.movimientosGrid]
                }
            ]
    });

    /*
     * Mostramos ventana, la centramos y cargamos los datos iniciales en el grid
     */
    GestorCuentas.contentWindow.show();
    GestorCuentas.contentWindow.center();
    GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
});
</pre>
<h2>Fichero 2: PHP de carga de datos del grid</h2>
<pre name="code" class="php">
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

require_once 'Config/ConfigCuentas.php';

$db->setFetchMode(Zend_Db::FETCH_OBJ);

if (!empty($_POST['dateStart']) &#038;&#038; !empty($_POST['dateEnd'])) {
    $filterClause = "WHERE (mov_fecha >= '".$_POST['dateStart']."' AND mov_fecha <= '".$_POST['dateEnd']."')";
} else {
    $filterClause = ' ';
}

if (!empty($_POST['limit']) &#038;&#038; !empty($_POST['start'])) {
    $limit = $_POST['limit'];
    $start = $_POST['start'];
    $limitClause = "LIMIT $start,$limit";
} else {
    $limitClause = ' ';
}

$query = "SELECT * FROM mov_movimientos $filterClause ORDER BY mov_fecha ASC $limitClause";

$result = $db->fetchAssoc($query);

$resultados = array();

foreach ($result as $record) {
    $resultados[] = $record;
}

$retValue = array (
    'total' => count($result),
    'data' => $resultados
);

echo json_encode($retValue);
</pre>
<h2>Fichero 3: PHP de carga de datos del formulario</h2>
<pre name="code" class="php">
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

require_once 'Config/ConfigCuentas.php';

$query = "SELECT * FROM mov_movimientos WHERE mov_id=" . $_POST['mov_id'];

$db->setFetchMode(Zend_Db::FETCH_OBJ);
$result = $db->fetchAssoc($query);

$record = $result[$_POST['mov_id']];

$resultados = array();
$retValue = array (
    'total' => count($result),
    'success'=>true,
    'data' => array (
        array(
            'mov_id' => $_POST['mov_id'],
            'mov_concepto' => $record['mov_concepto'],
            'mov_haber' => $record['mov_haber'],
            'mov_deber' => $record['mov_deber'],
            'mov_fecha' => $record['mov_fecha']
        )
    )
);
echo json_encode($retValue);
</pre>
<h2>Fichero 4: PHP de guardado de datos desde el formulario</h2>
<pre name="code" class="php">
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

require_once 'Config/ConfigCuentas.php';

if (is_null($_POST['mov_id']) || empty($_POST['mov_id'])) {
    $query = "INSERT INTO mov_movimientos(mov_fecha,mov_concepto,mov_haber,mov_deber) "
            ."VALUES(STR_TO_DATE('". $_POST['mov_fecha'] ."', '%d/%m/%Y'),'". $_POST['mov_concepto'] ."',".$_POST['mov_haber'].",".$_POST['mov_deber'].")";
} else {
    $query = "UPDATE mov_movimientos SET "
            ."mov_fecha = STR_TO_DATE('". $_POST['mov_fecha'] ."', '%d/%m/%Y'),"
            ."mov_concepto = '" . $_POST['mov_concepto'] ."',"
            ."mov_haber = " . $_POST['mov_haber'] . ","
            ."mov_deber = " . $_POST['mov_deber'] . " "
            ."WHERE mov_id = " . $_POST['mov_id'];
}

$db->setFetchMode(Zend_Db::FETCH_OBJ);
$result = $db->query($query);
$lastInsert = $db->lastInsertId();

$resultados = array();

$retValue = array (
    'total' => count($result),
    'success'=>true,
    'data' => array (
        array(
            'mov_id' => $mov_id
        )
    )
);

echo json_encode($retValue);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/12/03/extjs-cookbook-codigo-base/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Depurando código con FirePHP y Zend Framework, o como decir adiós a los var_dump()</title>
		<link>http://www.desarrollosweb.net/2008/12/01/depurando-codigo-con-firephp-y-zend-framework-o-como-decir-adios-a-los-var_dump/</link>
		<comments>http://www.desarrollosweb.net/2008/12/01/depurando-codigo-con-firephp-y-zend-framework-o-como-decir-adios-a-los-var_dump/#comments</comments>
		<pubDate>Mon, 01 Dec 2008 11:02:34 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[FirePHP]]></category>
		<category><![CDATA[depuración]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Firebug]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=46</guid>
		<description><![CDATA[Todos los que llevamos tiempo desarrollando en PHP sabemos que una de las tareas más tediosas es la depuración de nuestros scritps, y mucho más si estos están alimentando la respuesta de una llamada AJAX que tiene que devolver los datos en un cierto formato (JSON por ejemplo). Esta tarea se puede hacer más &#8220;agradable&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>Todos los que llevamos tiempo desarrollando en PHP sabemos que una de las tareas más tediosas es la depuración de nuestros scritps, y mucho más si estos están alimentando la respuesta de una llamada AJAX que tiene que devolver los datos en un cierto formato (JSON por ejemplo). Esta tarea se puede hacer más &#8220;agradable&#8221; si disponemos de un IDE con depuración integrada, como puede ser Zend Studio, o recientemente Eclipse con el módulo PDT</p>
<p>A pesar de la comodidad de tener el depurador integrado en el entorno de desarrollo, muchas veces se hace un poco engorroso tener que iniciar una sesión de depuración cada vez que quieres saber qué datos están pasando por tus variables</p>
<p>Pues bien, para facilitar la vida a los desarrolladores tenemos disponible un nuevo complemento para firefox: FirePHP. Este componente nos permite depurar directamente nuestros scripts PHP en la consola del firebug</p>
<p>La herramienta se basa en enviar unas ciertas cabeceras HTTP desde nuestros scripts PHP y que son capturadas por el plugin. De esta forma, la salida de nuestros scripts no se ve alterada (perfecto para llamadas AJAX que devuelvan un XML o un JSON) y ganamos rapidez en la depuración</p>
<p>FirePHP está ganando popularidad rápidamente entre los desarrolladores y de hecho muchos frameworks como Zend Framework o Symphony están incorporando en su API llamadas para depurar con esta herramienta</p>
<p>Si no estamos usando ninguno de estos frameworks, el equipo de desarrollo de FirePHP nos ofrece una pequeña librería en PHP5 que podemos integrar fácilmente en nuestros scripts</p>
<p>Lo primero que tenemos que hacer es instalar la extensión FirePHP en nuestro Firefox. Luego descargamos FirePHPCore, descomprimimos el contenido en el directorio donde estemos almacenando las librerías que usamos y ya sólo nos queda hacer las llamadas pertinentes tal y <a href="http://www.firephp.org/HQ/Use.htm" target="_blank">como se nos explica en este tutorial</a></p>
<p>Si estamos usando Zend Framework y su Modelo Vista Controlador (MVC) también podemos integrar FirePHPCore de forma fácil, si por un casual no nos gustase el API que nos ofrece el propio framework</p>
<p>Primero registramos el logger en el bootstrap.php de nuestra aplicación:</p>
<pre name="code" class="php">//Incluímos la librería
require_once('FirePHPCore/FirePHP.class.php');

// Instanciamos el logger y lo registramos
$firebugLogger = FirePHP::getInstance(true);
Zend_Registry::set('firebugLogger',$firebugLogger);</pre>
<p>Luego en cualquier parte de nuestro código podemos invocar al logger, como por ejemplo en una acción de un controlador:</p>
<pre name="code" class="php">    public function indexAction() {
        // Obtenemos la instancia del logger previamente registrada
        // en el bootstrap.php
        $fbLogger = Zend_Registry::get('firebugLogger');

        // Mensajes de log
        $fbLogger-&gt;log('Mensaje');
        $fbLogger-&gt;info('Mensaje nivel INFO');
        $fbLogger-&gt;warn('Mensaje nivel WARNING');
        $fbLogger-&gt;error('Mensaje niverl ERROR');

        // Mensaje de log con etiqueta adicional
        $fbLogger-&gt;log('Mensaje','Etiqueta Opcional');

        // Podemos mostrar una tabla de datos
        $table   = array();
        $table[] = array('Columna 1','Columna 2');
        $table[] = array('1:1','1:2');
        $table[] = array('2:1','2:2');
        $table[] = array('3:1','3:2');
        $fbLogger-&gt;table('Tabla de datos', $table);  // or FB::

        // También podemos mostrar información de traza
        $fbLogger-&gt;trace('Trace Label');
    }</pre>
<p>Aquí podemos ver cómo queda la salida en la consola del Firebug:</p>
<p style="text-align: center;"><a href="http://www.desarrollosweb.net/wp-content/captura_firephp.png"><img class="size-medium wp-image-47 aligncenter" title="captura_firephp" src="http://www.desarrollosweb.net/wp-content/captura_firephp-300x234.png" alt="" width="300" height="234" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/12/01/depurando-codigo-con-firephp-y-zend-framework-o-como-decir-adios-a-los-var_dump/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
