We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 52563
    • 12 Posts
    I'm fairly new to Modx. My employer recently asked me to look into what it would take to migrate one of our systems from TYPO3 to Modx. Personally, most of my development has centered around Laravel and Symfony.

    I have to say, although I can appreciate the modularity of the Modx platform, it is can be overly complicated to perform even simple development. Relationships are one of the most basic aspects of data handling, I'm surprised there is so much work involved in representing a many-to-many relationship in a custom manager page. I had created a whole CMP using EXTjs only to find out that I had to use Migx to accomplish what I wanted, it seems to not be supported by modExt. The whole concept of TV's is confusing to me, especially when it comes to Migx as although they use the same name, they seem to be two different beasts.

    What we have is a hierarchy of reseller websites that inherit data and content from the master, but each reseller has the option to disable or override any of that content or data.

    I have started by creating a custom table to store resellers and extended modUser to relate to the table. There will also be relations between resellers and resources, but that's already taken care of with a standard TV.

    I created a Migx config for the resellers table. It took me a while, but I ended up using the migxLoopCollection snippet to populate the multi-select dropdown and a hook snippet to create the pivot records manually. xPDO is also very confusing to me. I'm missing Eloquent more and more.

    I use another snippet in the default values field to populate the currently selected users for existing records. (this doesn't seem right)

    I cannot for the life of me, get the user names to show up in the column in the grid, though. I could surely write my own snippet and use this.renderChunk to show the related records, but there are people all over the forum saying that they can at least get the selected ID's to show up, I cannot for the life of me get this column to populate itself, though. Is there something I'm missing? I'd just be happy with a list of comma-seperated usernames.

    Here is my schema

    <?xml version="1.0" encoding="UTF-8"?>
    <model package="resellers" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.0">
    
        <!-- Extended modUser -->
        <object class="extUser" extends="modUser">
            <composite alias="ResellerUsers" local="id" class="ResellerUser" foreign="user_id" cardinality="many" owner="local" />
        </object>
        <!-- Resellers table -->
        <object class="Reseller" table="resellers" extends="xPDOSimpleObject">
            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="id" collation="A" null="false" />
            </index>
    
            <!-- general data -->
            <field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default=""/>
            <field key="comments" dbtype="text" phptype="string" null="false" default=""/>
    
            <!-- activity fields -->
            <field key="createdon" dbtype="datetime" phptype="datetime" null="true"/>
            <field key="createdby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
            <field key="editedon" dbtype="datetime" phptype="datetime" null="true"/>
            <field key="editedby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
    
            <field key="deleted" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
            <field key="published" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
    
            <!-- relations -->
            <composite alias="ResellerUsers" class="ResellerUser" local="id" foreign="user_id" cardinality="many" owner="local"/>
            <aggregate alias="CreatedBy" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign"/>
            <aggregate alias="EditedBy" class="modUser" local="editedby" foreign="id" cardinality="one" owner="foreign"/>
        </object>
        <!-- Reseller / User Pivot -->
        <object class="ResellerUser" table="reseller_user" extends="xPDOObject">
            <!-- id fields -->
            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="id" collation="A" null="false" />
            </index>
            <field key="reseller_id" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
            <field key="user_id" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
            <!-- relations -->
            <aggregate alias="Reseller" class="Reseller" local="reseller_id" foreign="id" cardinality="one" owner="foreign"/>
            <aggregate alias="User" class="extUser" local="user_id" foreign="id" cardinality="one" owner="foreign"/>
        </object>
    
        <!-- Reseller / Resource Pivot -->
        <object class="ResellerResource" table="reseller_resource" extends="xPDOObject">
            <!-- id fields -->
            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="id" collation="A" null="false" />
            </index>
            <field key="reseller_id" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
            <field key="resource_id" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
            <!-- relations -->
            <aggregate alias="Reseller" class="Reseller" local="reseller_id" foreign="id" cardinality="one" owner="foreign"/>
            <aggregate alias="Resource" class="modResource" local="resource_id" foreign="id" cardinality="one" owner="foreign"/>
        </object>
    </model>


    And here is my Migx config

    {
      "formtabs":[
        {
          "MIGX_id":1,
          "caption":"Resellers",
          "print_before_tabs":"0",
          "fields":[
            {
              "MIGX_id":1,
              "field":"name",
              "caption":"Name",
              "description":"Name of the reseller",
              "description_is_code":"0",
              "inputTV":"name",
              "inputTVtype":"string",
              "validation":"",
              "configs":"",
              "restrictive_condition":"",
              "display":"",
              "sourceFrom":"config",
              "sources":"",
              "inputOptionValues":"",
              "default":"",
              "useDefaultIfEmpty":"0",
              "pos":1
            },
            {
              "MIGX_id":6,
              "field":"comments",
              "caption":"",
              "description":"",
              "description_is_code":"0",
              "inputTV":"comments",
              "inputTVtype":"textarea",
              "validation":"",
              "configs":"",
              "restrictive_condition":"",
              "display":"",
              "sourceFrom":"config",
              "sources":"",
              "inputOptionValues":"",
              "default":"",
              "useDefaultIfEmpty":"0",
              "pos":2
            },
            {
              "MIGX_id":8,
              "field":"id",
              "caption":"",
              "description":"",
              "description_is_code":"0",
              "inputTV":"id",
              "inputTVtype":"hidden",
              "validation":"",
              "configs":"",
              "restrictive_condition":"",
              "display":"",
              "sourceFrom":"config",
              "sources":"",
              "inputOptionValues":"",
              "default":"",
              "useDefaultIfEmpty":"0",
              "pos":3
            }
          ],
          "pos":1
        },
        {
          "MIGX_id":4,
          "caption":"Users",
          "print_before_tabs":"0",
          "fields":[
            {
              "MIGX_id":10,
              "field":"users",
              "caption":"Users",
              "description":"",
              "description_is_code":"0",
              "inputTV":"users",
              "inputTVtype":"listbox-multiple",
              "validation":"",
              "configs":"",
              "restrictive_condition":"",
              "display":"",
              "sourceFrom":"config",
              "sources":"",
              "inputOptionValues":"@EVAL return $modx->runSnippet('migxLoopCollection',array('packageName'=>'resellers','classname'=>'extUser','tpl'=>'@CODE:[[+username]]==[[+id]]','outputSeparator'=>'||'));",
              "default":"@EVAL return $modx->runSnippet(\"get_selected_users\");",
              "useDefaultIfEmpty":1,
              "pos":1
            }
          ],
          "pos":2
        }
      ],
      "contextmenus":"update||recall_remove_delete||remove",
      "actionbuttons":"addItem||bulk||toggletrash",
      "columnbuttons":"",
      "filters":[
        {
          "MIGX_id":1,
          "name":"search",
          "label":"search",
          "emptytext":"",
          "type":"textbox",
          "getlistwhere":{
            "name:LIKE":"%[[+search]]%",
            "OR:description:LIKE":"%[[+search]]%"
          },
          "getcomboprocessor":"",
          "combotextfield":"",
          "comboidfield":"",
          "combowhere":"",
          "comboclassname":"",
          "combopackagename":"",
          "combo_use_custom_prefix":"0",
          "comboprefix":"",
          "combojoins":"",
          "comboparent":"",
          "default":""
        }
      ],
      "extended":{
        "migx_add":"Add Reseller",
        "disable_add_item":"",
        "add_items_directly":"",
        "formcaption":"",
        "update_win_title":"",
        "win_id":"",
        "maxRecords":"",
        "addNewItemAt":"bottom",
        "multiple_formtabs":"",
        "multiple_formtabs_label":"",
        "multiple_formtabs_field":"",
        "multiple_formtabs_optionstext":"",
        "multiple_formtabs_optionsvalue":"",
        "actionbuttonsperrow":4,
        "winbuttonslist":"",
        "extrahandlers":"",
        "filtersperrow":4,
        "packageName":"resellers",
        "classname":"Reseller",
        "task":"",
        "getlistsort":"",
        "getlistsortdir":"",
        "sortconfig":"",
        "gridpagesize":"",
        "use_custom_prefix":"0",
        "prefix":"",
        "grid":"",
        "gridload_mode":1,
        "check_resid":1,
        "check_resid_TV":"",
        "join_alias":"",
        "has_jointable":"yes",
        "getlistwhere":"",
        "joins":[
          {
            "alias":"ResellerUsers"
          }
        ],
        "hooksnippets":{
          "aftersave":"save_reseller_users"
        },
        "cmpmaincaption":"Resellers",
        "cmptabcaption":"All Resellers",
        "cmptabdescription":"",
        "cmptabcontroller":"",
        "winbuttons":"",
        "onsubmitsuccess":"",
        "submitparams":""
      },
      "columns":[
        {
          "MIGX_id":1,
          "header":"ID",
          "dataIndex":"id",
          "width":"",
          "sortable":true,
          "show_in_grid":1,
          "customrenderer":"",
          "renderer":"",
          "clickaction":"",
          "selectorconfig":"",
          "renderchunktpl":"",
          "renderoptions":"",
          "editor":""
        },
        {
          "MIGX_id":2,
          "header":"Name",
          "dataIndex":"name",
          "width":"",
          "sortable":"false",
          "show_in_grid":1,
          "customrenderer":"",
          "renderer":"",
          "clickaction":"",
          "selectorconfig":"",
          "renderchunktpl":"",
          "renderoptions":"",
          "editor":""
        },
        {
          "MIGX_id":3,
          "header":"Comments",
          "dataIndex":"comments",
          "width":"",
          "sortable":"false",
          "show_in_grid":1,
          "customrenderer":"",
          "renderer":"",
          "clickaction":"",
          "selectorconfig":"",
          "renderchunktpl":"",
          "renderoptions":"",
          "editor":""
        },
        {
          "MIGX_id":4,
          "header":"Users",
          "dataIndex":"users",
          "width":"",
          "sortable":"false",
          "show_in_grid":1,
          "customrenderer":"",
          "renderer":"",
          "clickaction":"",
          "selectorconfig":"",
          "renderchunktpl":"",
          "renderoptions":"",
          "editor":""
        }
      ]
    }


    What am I missing?

    Edit: note, I have created a join for the relation, however using ResellerUsers_user_id for the field name of the column results in only one user_id...

    This question has been answered by Bruno17. See the first response.

    [ed. note: dmeganoski last edited this post 10 years, 2 months ago.]
      • 4172
      • 5,888 Posts
      will come back tomorrow with some help.
        -------------------------------

        you can buy me a beer, if you like MIGX

        http://webcmsolutions.de/migx.html

        Thanks!
        • 52563
        • 12 Posts
        Quote from: Bruno17 at Jul 28, 2016, 03:31 PM
        will come back tomorrow with some help.

        Awesome. It would be much appreciated. I am off for the day soon as well. Look forward to hearing what I screwed up tomorrow. ^^
          • 36416
          • 589 Posts
          Quote from: dmeganoski at Jul 28, 2016, 02:33 PM
          xPDO is also very confusing to me. I'm missing Eloquent more and more.

          You won't be happy with xPDO after using that other month-named CMS... good luck.
            • 4172
            • 5,888 Posts
            Thanks for a very helpful post @Eoler!
              -------------------------------

              you can buy me a beer, if you like MIGX

              http://webcmsolutions.de/migx.html

              Thanks!
              • 4172
              • 5,888 Posts
              using joins in the grid works only for one2one - Relations.
              If you need to fetch one2many - Relations into one field, you can do it with a aftergetfields - hook-snippet.

              https://github.com/Bruno17/MIGX/blob/master/core/components/migx/processors/mgr/default/fields.php

              you can add values to the 'record_fields' of the object.

              For showing them in a grid-column, you can use the renderchunk-renderer. [ed. note: Bruno17 last edited this post 10 years, 2 months ago.]
                -------------------------------

                you can buy me a beer, if you like MIGX

                http://webcmsolutions.de/migx.html

                Thanks!
                • 52563
                • 12 Posts
                Quote from: danilocuculic at Jul 29, 2016, 12:46 AM
                You won't be happy with xPDO after using that other month-named CMS... good luck.

                Well, I have been working with Laravel itself for a while now. I only recently ventured into October CMS, but there are many drawbacks to it. For example, the lack of multi-tenant support and the lack of separation of logic from design.

                Quote from: Bruno17 at Jul 29, 2016, 07:50 AM
                using joins in the grid works only for one2one - Relations.
                If you need to fetch one2many - Relations into one field, you can do it with a aftergetfields - hook-snippet.

                https://github.com/Bruno17/MIGX/blob/master/core/components/migx/processors/mgr/default/fields.php

                you can add values to the 'record_fields' of the object.

                For showing them in a grid-column, you can use the renderchunk-renderer.

                Thanks Bruno. That will make this work.

                I have to say I'm a little surprised that there isn't a configuration that would support this. In reflection, once you get used to the flow of modx it probably takes seconds to pump out a snippet like this, while trying to support more complicated relations in the CMP builder would probably be more of a headache than it's worth.

                While we're on the topic... I have seen that it is possible to nest one grid inside of another, but I have not found anywhere that HOW to do that. Let's just say that the relation is not users, but another custom object I created that has a many to many relation to the resellers... let's call them products.

                How would I go about nesting a grid of products inside its own form tab? Any reference materials would be much appreciated.
                  • 4172
                  • 5,888 Posts
                  it would be easier to tell, if you would give me your xpdo-schema, which you have with the many to many relations between resellers and products.
                    -------------------------------

                    you can buy me a beer, if you like MIGX

                    http://webcmsolutions.de/migx.html

                    Thanks!
                    • 52563
                    • 12 Posts
                    Well, I haven't actually created a products table (yet). This is just theoretical.

                    I think this is the proper way to define the relationship. I may have the wrong idea here. It doesn't make much sense to me that there is no way to define a direct relation between resellers and products. For example, with Eloquent you would define a relation between Product and Reseller and then (optionally) define the pivot table. With xpdo, all I see regarding many to many relations only references the relation to the pivot, and not the other object.

                    Here is a simplified schema demonstrating the relationship.

                    <?xml version="1.0" encoding="UTF-8"?>
                    <model package="resellers" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.0">
                    
                        <!-- Resellers table -->
                        <object class="Reseller" table="resellers" extends="xPDOSimpleObject">
                            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
                            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                                <column key="id" collation="A" null="false" />
                            </index>
                    
                            <field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default=""/>
                            <field key="comments" dbtype="text" phptype="string" null="false" default=""/>
                    
                            <field key="createdon" dbtype="datetime" phptype="datetime" null="true"/>
                            <field key="createdby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
                            <field key="editedon" dbtype="datetime" phptype="datetime" null="true"/>
                            <field key="editedby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
                    
                            <field key="deleted" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
                            <field key="published" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
                    
                            <composite alias="ResellerProducts" class="ResellerProduct" local="id" foreign="user_id" cardinality="many" owner="local"/>
                            <aggregate alias="CreatedBy" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign"/>
                            <aggregate alias="EditedBy" class="modUser" local="editedby" foreign="id" cardinality="one" owner="foreign"/>
                        </object>
                    
                        <object class="Product" table="products" extends="xPDOSimpleObject">
                            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
                            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                                <column key="id" collation="A" null="false" />
                            </index>
                    
                            <field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default=""/>
                            <field key="comments" dbtype="text" phptype="string" null="false" default=""/>
                    
                            <field key="createdon" dbtype="datetime" phptype="datetime" null="true"/>
                            <field key="createdby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
                            <field key="editedon" dbtype="datetime" phptype="datetime" null="true"/>
                            <field key="editedby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
                    
                            <field key="deleted" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
                            <field key="published" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
                    
                            <composite alias="ResellerProducts" class="ResellerProduct" local="id" foreign="product_id" cardinality="many" owner="local"/>
                            <aggregate alias="CreatedBy" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign"/>
                            <aggregate alias="EditedBy" class="modUser" local="editedby" foreign="id" cardinality="one" owner="foreign"/>
                        </object>
                    
                        <object class="ResellerProduct" table="reseller_product" extends="xPDOObject">
                            <field key="id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
                            <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                                <column key="id" collation="A" null="false" />
                            </index>
                    
                            <field key="reseller_id" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
                            <field key="product_id" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0"/>
                    
                            <aggregate alias="Reseller" class="Reseller" local="reseller_id" foreign="id" cardinality="one" owner="foreign"/>
                            <aggregate alias="Product" class="Product" local="product_id" foreign="id" cardinality="one" owner="foreign"/>
                        </object>
                    </model>
                    [ed. note: dmeganoski last edited this post 10 years, 2 months ago.]
                      • 52563
                      • 12 Posts
                      What I mean to say is, Isn't there a way to define the relation so that I could do something like:

                      $reseller = $modx->getObject('Reseller', 1);
                      
                      $res_products = $reseller->getMany('Products');