Ext JS 4: Salvar Model/Store que tenha Associations (HasMany e HasOne)

Olá pessoal,

Com a chegada do Ext JS 4, a Sencha introduziu as associações (Associations) no data package. Todo mundo ficou muito contente, mas infelizmente essa parte das Associations não foi 100% implementada como a comunidade esperou.

extjs-associations-writer-loiane

Só se pode carregar dados aninhados (nested data/json) usando Associations (você pode assistir um tutorial sobre isso aqui). Mas e a parte de salvar, enviar os dados aninhados de volta para o sever, como é que fica? Pois é, não fica, ou seja, não é possível via API nativa do Ext JS.

Mas...  a gente dá um jeito em tudo. Existem uns workarounds, maneiras de fazer isso funcionar. E não é gambiarra, é apenas modificar o código do Ext JS para que funcione do jeito que a gente quer.

Para salvar dados via data package existem duas formas: fazer um sync na Store ou chamar o método save do Model. O sync da Store é o mais usado e por isso vou mostrar esse workaround aqui.

Solução:

Basta criar um novo Writer com a funcionalidade de incluir os dados associados. Simples né?

Segue código:

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]Ext.define('AssociatedWriter', {
extend: 'Ext.data.writer.Json',
alias: 'writer.associatedjson',

constructor: function(config) {
this.callParent(arguments);
},

getRecordData: function (record, operation) {
record.data = this.callParent(arguments);
Ext.apply(record.data, record.getAssociatedData());
return record.data;
}
});[/code]

Você pode colocar esse código dentro da sua pasta app do seu projeto MVC sem problemas.

Exemplo de uso:

Considere que temos 2 Models: Contato e Telefone. Seguem os Models com Association:

Model Contato

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]Ext.define('Contato',{
extend: 'Ext.data.Model',

fields: [
{name: 'id', type: 'int'},
{name: 'nome', type: 'string'},
{name: 'sobrenome', type: 'string'}
],

hasMany: {model: 'Telefone', name: 'telefones', foreignKey: 'contatoId'}
});[/code]

Model Telefone

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]Ext.define('Telefone',{
extend: 'Ext.data.Model',

fields: [
{name: 'id', type: 'int'},
{name: 'ddd', type: 'string'},
{name: 'numero', type: 'string'},
{name: 'contatoId', type: 'int'}
]
});[/code]

Store Contatos

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false" highlight="20"]Ext.define('Contatos',{
extend: 'Ext.data.Store',
model: 'Contato',
proxy: {
type: 'ajax',

api: {
create: 'php/json/criaContato.php', //CRUD
read: 'php/json/listaContatos.php',
update: 'php/json/atualizaContato.php',
destroy: 'php/json/deletaContato.php',
},

reader: {
type: 'json',
root: 'contatos'
},

writer: {
type: 'associatedjson', //nosso Writer customizado
root: 'contatos',
writeAllFields: true,
encode: true,
allowSingle: false
}
}
});[/code]

Código de Teste

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]Ext.onReady(function(){

var store = Ext.create('Contatos');

var novoTelefone01 = Ext.create('Telefone',{
ddd: '11',
numero: '9 9999-9999'
});

var novoTelefone02 = Ext.create('Telefone',{
ddd: '11',
numero: '9 8888-8888'
});

var novoContato = Ext.create('Contato',{
nome: 'Loiane',
sobrenome: 'Groner'
});

novoContato.telefones().add(novoTelefone01);
novoContato.telefones().add(novoTelefone02);

store.add(novoContato);

store.sync();
});[/code]

E se a gente usar o nosso Writer customizado na Store, vamos enviar dados assim para o server:

extjs-associations-writer

E no server basta fazer o decode do JSON:

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]{
contatos:[
{
"id":0,
"nome":"Loiane",
"sobrenome":"Groner",
"telefones":[
{
"id":0,
"ddd":"11",
"numero":"9 9999-9999",
"contatoId":0
},
{
"id":0,
"ddd":"11",
"numero":"9 8888-8888",
"contatoId":0
}
]
}
]
}[/code]

Parece complicado, mas na verdade é bem simples! :)

Bem, é isso!

Pra quem quiser fazer download do Writer customizado e do arquivo de exemplo, segue repositório do github: https://github.com/loiane/extjs-associations-writer

Até a próxima! :)