title: Why does TreeStore.sync() not write to database?
body: <p>I've been assigned a task to extent and modify a Shopware plugin. The original author isn't in the company anymore. Before that I've never dealt with Shopware and ExtJs.
  So I spend the last couple of days getting myself into it and I think I understood the principles and paradigm so far.</p>
  
  <p>The only thing I'm having trouble with right now is the following issue:</p>
  
  <p>I've got an <code>Ext.tree.Panel</code> which I want to save into a database using Ajax. The node is being added to the tree, I can see it appearing in the GUI. But after calling <code>optionsTree.getStore().sync()</code> there is nothing arriving in the database. The <code>createProductOptionAction()</code> in the PHP controller isn't called, but I can't figure out why. There is no error message in Browser console log, no error message in the Shopware log files. Nothing. Everything seems to work fine. But the data isn't being stored.</p>
  
  <p>The original plugin had an <code>Ext.grid.Panel</code> to store and display data. And this works fine. But after changing to <code>Ext.tree.Panel</code> and modifying the code, it doesn't work anymore. From my point of view it should work tho. But it doesn't and I can't see my mistake(s).</p>
  
  <p>Any help is really appreciated, I'm still a bloody beginner with ExtJs. :)</p>
  
  <p>Here is what I've got so far:</p>
  
  <p><strong>app.js</strong></p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager', {
  
      extend:'Enlight.app.SubApplication',
      name:'Shopware.apps.CCBConfigurablePhotoProductsManager',
  
      bulkLoad: true,
      loadPath:'{url controller="CCBConfigurablePhotoProductsManager" action="load"}',
  
      controllers:['ProductConfigurator'],
      stores:['ProductOptionsList'],
      models:['ProductOption'],
      views: ['ProductOptions', 'Window' ],
  
      launch: function() {
          var me = this,
              mainController = me.getController('ProductConfigurator');
          return mainController.mainWindow;
      }
  });
  </code></pre>
  
  <p><strong>controller/controller.js</strong></p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.controller.ProductConfigurator', {
  
      extend:'Ext.app.Controller',
  
      refs: [
          { ref: 'productOptionsTree', selector: 'product-configurator-settings-window product-options-tree' }
      ],
  
      init:function () {
          var me = this;
  
          me.mainWindow = me.createMainWindow();
          me.addControls();
          me.callParent(arguments);
  
          return me.mainWindow;
      },
  
      addControls: function() {
          var me = this;
  
          me.control({
              'product-configurator-settings-window product-options-tree': {
                  addProductOption: me.onAddProductOption
              }
          });
      },
  
      createMainWindow: function() {
          var me = this,
              window = me.getView('Window').create({
                  treeStore: Ext.create('Shopware.apps.CCBConfigurablePhotoProductsManager.store.ProductOptionsList').load()
              }).show();
  
          return window;
      },
  
      onAddProductOption: function() {
          var me = this,
              optionsTree = me.getProductOptionsTree(),
              parentNode = optionsTree.getRootNode(),
              nodeCount = parentNode.childNodes.length + 1,
              productOption = Ext.create('Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption', {
                  parent: 0,
                  type: 0,
                  title: Ext.String.format('{s name="group/default_name"}New Group [0]{/s}', optionsTree.getRootNode().childNodes.length + 1),
                  active: true,
                  leaf: false
              });
          productOption.setDirty();
          parentNode.appendChild(productOption);
          optionsTree.getStore().sync(); // Nothing arrives at DB
          optionsTree.expandAll();
      },
  
      // ...
  </code></pre>
  
  <p><strong>view/window.js</strong></p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.view.Window', {
  
      extend:'Enlight.app.Window',
  
      cls:Ext.baseCSSPrefix + 'product-configurator-settings-window',
      alias:'widget.product-configurator-settings-window',
  
      border:false,
      autoShow:true,
      maximizable:true,
      minimizable:true,
  
      layout: {
          type: 'hbox',
          align: 'stretch'
      },
  
      width: 700,
      height: 400,
  
      initComponent:function () {
          var me = this;
          me.createItems();
          me.title = '{s name=window/title}Configurator Settings{/s}';
          me.callParent(arguments);
      },
  
      createItems: function() {
          var me = this;
  
          me.items = [
              me.createProductOptionsTree()
          ];
      },
  
      createProductOptionsTree: function() {
          var me = this;
          return Ext.create('Shopware.apps.CCBConfigurablePhotoProductsManager.view.ProductOptions', {
              store: me.treeStore,
              width: '20%',
              flex: 1
          });
      }
  });
  </code></pre>
  
  <p><strong>store/product_options_list.js</strong></p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.store.ProductOptionsList', {
      extend: 'Ext.data.TreeStore',
      pageSize: 30,
      autoLoad: false,
      remoteSort: true,
      remoteFilter: true,
      model : 'Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption',
      proxy:{
          type:'ajax',
          url:'{url controller="CCBConfigurablePhotoProductsManager" action="getProductOptionsList"}',
          reader:{
              type:'json',
              root:'data',
              totalProperty:'total'
          }
      }
  });
  </code></pre>
  
  <p><strong>model/product_option.js</strong></p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption', {
      extend : 'Ext.data.Model',
      fields : [
          { name : 'id', type : 'int', useNull: true },
          { name : 'parent', type : 'int' },
          { name : 'title', type : 'string' },
          { name : 'active', type: 'boolean' },
          { name : 'type', type : 'int' }
      ],
      idProperty : 'id',
      proxy : {
          type : 'ajax',
          api: {
              create: '{url controller="CCBConfigurablePhotoProductsManager" action="createProductOption"}',
              update: '{url controller="CCBConfigurablePhotoProductsManager" action="updateProductOption"}',
              destroy: '{url controller="CCBConfigurablePhotoProductsManager" action="deleteProductOption"}'
          },
          reader : {
              type : 'json',
              root : 'data',
              totalProperty: 'total'
          }
      }
  });
  </code></pre>
  
  <p><strong>php/controller.php</strong></p>
  
  <pre><code>&lt;?php
  use Shopware\CustomModels\CCB\ProductOption;
  
  class Shopware_Controllers_Backend_CCBConfigurablePhotoProductsManager extends Shopware_Controllers_Backend_ExtJs
  {
  
      public function createProductOptionAction()
      {
          // Never being called
          file_put_contents('~/test.log', "createProductOptionAction\n", FILE_APPEND);
          $this-&gt;View()-&gt;assign(
              $this-&gt;saveProductOption($this-&gt;Request()-&gt;getParams())
          );
      }
  
      public function getProductOptionsListAction()
      {
          // Works fine
          file_put_contents('~/test.log', "getProductOptionsListAction\n", FILE_APPEND);
          // ...
      }
  
      // ...
  </code></pre>
  
  <p><strong>EDIT 1</strong></p>
  
  <p>I tried adding a writer for both, the store and the model, as suggested by Saki. But unfortunately it still doesn't work. The <code>createProductOptionAction()</code> in the PHP controller is never being called.</p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption', {
      extend : 'Ext.data.Model',
      fields : [
          { name : 'id', type : 'int', useNull: true },
          { name : 'parent', type : 'int' },
          { name : 'title', type : 'string' },
          { name : 'active', type: 'boolean' },
          { name : 'type', type : 'int' }
      ],
      idProperty : 'id',
      proxy : {
          type: 'ajax',
          api: {
              create: '{url controller="CCBConfigurablePhotoProductsManager" action="createProductOption"}',
              update: '{url controller="CCBConfigurablePhotoProductsManager" action="updateProductOption"}',
              destroy: '{url controller="CCBConfigurablePhotoProductsManager" action="deleteProductOption"}'
          },
          reader: {
              type : 'json',
              root : 'data',
              totalProperty: 'total'
          },
          writer: {
              type: 'json'
          }
      }
  });
  </code></pre>
  
  <p>What I'm wondering tho, the original plugin had no writer implemented. But when adding an entry it immediately appeared in the database.</p>
  
  <p><strong>EDIT 2</strong></p>
  
  <p>I added several listeners to the <code>store.ProductOptionsList</code>:</p>
  
  <pre><code>Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.store.ProductOptionsList', {
      extend: 'Ext.data.TreeStore',
      pageSize: 30,
      autoLoad: false,
      remoteSort: true,
      remoteFilter: true,
      model : 'Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption',
      root: {
          text: 'Product Options',
          id: 'productOptions',
          expanded: true
      },
      proxy:{
          type: 'ajax',
          url: '{url controller="CCBConfigurablePhotoProductsManager" action="getProductOptionsList"}',
          reader: {
              type:'json',
              root:'data',
              totalProperty:'total'
          }
      },
      listeners: {
          add: function(store, records, index, eOpts) {
              console.log("**** Add fired");
              console.log(records);
          },
          append: function(store, node, index, eOpts) {
              console.log("**** Append fired");
              console.log(node);
          },
          beforesync: function(operations) {
              console.log("**** Beforesync fired");
              console.log(operations);
          }
      }
  });
  </code></pre>
  
  <p>All these Events are getting fired. The <code>beforesync</code> event shows</p>
  
  <pre><code>**** Beforesync fired
  Object {create: Array[1]}
    create: Array[1]
    ...
  </code></pre>
  
  <p>But still, the API requests of the <code>model.ProductOption</code> are not getting fired. It should work. Shouldn't it? Maybe this is a bug in ExtJS 4.1? Or something with Shopware + ExtJS?</p>
  
  <p><strong>EDIT 3</strong></p>
  
  <p>Ok, this is really getting weird.</p>
  
  <p>I added a "write"-Listener to the <code>TreeStore</code>.</p>
  
  <pre><code>    write: function(store, operation, opts){
          console.log("**** Write fired");
          console.log(operation);
          Ext.each(operation.records, function(record){
              console.log("**** ...");
              if (record.dirty) {
                  console.log("**** Commiting dirty record");
                  record.commit();
              }
          });
      }
  </code></pre>
  
  <p>After adding a Node and calling <code>.getStore().sync()</code>, the <code>write</code>-event IS fired, he iterates <code>operation.records</code>, finds one record (the one I just added)... but it isn't <code>dirty</code>, even though I do <code>productOption.setDirty()</code> before adding it to the Tree?!</p>
  
  <p>Thanks alot for your time! :)</p>