1. #1
    Sencha User
    Join Date
    Feb 2010
    Posts
    353
    Answers
    14
    Vote Rating
    4
    maneljn is on a distinguished road

      0  

    Default Answered: How can i force store commitRecords after sync in batch mode if just one record fails

    Answered: How can i force store commitRecords after sync in batch mode if just one record fails


    Extjs 4.1.1.a

    I'm doing a store.sync() batch save
    For example i save 5 records.
    4 of them in php were succesfully saved and one have an error.
    php returns json with success: false, data: with array of 4 successfully saves records

    I need to process in failure callback of sync that the 4 records refreshed data is commit in store, because of success = false it does'nt commit any records by default.

    How ?
    -------------------
    Manel Juàrez

  2. That's my workaround

    Basically i have to return diferent structure response if succes was true or false

    If success = true, i return something like this , and sync() does all store commits automatically

    Code:
    {
        "success": true,
        "data": [...] // an array of updated or newly created records with ids 
    }
    If success = false, i have to put the 'data' inside the 'message', because 'message' is the only thing that sync() will see with operation.getError()

    Code:
    {
        "success": false,
        "message": [
              "myerrorsmessages":[{ ....... }],
              "data": [...] // an array of updated or newly created records with ids 
        ]
    }
    This is my pieces of source code working example.

    The model definition
    Code:
    //@charset UTF-8
    
    // Modelo de datos para las direcciones
    Ext.define('esicontactos.model.direccion', {
        extend: 'Ext.data.Model',
        
        idProperty: 'dir_id',
        fields: [
            { name: 'dir_id',                     type: 'integer' },
            { name: 'dir_guid',                     type: 'string' },
            { name: 'dir_contacto_id',            type: 'integer' },
            { name: 'dir_fiscal',                 type: 'boolean' },
            { name: 'dir_alias',                 type: 'string' },
            { name: 'dir_via',                    type: 'string' },
            { name: 'dir_nom',                    type: 'string' },
            { name: 'dir_num',                    type: 'integer' },
            { name: 'dir_esc',                    type: 'string' },
            { name: 'dir_pis',                    type: 'string' },
            { name: 'dir_pue',                    type: 'string' },
            { name: 'dir_direc1',                type: 'string' },
            { name: 'dir_direc2',                type: 'string' },
            { name: 'dir_pos',                    type: 'string' },
            { name: 'dir_pob',                    type: 'string' },
            { name: 'dir_pro',                    type: 'string' },
            { name: 'dir_pais_iso3',            type: 'string' },
            { name: 'dir_tel1',                    type: 'string' },
            { name: 'dir_tel2',                    type: 'string' },
            { name: 'dir_fax',                    type: 'string' },
            { name: 'dir_email',                type: 'string' },
            { name: 'dir_web',                    type: 'string' },
            { name: 'dir_activo',                type: 'boolean' },
            
            { name: 'dir_usralta_id',            type: 'integer', persist: false },
            { name: 'dir_fechaalta',             type: 'date', dateFormat: 'Y-m-d H:i:s', persist: false },            
            { name: 'dir_usrmod_id',            type: 'integer', persist: false },
            { name: 'dir_fechamod',             type: 'date', dateFormat: 'Y-m-d H:i:s' },        // Este campo se usará para el aviso de sobreescritura en concurrencia (2 usuarios grabando en el mismo registro a la vez)
            { name: 'dir_usralta_nombre',         type: 'string', persist: false },
            { name: 'dir_usrmod_nombre',         type: 'string', persist: false }
        ]
    });
    The store definition
    Code:
    //@charset UTF-8
    
    // Store para las direcciones
    Ext.define('esicontactos.store.direcciones', {
        extend: 'Ext.data.Store',
    
        requires: [
            'esicontactos.model.direccion'
        ],
    
        constructor: function(cfg) {
            var me = this;
            cfg = cfg || {};
            me.callParent([Ext.apply({
                autoLoad: false,
                autoSync: false,
                buffered: false,
                model: 'esicontactos.model.direccion',
                remoteSort: true,
                remoteFilter: true,
                pageSize: 50,
                proxy: {
                    type: 'direct',
                    batchActions: true,                        // Al ser gestionado en una sublista permitimos hacer sync() en batch mode
                    batchOrder: 'destroy,create,update',    // Cambiamos orden por defecto para que primero haga los destroy.
                    paramAsHash: true,
                    extraParams: {  
                        buscar: null
                    },
                    api: {
                        read: Ext.esicontactosDirect.esicontactos_direcciones.getDirecciones,
                        create: Ext.esicontactosDirect.esicontactos_direcciones.createDireccion,
                        update: Ext.esicontactosDirect.esicontactos_direcciones.updateDireccion,
                        destroy: Ext.esicontactosDirect.esicontactos_direcciones.destroyDireccion
                    },
                    reader: {
                        type: 'json',
                        root: 'data',
                        idProperty: 'dir_id',
                        totalProperty: 'total',
                        successProperty: 'success',
                        messageProperty : 'message'                    
                    }
                }
            }, cfg)]);
        }
    
            
    });
    The store creation
    Code:
            me.store = Ext.create('esicontactos.store.direcciones', {
                storeId: Ext.id(),
                pageSize: -1,
                // Impedimos que la reordenacion haga un query en php porque perderiamos los cambios pendientes de guardar.
                // Al no paginar los datos se ordenara localmente sin problemas.
                remoteSort: false
            });
    The controller sync()
    Code:
        syncDirecciones: function(registroContacto, panelEdicion) {
            var me= this;
            var cto_id = registroContacto.get('cto_id');
            var gridDirecciones = panelEdicion.down('esicontactos_view_direcciones_mantGrid');
            var storeDirecciones = gridDirecciones.getStore();
            var formulario = panelEdicion.formularioContactos;
            var registro = formulario.getRecord();
            var mainStatusBar = panelEdicion.up('esicontactos_view_main').down('esicontactos_view_mainStatusBar[name="mainStatusBar"]');
    
            // Comprobar si hay registros que actualizar
            if ( !(storeDirecciones.getModifiedRecords().length>0 || storeDirecciones.getRemovedRecords().length>0) ) {
                // Informar que hemos completado correctamente la tarea de guardar las direcciones  para el multiguardar
                me.completarMultiGuardar(registroContacto, panelEdicion, 'direcciones', true);
                return true;
            }
            
            // Si el contacto era nuevo y aun no se habia guardado por primera vez hay que poner el contacto_id en las direcciones
            if ( panelEdicion.modo=="nuevo" ) {
                var regsModificadosoNuevos = storeDirecciones.getModifiedRecords();
                for (var i=0; i<regsModificadosoNuevos.length; i++) {
                    regsModificadosoNuevos[i].set('dir_contacto_id',cto_id);
                }            
                var regsBorrados = storeDirecciones.getRemovedRecords();    // No haria falta ponerlo en los borrados.
                for (var i=0; i<regsBorrados.length; i++) {
                    regsBorrados[i].set('dir_contacto_id',cto_id);
                }            
            }
    
            // Mostrar "guardando" en barra de estado
            if (mainStatusBar) {
                mainStatusBar.setStatus({
                    text: gt.dgettext('esicontactos','Guardando registro ... ') + registroContacto.get('cto_nombre_fiscal') + ' ' + gt.dgettext('esicontactos','direcciones ... '),
                    iconCls: 'x-status-busy',
                    clear: false
                });
            }    
            // Mostrar mascara 
            panelEdicion.getEl().mask(gt.dgettext('esicontactos','Guardando direcciones ...'));
    
        
            // Ejecutar el sync del store (lanza en modo batch todos los destroy, create y update a través del proxy de forma asincrona)
            storeDirecciones.sync({
    
                // operaciones crud todas correctas
                success: function(batch, options) {
                    // Limpiar barra de estado
                    if (mainStatusBar) {
                        mainStatusBar.clearStatus({ useDefaults:true, anim: false });
                    }
                    panelEdicion.getEl().unmask();
                    // Informar que hemos completado correctamente la tarea de guardar las direcciones para el multiguardar
                    me.completarMultiGuardar(registroContacto, panelEdicion, 'direcciones', true);
                },                            
        
                // Alguna operacion crud con fallos
                failure: function(batch, options) {
                    
                    // Limpiar barra de estado
                    if (mainStatusBar) {
                        mainStatusBar.clearStatus({ useDefaults:true, anim: false });
                    }
                    panelEdicion.getEl().unmask();
    
                    // Cadena con el mensaje de error a mostrar
                    var mensajeError = null;                
                    
                    // Recorrer resultados de las operaciones crud generadas automaticamente por sync()
                    for (var i=0; i<batch.operations.length; i++) {
                        var operacion = batch.operations[i];
                        // Error en la operacion (solo puede ser create, update o destroy)
                        if (operacion.hasException()) {
                            // Obtener registros que han generado la operacion.
                            var registrosOperacion = operacion.getRecords();
                            // Si solo se mandó un registro el único error posible no viene en un array sino en un string directo
                            if (registrosOperacion.length==1 || (typeof operacion.getError())=="string") {
                                var registroOperacion = registrosOperacion[0];
                                if (registroOperacion) {    
                                    // Si la accion era borrar y ha fallado volver a poner el registro borrado en el store
                                    if (operacion.action=='destroy') {
                                        storeDirecciones.add(registroOperacion);
                                    }        
                                    // Montar cadena mensaje de error
                                    mensajeError = (mensajeError==null ? '' : mensajeError + '<br />') + gt.dgettext('esicontactos','Dirección:') + ' ' + registroOperacion.get('dir_alias') + '<br />' + operacion.getError();
                                }                        
                                    
                            } else {
    
                                // Si se mandaron multiples registros aunque solo haya un error viene en un array con 2 partes
                                // php $respuesta['message']['errores'] array de mensajes de error
                                // php $respuesta['message']['data']    registros refrescados despues de procesar o con el valor original si han fallado
                                // obtener array de mensajes de error si los hay (en sync batch mode sera un array con estas keys 'indexRegOpe', 'errorNum' , 'errorMsg', 'message'
                                var mensajesErrorOperacion = operacion.getError().errores;
    
                                // Montar cadena error y recuperar borrados que han fallado
                                if (mensajesErrorOperacion==null || mensajesErrorOperacion.length<=0) {
                                    mensajeError = (mensajeError==null ? '' : mensajeError + '<br />') + gt.dgettext('esicontactos','Error al guardar las direcciones.') +' ('+operacion.action+')';
                                } else {            
                                    // Bucle para montar el mensaje de error
                                    for (var j=0; j<mensajesErrorOperacion.length; j++) {
                                        if (typeof mensajesErrorOperacion[j]['indexRegOpe'] != "undefined") {
                                            if ( mensajesErrorOperacion[j]['indexRegOpe']>=0 &&  mensajesErrorOperacion[j]['indexRegOpe']<registrosOperacion.length) {
                                                // Coger el registro original que corresponde al error recibido
                                                var registroOperacion = registrosOperacion[ mensajesErrorOperacion[j]['indexRegOpe'] ];
                                                if (registroOperacion) {    
                                                    // Si la accion era borrar y ha fallado volver a poner el registro borrado en el store
                                                    if (operacion.action=='destroy') {
                                                        storeDirecciones.add(registroOperacion);
                                                    }        
                                                    // Montar cadena mensaje de error
                                                    mensajeError = (mensajeError==null ? '' : mensajeError + '<br />') + gt.dgettext('esicontactos','Dirección:') + ' ' + registroOperacion.get('dir_alias') + '<br />' + mensajesErrorOperacion[j]['message'];
                                                }                            
                                            }
                                        }
                                    }
                                }
                                
    
                                // Aunque haya errores actualizamos los otros registros  en create y update
                                // que si hayan ido bien y han devuelto el data refrescado
                                // php $respuesta['message']['data']    registros refrescados despues de procesar o con el valor original si han fallado
                                var dataRespuestaOperacion = operacion.getError().data;
                                if (dataRespuestaOperacion) {
                                    var registrosRespuestaOperacion = storeDirecciones.getProxy().getReader().read(dataRespuestaOperacion).records;
                                    if ((operacion.action=='create' || operacion.action=='update') && registrosRespuestaOperacion) {
                                        // Recorremos todos los registros originales enviados en la operacion para ver si se pueden actualizar
                                        // con su homonimo recibido del php
                                        for (var r=0; r<registrosOperacion.length; r++) {
                                            // Mirar si el index r no esta entre los registros que tiene error
                                            var regConError = false;
                                            for (var j=0; j<mensajesErrorOperacion.length; j++) {
                                                if ( mensajesErrorOperacion[j]['indexRegOpe']==r) {
                                                    regConError = true;
                                                    break;
                                                }
                                            }
                                            if (!regConError) {
                                                var registroOperacion = registrosOperacion[r];
                                                var registroRespuestaOperacion = registrosRespuestaOperacion[r];
                                                if (registroOperacion && registroRespuestaOperacion) {
                                                    // Si el registro original no es nuevo (phantom), asegurarse que los ids coinciden entre el registro original enviado y el recibido.
                                                    if(registroOperacion.getId() === registroRespuestaOperacion.getId() || registroOperacion.phantom ) {
                                                        registroOperacion.copyFrom(registroRespuestaOperacion);
                                                        registroOperacion.commit();
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } // Fin commits
                            
                            } // Fin varios registros
                        }    // Fin tiene algun error operacion
                    }    // Fin bucle operaciones
    
                    // Se muestra un solo mensaje de error con los textos de todas las operaciones
                    if (mensajeError) {
                        Ext.Msg.show({
                            title: gt.dgettext('esicontactos','Direcciones'),
                            msg: mensajeError,
                            buttons: Ext.Msg.OK,
                            icon: Ext.Msg.WARNING
                        });                        
                    }
    
                    // Puesto que si falla algun borrar se reagrega el registro al store, limpiamos el array de borrados para el siguiente sync.
                    storeDirecciones.removed = [];                                        
    
                    // Informar que hemos completado con errores la tarea de guardar las direcciones para el multiguardar
                    me.completarMultiGuardar(registroContacto, panelEdicion, 'direcciones', false);
    
                }
            });
                            
        }
    The php server side of create/update batch process
    Code:
        public static function guardarBatchDireccion( $modo, $_parametros) 
        {          
            // Cuando se usa el sync() del store en batchActions=true se reciben todos los CREATEs y UPDATEs en un solo POST 
            // y los parametros vienen en un array de objetos.
            // Sistema sync() batch mode (bucle)
            $respuestaData = null;
            $respuestaErrores = null;
            foreach($_parametros as $indexRegOpe => $_parametrosRegistro) {
                $respuestaRegistro = self::guardarDireccion( $modo, $_parametrosRegistro );
                if (!$respuestaRegistro['success']) {
                    $respuestaErrores[] = array( 
                        'indexRegOpe' => $indexRegOpe,        // Indicamos en que registro origen se produjo el error de proceso
                        'errorNum' => (isset($respuestaRegistro['errorNum']) ? $respuestaRegistro['errorNum'] : null), 
                        'errorMsg' => (isset($respuestaRegistro['errorMsg']) ? $respuestaRegistro['errorMsg'] : null), 
                        'message' => (isset($respuestaRegistro['message']) ? $respuestaRegistro['message'] : null)
                    );
                }
                // Nos autoobligamos por la estructuracion de los eventos succes i failure del sync en extjs receptor
                // a devolver siempre el mismo número de registros recibidos para procesar en el mismo orden.
                // Si tenemos datos refrescados ponemos los refrescados, y sino por error o cualquier otro motivo
                // ponemos el registro original.
                if (isset($respuestaRegistro['data'][0])) {
                    $respuestaData[] = $respuestaRegistro['data'][0]; 
                } else {
                    $respuestaData[] = $_parametrosRegistro; 
                }
            }
            // El formato de respuesta serà distinto si és succes true o false
            // Para true, anadimos el array ['data'] con los registros a devolver.
            // En la parte cliente extjs el sync hace los commits en el store automàticamente.
            // Si es false, tenemos que colar el ['data'] dentro del message que es lo único que procesa el sync () con el evento failure
            // y así en la parte cliente se pueden actualizar los registros que si que han sido correctos, mostrando error en los otros.
            if (count($respuestaErrores)>0) {
                $respuesta['success'] = false;
                $respuesta['errores'] = count($respuestaErrores);
                $respuesta['message']['errores'] = $respuestaErrores;
                $respuesta['message']['data'] = $respuestaData;
            } else {
                $respuesta['success'] = true;
                $respuesta['data'] = $respuestaData;
            }
            return $respuesta;
        }
    Like this i can send some sync() actions: multiple creates, updates, deletes and refresh/commit in store all the records that went good, and show all the error messages from all operations.

  3. #2
    Sencha - Community Support Team
    Join Date
    Jan 2012
    Posts
    1,376
    Answers
    346
    Vote Rating
    113
    vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all

      0  

    Default


    For updating/creating requests if you return successful response then all newly created/modified records will be committed, if you return failed response then all records will not be committed. So with your case, I think you have to process the response and do the committing records by yourself. You can do this by some ways:
    - Defining callbacks for store.sync():
    Code:
    store.sync({
        success: function(batch, options){
            ...
        },
        failure: function(batch, options){
            ...
        }
    })
    - Defining onCreateRecords, onUpdateRecords for the store:
    Code:
    var store = Ext.create('Ext.data.Store', {
        ...
        onCreateRecords: function(records, operation, success){
          ...
        },
        onUpdateRecords: function(records, operation, success){
          ...
        }
    });
    - Defining listeners for 'exception' event on store proxy.
    - etc

  4. #3
    Sencha User
    Join Date
    Feb 2010
    Posts
    353
    Answers
    14
    Vote Rating
    4
    maneljn is on a distinguished road

      0  

    Default


    That's my workaround

    Basically i have to return diferent structure response if succes was true or false

    If success = true, i return something like this , and sync() does all store commits automatically

    Code:
    {
        "success": true,
        "data": [...] // an array of updated or newly created records with ids 
    }
    If success = false, i have to put the 'data' inside the 'message', because 'message' is the only thing that sync() will see with operation.getError()

    Code:
    {
        "success": false,
        "message": [
              "myerrorsmessages":[{ ....... }],
              "data": [...] // an array of updated or newly created records with ids 
        ]
    }
    This is my pieces of source code working example.

    The model definition
    Code:
    //@charset UTF-8
    
    // Modelo de datos para las direcciones
    Ext.define('esicontactos.model.direccion', {
        extend: 'Ext.data.Model',
        
        idProperty: 'dir_id',
        fields: [
            { name: 'dir_id',                     type: 'integer' },
            { name: 'dir_guid',                     type: 'string' },
            { name: 'dir_contacto_id',            type: 'integer' },
            { name: 'dir_fiscal',                 type: 'boolean' },
            { name: 'dir_alias',                 type: 'string' },
            { name: 'dir_via',                    type: 'string' },
            { name: 'dir_nom',                    type: 'string' },
            { name: 'dir_num',                    type: 'integer' },
            { name: 'dir_esc',                    type: 'string' },
            { name: 'dir_pis',                    type: 'string' },
            { name: 'dir_pue',                    type: 'string' },
            { name: 'dir_direc1',                type: 'string' },
            { name: 'dir_direc2',                type: 'string' },
            { name: 'dir_pos',                    type: 'string' },
            { name: 'dir_pob',                    type: 'string' },
            { name: 'dir_pro',                    type: 'string' },
            { name: 'dir_pais_iso3',            type: 'string' },
            { name: 'dir_tel1',                    type: 'string' },
            { name: 'dir_tel2',                    type: 'string' },
            { name: 'dir_fax',                    type: 'string' },
            { name: 'dir_email',                type: 'string' },
            { name: 'dir_web',                    type: 'string' },
            { name: 'dir_activo',                type: 'boolean' },
            
            { name: 'dir_usralta_id',            type: 'integer', persist: false },
            { name: 'dir_fechaalta',             type: 'date', dateFormat: 'Y-m-d H:i:s', persist: false },            
            { name: 'dir_usrmod_id',            type: 'integer', persist: false },
            { name: 'dir_fechamod',             type: 'date', dateFormat: 'Y-m-d H:i:s' },        // Este campo se usará para el aviso de sobreescritura en concurrencia (2 usuarios grabando en el mismo registro a la vez)
            { name: 'dir_usralta_nombre',         type: 'string', persist: false },
            { name: 'dir_usrmod_nombre',         type: 'string', persist: false }
        ]
    });
    The store definition
    Code:
    //@charset UTF-8
    
    // Store para las direcciones
    Ext.define('esicontactos.store.direcciones', {
        extend: 'Ext.data.Store',
    
        requires: [
            'esicontactos.model.direccion'
        ],
    
        constructor: function(cfg) {
            var me = this;
            cfg = cfg || {};
            me.callParent([Ext.apply({
                autoLoad: false,
                autoSync: false,
                buffered: false,
                model: 'esicontactos.model.direccion',
                remoteSort: true,
                remoteFilter: true,
                pageSize: 50,
                proxy: {
                    type: 'direct',
                    batchActions: true,                        // Al ser gestionado en una sublista permitimos hacer sync() en batch mode
                    batchOrder: 'destroy,create,update',    // Cambiamos orden por defecto para que primero haga los destroy.
                    paramAsHash: true,
                    extraParams: {  
                        buscar: null
                    },
                    api: {
                        read: Ext.esicontactosDirect.esicontactos_direcciones.getDirecciones,
                        create: Ext.esicontactosDirect.esicontactos_direcciones.createDireccion,
                        update: Ext.esicontactosDirect.esicontactos_direcciones.updateDireccion,
                        destroy: Ext.esicontactosDirect.esicontactos_direcciones.destroyDireccion
                    },
                    reader: {
                        type: 'json',
                        root: 'data',
                        idProperty: 'dir_id',
                        totalProperty: 'total',
                        successProperty: 'success',
                        messageProperty : 'message'                    
                    }
                }
            }, cfg)]);
        }
    
            
    });
    The store creation
    Code:
            me.store = Ext.create('esicontactos.store.direcciones', {
                storeId: Ext.id(),
                pageSize: -1,
                // Impedimos que la reordenacion haga un query en php porque perderiamos los cambios pendientes de guardar.
                // Al no paginar los datos se ordenara localmente sin problemas.
                remoteSort: false
            });
    The controller sync()
    Code:
        syncDirecciones: function(registroContacto, panelEdicion) {
            var me= this;
            var cto_id = registroContacto.get('cto_id');
            var gridDirecciones = panelEdicion.down('esicontactos_view_direcciones_mantGrid');
            var storeDirecciones = gridDirecciones.getStore();
            var formulario = panelEdicion.formularioContactos;
            var registro = formulario.getRecord();
            var mainStatusBar = panelEdicion.up('esicontactos_view_main').down('esicontactos_view_mainStatusBar[name="mainStatusBar"]');
    
            // Comprobar si hay registros que actualizar
            if ( !(storeDirecciones.getModifiedRecords().length>0 || storeDirecciones.getRemovedRecords().length>0) ) {
                // Informar que hemos completado correctamente la tarea de guardar las direcciones  para el multiguardar
                me.completarMultiGuardar(registroContacto, panelEdicion, 'direcciones', true);
                return true;
            }
            
            // Si el contacto era nuevo y aun no se habia guardado por primera vez hay que poner el contacto_id en las direcciones
            if ( panelEdicion.modo=="nuevo" ) {
                var regsModificadosoNuevos = storeDirecciones.getModifiedRecords();
                for (var i=0; i<regsModificadosoNuevos.length; i++) {
                    regsModificadosoNuevos[i].set('dir_contacto_id',cto_id);
                }            
                var regsBorrados = storeDirecciones.getRemovedRecords();    // No haria falta ponerlo en los borrados.
                for (var i=0; i<regsBorrados.length; i++) {
                    regsBorrados[i].set('dir_contacto_id',cto_id);
                }            
            }
    
            // Mostrar "guardando" en barra de estado
            if (mainStatusBar) {
                mainStatusBar.setStatus({
                    text: gt.dgettext('esicontactos','Guardando registro ... ') + registroContacto.get('cto_nombre_fiscal') + ' ' + gt.dgettext('esicontactos','direcciones ... '),
                    iconCls: 'x-status-busy',
                    clear: false
                });
            }    
            // Mostrar mascara 
            panelEdicion.getEl().mask(gt.dgettext('esicontactos','Guardando direcciones ...'));
    
        
            // Ejecutar el sync del store (lanza en modo batch todos los destroy, create y update a través del proxy de forma asincrona)
            storeDirecciones.sync({
    
                // operaciones crud todas correctas
                success: function(batch, options) {
                    // Limpiar barra de estado
                    if (mainStatusBar) {
                        mainStatusBar.clearStatus({ useDefaults:true, anim: false });
                    }
                    panelEdicion.getEl().unmask();
                    // Informar que hemos completado correctamente la tarea de guardar las direcciones para el multiguardar
                    me.completarMultiGuardar(registroContacto, panelEdicion, 'direcciones', true);
                },                            
        
                // Alguna operacion crud con fallos
                failure: function(batch, options) {
                    
                    // Limpiar barra de estado
                    if (mainStatusBar) {
                        mainStatusBar.clearStatus({ useDefaults:true, anim: false });
                    }
                    panelEdicion.getEl().unmask();
    
                    // Cadena con el mensaje de error a mostrar
                    var mensajeError = null;                
                    
                    // Recorrer resultados de las operaciones crud generadas automaticamente por sync()
                    for (var i=0; i<batch.operations.length; i++) {
                        var operacion = batch.operations[i];
                        // Error en la operacion (solo puede ser create, update o destroy)
                        if (operacion.hasException()) {
                            // Obtener registros que han generado la operacion.
                            var registrosOperacion = operacion.getRecords();
                            // Si solo se mandó un registro el único error posible no viene en un array sino en un string directo
                            if (registrosOperacion.length==1 || (typeof operacion.getError())=="string") {
                                var registroOperacion = registrosOperacion[0];
                                if (registroOperacion) {    
                                    // Si la accion era borrar y ha fallado volver a poner el registro borrado en el store
                                    if (operacion.action=='destroy') {
                                        storeDirecciones.add(registroOperacion);
                                    }        
                                    // Montar cadena mensaje de error
                                    mensajeError = (mensajeError==null ? '' : mensajeError + '<br />') + gt.dgettext('esicontactos','Dirección:') + ' ' + registroOperacion.get('dir_alias') + '<br />' + operacion.getError();
                                }                        
                                    
                            } else {
    
                                // Si se mandaron multiples registros aunque solo haya un error viene en un array con 2 partes
                                // php $respuesta['message']['errores'] array de mensajes de error
                                // php $respuesta['message']['data']    registros refrescados despues de procesar o con el valor original si han fallado
                                // obtener array de mensajes de error si los hay (en sync batch mode sera un array con estas keys 'indexRegOpe', 'errorNum' , 'errorMsg', 'message'
                                var mensajesErrorOperacion = operacion.getError().errores;
    
                                // Montar cadena error y recuperar borrados que han fallado
                                if (mensajesErrorOperacion==null || mensajesErrorOperacion.length<=0) {
                                    mensajeError = (mensajeError==null ? '' : mensajeError + '<br />') + gt.dgettext('esicontactos','Error al guardar las direcciones.') +' ('+operacion.action+')';
                                } else {            
                                    // Bucle para montar el mensaje de error
                                    for (var j=0; j<mensajesErrorOperacion.length; j++) {
                                        if (typeof mensajesErrorOperacion[j]['indexRegOpe'] != "undefined") {
                                            if ( mensajesErrorOperacion[j]['indexRegOpe']>=0 &&  mensajesErrorOperacion[j]['indexRegOpe']<registrosOperacion.length) {
                                                // Coger el registro original que corresponde al error recibido
                                                var registroOperacion = registrosOperacion[ mensajesErrorOperacion[j]['indexRegOpe'] ];
                                                if (registroOperacion) {    
                                                    // Si la accion era borrar y ha fallado volver a poner el registro borrado en el store
                                                    if (operacion.action=='destroy') {
                                                        storeDirecciones.add(registroOperacion);
                                                    }        
                                                    // Montar cadena mensaje de error
                                                    mensajeError = (mensajeError==null ? '' : mensajeError + '<br />') + gt.dgettext('esicontactos','Dirección:') + ' ' + registroOperacion.get('dir_alias') + '<br />' + mensajesErrorOperacion[j]['message'];
                                                }                            
                                            }
                                        }
                                    }
                                }
                                
    
                                // Aunque haya errores actualizamos los otros registros  en create y update
                                // que si hayan ido bien y han devuelto el data refrescado
                                // php $respuesta['message']['data']    registros refrescados despues de procesar o con el valor original si han fallado
                                var dataRespuestaOperacion = operacion.getError().data;
                                if (dataRespuestaOperacion) {
                                    var registrosRespuestaOperacion = storeDirecciones.getProxy().getReader().read(dataRespuestaOperacion).records;
                                    if ((operacion.action=='create' || operacion.action=='update') && registrosRespuestaOperacion) {
                                        // Recorremos todos los registros originales enviados en la operacion para ver si se pueden actualizar
                                        // con su homonimo recibido del php
                                        for (var r=0; r<registrosOperacion.length; r++) {
                                            // Mirar si el index r no esta entre los registros que tiene error
                                            var regConError = false;
                                            for (var j=0; j<mensajesErrorOperacion.length; j++) {
                                                if ( mensajesErrorOperacion[j]['indexRegOpe']==r) {
                                                    regConError = true;
                                                    break;
                                                }
                                            }
                                            if (!regConError) {
                                                var registroOperacion = registrosOperacion[r];
                                                var registroRespuestaOperacion = registrosRespuestaOperacion[r];
                                                if (registroOperacion && registroRespuestaOperacion) {
                                                    // Si el registro original no es nuevo (phantom), asegurarse que los ids coinciden entre el registro original enviado y el recibido.
                                                    if(registroOperacion.getId() === registroRespuestaOperacion.getId() || registroOperacion.phantom ) {
                                                        registroOperacion.copyFrom(registroRespuestaOperacion);
                                                        registroOperacion.commit();
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } // Fin commits
                            
                            } // Fin varios registros
                        }    // Fin tiene algun error operacion
                    }    // Fin bucle operaciones
    
                    // Se muestra un solo mensaje de error con los textos de todas las operaciones
                    if (mensajeError) {
                        Ext.Msg.show({
                            title: gt.dgettext('esicontactos','Direcciones'),
                            msg: mensajeError,
                            buttons: Ext.Msg.OK,
                            icon: Ext.Msg.WARNING
                        });                        
                    }
    
                    // Puesto que si falla algun borrar se reagrega el registro al store, limpiamos el array de borrados para el siguiente sync.
                    storeDirecciones.removed = [];                                        
    
                    // Informar que hemos completado con errores la tarea de guardar las direcciones para el multiguardar
                    me.completarMultiGuardar(registroContacto, panelEdicion, 'direcciones', false);
    
                }
            });
                            
        }
    The php server side of create/update batch process
    Code:
        public static function guardarBatchDireccion( $modo, $_parametros) 
        {          
            // Cuando se usa el sync() del store en batchActions=true se reciben todos los CREATEs y UPDATEs en un solo POST 
            // y los parametros vienen en un array de objetos.
            // Sistema sync() batch mode (bucle)
            $respuestaData = null;
            $respuestaErrores = null;
            foreach($_parametros as $indexRegOpe => $_parametrosRegistro) {
                $respuestaRegistro = self::guardarDireccion( $modo, $_parametrosRegistro );
                if (!$respuestaRegistro['success']) {
                    $respuestaErrores[] = array( 
                        'indexRegOpe' => $indexRegOpe,        // Indicamos en que registro origen se produjo el error de proceso
                        'errorNum' => (isset($respuestaRegistro['errorNum']) ? $respuestaRegistro['errorNum'] : null), 
                        'errorMsg' => (isset($respuestaRegistro['errorMsg']) ? $respuestaRegistro['errorMsg'] : null), 
                        'message' => (isset($respuestaRegistro['message']) ? $respuestaRegistro['message'] : null)
                    );
                }
                // Nos autoobligamos por la estructuracion de los eventos succes i failure del sync en extjs receptor
                // a devolver siempre el mismo número de registros recibidos para procesar en el mismo orden.
                // Si tenemos datos refrescados ponemos los refrescados, y sino por error o cualquier otro motivo
                // ponemos el registro original.
                if (isset($respuestaRegistro['data'][0])) {
                    $respuestaData[] = $respuestaRegistro['data'][0]; 
                } else {
                    $respuestaData[] = $_parametrosRegistro; 
                }
            }
            // El formato de respuesta serà distinto si és succes true o false
            // Para true, anadimos el array ['data'] con los registros a devolver.
            // En la parte cliente extjs el sync hace los commits en el store automàticamente.
            // Si es false, tenemos que colar el ['data'] dentro del message que es lo único que procesa el sync () con el evento failure
            // y así en la parte cliente se pueden actualizar los registros que si que han sido correctos, mostrando error en los otros.
            if (count($respuestaErrores)>0) {
                $respuesta['success'] = false;
                $respuesta['errores'] = count($respuestaErrores);
                $respuesta['message']['errores'] = $respuestaErrores;
                $respuesta['message']['data'] = $respuestaData;
            } else {
                $respuesta['success'] = true;
                $respuesta['data'] = $respuestaData;
            }
            return $respuesta;
        }
    Like this i can send some sync() actions: multiple creates, updates, deletes and refresh/commit in store all the records that went good, and show all the error messages from all operations.
    -------------------
    Manel Juàrez

Thread Participants: 1

Turkiyenin en sevilen filmlerinin yer aldigi xnxx internet sitemiz olan ve porn sex tarzi bir site olan mobil porno izle sitemiz gercekten dillere destan bir durumda herkesin sevdigi bir site olarak tarihe gececege benziyor. Sitenin en belirgin ozelliklerinden birisi de Turkiyede gercekten kaliteli ve muntazam, duzenli porno izle siteleri olmamasidir. Bu yuzden iste. Ayrica en net goruntu kalitesine sahip adresinde yayinlanmaktadir. Mesela diğer sitelerimizden bahsedecek olursak, en iyi hd porno video arşivine sahip bir siteyiz. "The Best anal porn videos and slut anus, big asses movies set..." hd porno faketaxi