We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 43712
    • 2 Posts
    Hello all.

    I've been working with MODx for about a month now and am really loving it. However, I'm have some legacy data that I need to import into my current MODx instance and I'm trying to create a custom resource class with a couple of related tables to hold the existing data as well as the new data that will be entered going forward. I've successfully created a couple of CRC classes (Chef and ChefContainer) and they are working (can create the custom resource in the manager). I've created (I believe) the correct xPDO schema, created the tables, and all seems to be correct. However, now I'm finding it very difficult to find out how to get to the next point of getting the data from the extended tables to show up in the TV values so that they can be edited, etc. I'm not even sure if this is the correct way to go.

    I've looked at several of the tutorials (http://rtfm.modx.com/revolution/2.x/developing-in-modx/advanced-development/extending-moduser, http://rtfm.modx.com/revolution/2.x/case-studies-and-tutorials/developing-an-extra-in-modx-revolution, many forum posts, and looking in the source for the Articles plugin), and I'm just plain stuck for how to get the data back and forth to the related tables as well as getting it into the editor. I feel like I'm missing something. The bits and pieces that I find don't seem to add up to a working whole.

    Is there somewhere that someone can point me that has a complete example of how this should be done? I've been banging my head on this all week and I'm starting to tear my hair out smiley I know that it's because I just don't have a good solid mental model and deep understanding of MODx yet, but I feel like I'm at a roadblock and I'm not sure where to go next.

    I'm running Revolution 2.2.8-pl

    I'd be happy for any suggestions that anyone might be able to give me that points me in the right direction.

    Thanks

    Here's my schema file:
    <?xml version="1.0" encoding="UTF-8"?>
    <model package="panna" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
        <object class="ChefContainer" extends="modResource">
            <composite alias="Chefs" class="Chef" local="id" foreign="parent" cardinality="many" owner="local" />
        </object>
        <object class="Chef" extends="modResource">
            <aggregate alias="Container" class="ChefContainer" local="parent" foreign="id" cardinality="one" owner="foreign" />
            <composite alias="data" class="ChefData" local="id" foreign="id" cardinality="one" owner="local" />
        </object>
        <object class="ChefData" table="panna_chef_data" extends="xPDOSimpleObject">
            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
            <field key="resource_id" dbtype="int" precision="11" phptype="integer" null="true" />
            <field key="bio" dbtype="text" phptype="string" null="true" />
            <field key="chefGUID" dbtype="varchar" precision="38" phptype="string" null="true" default="" />
            <field key="image_id" dbtype="int" precision="11" phptype="integer" null="true" />
            <field key="hd_image_id" dbtype="int" precision="11" phptype="integer" null="true" />
            <field key="name" dbtype="varchar" precision="255" phptype="string" null="true" default="" />
            <field key="homecity" dbtype="varchar" precision="255" phptype="string" null="true" default="" />
            <field key="chefTitle" dbtype="varchar" precision="255" phptype="string" null="true" default="" />
            <field key="videoURLStream" dbtype="varchar" precision="250" phptype="string" null="true" />
            <field key="specialty" dbtype="text" phptype="string" null="true" />
            <field key="signature_hd_image_id" dbtype="int" precision="11" phptype="integer" null="true" />
            <field key="signature_image_id" dbtype="int" precision="11" phptype="integer" null="true" />
            <field key="imagefolder" dbtype="varchar" precision="250" phptype="string" null="true" />
            <field key="restaurantName" dbtype="varchar" precision="250" phptype="string" null="true" />
            <field key="twitterHandle" dbtype="varchar" precision="200" phptype="string" null="true" />
            <field key="emailAddress" dbtype="varchar" precision="200" phptype="string" null="true" />
    
            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="id" collation="A" null="false" />
            </index>
    
            <composite alias="resource" class="Chef" local="id" foreign="id" cardinality="one" owner="foreign" />
            <composite alias="image" class="Image" local="image_id" foreign= "id" cardinality="one" owner="local" />
            <composite alias="hd_image" class="Image" local="hd_image_id" foreign= "id" cardinality="one" owner="local" />
            <composite alias="signature_image" class="Image" local="signature_image_id" foreign= "id" cardinality="one" owner="local" />
            <composite alias="hd_signature_image" class="Image" local="signature_hd_image_id" foreign= "id" cardinality="one" owner="local" />
        </object>
    
        <object class="Image" table="panna_image" extends="xPDOSimpleObject">
            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
            <field key="filename" dbtype="varchar" precision="255" phptype="string" null="true" default="" />
            <field key="localpath" dbtype="varchar" precision="255" phptype="string" null="true" default="" />
            <field key="uploadedName" dbtype="varchar" precision="255" phptype="string" null="true" default="" />
            <field key="cloudpath" dbtype="varchar" precision="255" phptype="string" null="true" />
            <field key="created_at" dbtype="int" precision="11" phptype="integer" null="false" />
            <field key="updated_at" dbtype="int" precision="11" phptype="integer" null="false" />
            <field key="CDNpath" dbtype="varchar" precision="255" phptype="string" null="true" />
            <field key="imagefolder" dbtype="varchar" precision="200" phptype="string" null="true" />
    
            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="id" collation="A" null="false" />
            </index>
    
            <aggregate alias="id" class="ChefData" local="id" foreign= "image_id" cardinality="one" owner="foreign" />
            <aggregate alias="id" class="ChefData" local="id" foreign= "hd_image_id" cardinality="one" owner="foreign" />
            <aggregate alias="id" class="ChefData" local="id" foreign= "signature_image_id" cardinality="one" owner="foreign" />
            <aggregate alias="id" class="ChefData" local="id" foreign= "signature_hd_image_id" cardinality="one" owner="foreign" />
    
        </object>
    </model>
    
    
    
    


      • 41592 ☆ A M B ☆
      • 83 Posts
      Hello!
      Also I have created the CRC, maybe I can help.
      what you want is to manage the extra data in the panel management of the resource in question, right?
      (see the picture)

      I did something similar for my CRC, first (like you) I create the classes:
      - Room (extends modResource and composite RoomPrice)
      - Room Price (extends xPDOSimpleObject and aggregate Room)

      Then I created the controllers for the creation and updating of the class "Room":
      class RoomCreateManagerController extends ResourceCreateManagerController
      class RoomUpdateManagerController extends ResourceUpdateManagerController


      Inside you can override the function:
      public loadCustomCssJs (){}

      That allow you to manage how to display resource fields (and thus to add additional fields) during your first creation(create.class.php) and all the future update(update.class.php) of your CRC.
      Check controllers/default/resource/create.class.php to see how to make your loadCustomCssJs() function
      Substantially in the function you have to add sources to your additional ExtPanel (in my case is a extJS grid) and overwrite default one (modx.panel.resource.js) adding the extra tab/fields.

      Once you've make your controllers you need to make your JS Resource modExt Panel (createCRC.js)
      Even in this case you can refer to:
      /manager/assets/modext/widgets/resource/modx.panel.resource.js


      Once you have created your custom panel for the CRC and properly connected to the controller no longer have limits smiley

      In my case I replicated the structure of creating a resource "modResource" but adding a new panel for manage the price table for my Room instance. (see picture)

      Hope can help,

      Francesco [ed. note: solidusite last edited this post 12 years, 11 months ago.]
        Francesco Mussoni | MODX Ambassador | Skype: solidusite2 | Email: [email protected]
        • 9207 ☆ A M B ☆
        • 2,475 Posts
        Could you include your custom Javascript? Part of the difficulty here is understanding how Ext JS draws panels, grids, tabs, and form fields. Thanks!
          • 41592 ☆ A M B ☆
          • 83 Posts
          I know I know, tomorrow I'm not in office, but Tuesday I will do it.
            Francesco Mussoni | MODX Ambassador | Skype: solidusite2 | Email: [email protected]
            • 41592 ☆ A M B ☆
            • 83 Posts
            Ok as promise. Let's take a look to my example:
            According to the example of the Room (class which extends modResource) and the RoomPrice (class that extends xpdoSimpleObject) I created my controller.
            For convenience I created the additional panel (ExtJs) in an external file which then inside the controller I linked.
            The tab "price list" is a simple table ExtJs:
            Swingtime.grid.Prezzi = function(config) {
                config = config || {};
                Ext.applyIf(config,{
                    id: 'Swingtime-grid-Prezzi'
                    ,url: Swingtime.connector_url
                    ,baseParams: { action: 'mgr/prezzi/getList', roomID: Swingtime.roomID, classKey: Swingtime.classKey}
                    ,fields: [
                        ,{ name: 'id', type: 'int' }
                        ,{ name: 'roomPrice_id', type: 'int' }
                        ,{ name: 'price', type: 'string' }
                        ,{ name: 'priceType_id', type: 'int' }
                        ,{ name: 'priceDeal_id', type: 'int' }
                        ,{ name: 'priceTarget_id', type: 'int' }
                        ,{ name: 'date_start', type: 'date' }
                        ,{ name: 'date_end', type: 'date' }
                        ,{ name: 'priceInfo', type: 'string' }
                     ] 
                    ,paging: true
                    ,remoteSort: true	
            		,lazyRender: true
                    ,anchor: '97%'
                    ,autoExpandColumn: 'price'
            		,save_action: 'mgr/prezzi/updatefromgrid'
            		,autosave: true
            		,getMenu: function() {
            			return [{
                            text: 'Update'
                            ,handler: this.updatePrezzi
                        },{
            				text: 'Remove'
            				,handler: this.removePrezzi
            			}];
            		}
                    ,updatePrezzi: function(btn,e) {
                        if (!this.updatePrezziWindow) {
                            this.updatePrezziWindow = MODx.load({
                                xtype: 'Swingtime-window-Update-Prezzo'
                                ,record: this.menu.record
                                ,listeners: {
                                    'success': {fn:this.refresh,scope:this}
                                }
                            });
                        }
                        this.updatePrezziWindow.setValues(this.menu.record);
            
                        this.updatePrezziWindow.show(e.target);
                    }
            
                    ,removePrezzi: function() {
            			MODx.msg.confirm({
            				title: 'Remove'
            				,text: 'Are you Sure?'
            				,url: this.config.url
            				,params: {
            					action: 'mgr/prezzi/remove'
            					,id: this.menu.record.id
            				}
            				,listeners: {
            					'success': {fn:this.refresh,scope:this}
            				}
            			});
            		}
                    ,columns: [{
                        header: 'Price'
                        ,dataIndex: 'price'
                        ,sortable: true
                        ,width: 100
                        ,editor: { xtype: 'numberfield' }
                    },{
                        header: 'Price Type'
                        ,dataIndex: 'priceType_id'
                        ,sortable: true
                        ,width: 150
                        ,editor:{ xtype: 'Swingtime-combo-priceType', renderer: true}
            
                    },{
                        header: 'Price Deal'
                        ,dataIndex: 'priceDeal_id'
                        ,sortable: true
                        ,width: 150
                        ,editor:{ xtype: 'Swingtime-combo-priceDeal', renderer: true}
            
                    },{
                        header: 'Price Target'
                        ,dataIndex: 'priceTarget_id'
                        ,sortable: true
                        ,width: 150
                        ,editor:{ xtype: 'Swingtime-combo-priceTarget', renderer: true}
            
                    },{
                        header: 'Date Start'
                        ,dataIndex: 'date_start'
                        ,sortable: true
                        ,width: 150
                        ,editor: {xtype: 'datefield' }
                    },{
                        header: 'Date End'
                        ,dataIndex: 'date_end'
                        ,sortable: true
                        ,width: 150
                        ,editor: { xtype: 'datefield' }
                    }]
               ,tbar:[{
               text: 'Add new price'
               ,handler: { xtype: 'Swingtime-window-Create-Prezzo' ,blankValues: true }
               },{
                xtype: 'textfield'
                ,id: 'Swingtime-search-filter'
                ,emptyText: 'Search...'
                ,listeners: {
                    'change': {fn:this.search,scope:this}
                    ,'render': {fn: function(cmp) {
                        new Ext.KeyMap(cmp.getEl(), {
                            key: Ext.EventObject.ENTER
                            ,fn: function() {
                                this.fireEvent('change',this);
                                this.blur();
                                return true;
                            }
                            ,scope: cmp
                        });
                    },scope:this}
                }
            }]
                });
                Swingtime.grid.Prezzi.superclass.constructor.call(this,config)
            };
            Ext.extend(Swingtime.grid.Prezzi,MODx.grid.Grid);
            Ext.reg('Swingtime-grid-Prezzi',Swingtime.grid.Prezzi);
            


            I've omitted to report other functions in my ExtJs, by the way, there're two function as Window ExtJs that allow me to Create or Edit a RoomPrice.

            The second step is to connect to my ExtGrid panel creation / update the resource Room, which is nothing more than a modResource. So within my controller, I added a replica of the ExtJs panel of the resource, edited according to my needs (in this case by adding the Tab with the others, Settings, Resource Group, Template Vars, etc.).

            So in my controllers i've something like:

            $this->addLastJavascript($swingtimeJsUrl.'room/update.js');
            $this->addLastJavascript($swingtimeJsUrl.'room/prices.grid.js');
            


            update.js is exactly the same Js of resource.update.js.
            prices.grid.js is my custom grid to add.

            Ok now let's see how link grid with the Resource panel:
            Inside the ExtJs Resource Panel (my update.js) there's a function called:
             ,getFields: function(config) {}

            Inside of it you can work on the structure of your resource ExtPanel. What I've done? I've add a tab:

             
                ,getFields: function(config) {
                    var it = [];
                    if (config.show_tvs && MODx.config.tvs_below_content != 1) {
                        it.push({
                                title: 'Room'
                                ,items: this.getTemplateVariablesPanel(config)
                            }
                        );
                    }
                    it.push({
                        title: 'SEO'
                        ,id: 'modx-resource-settings'
                        ,cls: 'modx-resource-tab'
                        ,layout: 'form'
                        ,labelAlign: 'top'
                        ,labelSeparator: ''
                        ,bodyCssClass: 'tab-panel-wrapper main-wrapper'
                        ,autoHeight: true
                        ,defaults: {
                            border: false
                            ,msgTarget: 'under'
                            ,width: 400
                        }
                        ,items: this.getMainFields(config)
                    });
            	it.push({
                        title: 'Price Table'
                        ,id: 'modx-resource-priceTable'
                        ,cls: 'modx-resource-tab'
                        ,layout: 'form'
                        ,labelAlign: 'top'
                        ,labelSeparator: ''
                        ,bodyCssClass: 'tab-panel-wrapper main-wrapper'
                        ,autoHeight: true
                        ,defaults: {
                            border: false
                            ,msgTarget: 'under'
                            ,width: '98%'
                        }
                        ,items: [{
                            title: _('swingtime')
                            ,defaults: { autoHeight: true }
                            ,items: [{
                                html: '<p>Here you can manage the relative prices of the selected room</p>'
                                ,border: false
                                ,bodyCssClass: 'panel-desc'
                            },{
                                xtype: 'Swingtime-grid-Prezzi'
                                ,cls: 'main-wrapper'
            					,anchor:'98%'
            					,width:'98%'
                                ,preventRender: true
                            }]
            			}]
                    });
                    it.push({
                        id: 'modx-page-settings'
                        ,title: _('settings')
                        ,cls: 'modx-resource-tab'
                        ,layout: 'form'
                        ,forceLayout: true
                        ,deferredRender: false
                        ,labelWidth: 200
                        ,bodyCssClass: 'main-wrapper'
                        ,autoHeight: true
                        ,defaults: {
                            border: false
                            ,msgTarget: 'under'
                        }
                        ,items: this.getSettingFields(config)
                    });
            
                    if (MODx.perm.resourcegroup_resource_list == 1) {
                        it.push(this.getAccessPermissionsTab(config));
                    }
                    var its = [];
                    its.push(this.getPageHeader(config),{
                        id:'modx-resource-tabs'
                        ,xtype: 'modx-tabs'
                        ,forceLayout: true
                        ,deferredRender: false
                        ,collapsible: true
                        ,animCollapse: false
                        ,itemId: 'tabs'
                        ,items: it
                    });
                    var ct = this.getContentField(config);
                    /*if (ct) {
                        its.push({
                            title: _('resource_content')
                            ,id: 'modx-resource-content'
                            ,layout: 'form'
                            ,bodyCssClass: 'main-wrapper'
                            ,autoHeight: true
                            ,collapsible: true
                            ,animCollapse: false
                            ,hideMode: 'offsets'
                            ,items: ct
                            ,style: 'margin-top: 10px'
                        });
                    }*/
                    if (MODx.config.tvs_below_content == 1) {
                        var tvs = this.getTemplateVariablesPanel(config);
                        its.push(tvs);
                    }
                    return its;
                }
            


            As you can see here I changed more things as well as add my Tab.
            First of one I've change the order: I moved TV Tab at the beginning of rendering, this way is the first tab that is displayed.
             if (config.show_tvs && MODx.config.tvs_below_content != 1) {
                        it.push({
                                title: 'Room'
                                ,items: this.getTemplateVariablesPanel(config)
                            }
                        );
                    }
            


            The second things i've done was to change some settings of Resource Tab, like the title, in my case SEO.
            it.push({
                        title: 'SEO'
                        ,id: 'modx-resource-settings'
                        ,cls: 'modx-resource-tab'
                        ,layout: 'form'
                        ,labelAlign: 'top'
                        ,labelSeparator: ''
                        ,bodyCssClass: 'tab-panel-wrapper main-wrapper'
                        ,autoHeight: true
                        ,defaults: {
                            border: false
                            ,msgTarget: 'under'
                            ,width: 400
                        }
                        ,items: this.getMainFields(config)
                    });
            


            And now comes the fun part: I added my ExtGrid.

            it.push({
                        title: 'Price Table'
                        ,id: 'modx-resource-priceTable'
                        ,cls: 'modx-resource-tab'
                        ,layout: 'form'
                        ,labelAlign: 'top'
                        ,labelSeparator: ''
                        ,bodyCssClass: 'tab-panel-wrapper main-wrapper'
                        ,autoHeight: true
                        ,defaults: {
                            border: false
                            ,msgTarget: 'under'
                            ,width: '98%'
                        }
                        ,items: [{
                            title: _('swingtime')
                            ,defaults: { autoHeight: true }
                            ,items: [{
                                html: '<p>Here you can manage the relative prices of the selected room</p>'
                                ,border: false
                                ,bodyCssClass: 'panel-desc'
                            },{
                                xtype: 'Swingtime-grid-Prezzi'
                                ,cls: 'main-wrapper'
            					,anchor:'98%'
            					,width:'98%'
                                ,preventRender: true
                            }]
            			}]
                    });
            


            Many lines about customizing the appearance.
            What interests us here is that i've added a new tab and inside of it I've put my "Items", that is ExtGrid Price Table. As you can see in the line where you choose your Xtype I've selected exactly the grid that I've posted before.

             xtype: 'Swingtime-grid-Prezzi'


            that's all
            Hope can help! let me know smiley
              Francesco Mussoni | MODX Ambassador | Skype: solidusite2 | Email: [email protected]