Getting Resources Inside and Outside a Resource Group

A simple snippet to retrieve Resources in, or out of a Resource Group

Suppose you want to get all the Resources in a Resource Group, or all the Resources *except* the ones in the Resource Group. Maybe you want to display them a little faster than getResources would do it, or maybe you're creating a utility snippet to do something with each resource such as adding a prefix to the alias.

Here's a simple snippet called GetGroupResources that will get the Resources inside or outside the group, depending on a property in the snippet tag.

The Tag

[[!GetGroupResources? &groupId=`12` &inGroup=`1`]]

As you've probably guessed, &groupId is the ID of the Resource Group, shown in parentheses after the name of the group on the left side of the panel reached by selecting "Security", then "Resources Groups" in the MODX Manager's Top Menu.

The &inGroup property is set to 1 when you want Resources in the Resource Group and 0 when you want Resources outside the group.

The Snippet

/* GetGroupResources snippet */

$output = '';

$groupId = $scriptProperties['groupId'];
$inGroup = $scriptProperties['inGroup'];

$query = $modx->newQuery('modResourceGroupResource');

/* Set the criteria for the search based
 * on the &inGroup property */
if ($inGroup) {
    $query->where(array('document_group' => $groupId));
} else {
    $query->where(array('document_group:!=' => $groupId));

/* Get the modResourceGroupResources */
$rgrs = $modx->getCollection('modResourceGroupResource', $query);
$count = count($rgrs);

foreach ($rgrs as $rgr) {
    /** @var $rgr modResourceGroupResource */
    $doc = $rgr->getOne('Resource');

    $output .=  "<br />" . $doc->get('pagetitle');
    /* Do something with $doc object */

$output .= '<br /><br />Did something with ' . $count . ' Resources';
return $output;

What the Heck is a modResourceGroupResource?

The code above is fairly self-explanatory, but the $rgrs variable needs some explanation.

If there can only be one value for something associated with a Resource, such as the parent, template, pagetitle, or alias, MODX stores the information with the resource in a single field of that Resource's row in the modx_site_content table.

That doesn't work, though, when there can be more than one value to store. Resources can belong to more than one Resource Group (just as Users can be in more than one User Group). In those cases, MODX stores the information in an "intersect" object, which in this case has the class modResourceGroupResource.

Resources are stored in one table and Resource Groups are stored in another table. The connections between the two are stored in a third table (in this case, the modx_document_groups table). Each row in that table stores a modResourceGroupResource intersect object that just records the ID of the Resource Group and the ID of the Resource. In that table, the Resource ID is in the document field and the Resource Group ID is in the document_group field.

So, if a Resource belongs to six Resource Groups, there will be six rows in the modx_document_groups table, one for each Resource Group the Resource belongs to. Each row is a modResourceGroupResource object as far as MODX is concerned. The document field would contain the ID of the Resource, and the document_group field would contain the ID of the Resource Group.

The intersect objects are always named using the names of the two objects being connected, modResourceGroupResource, modPluginEvent, modElementPropertySet, modUserGroupMember, and so on.

Look at our line of code specifying the query's "where" clause to get the resources in the group:

$query->where(array('document_group' => $groupId));

In that line, we're asking MODX (xPDO, actually) to give us all the modResourceGroupResource objects where the document_group matches the ID of the Resource Group we're looking for.

Once we have them, we loop through them and ask each of them for their related Resource object. Once we have the Resource object, we can do whatever we like with it.

All intersect objects in MODX have two getOne() calls, one to get each of the two objects they point to. We could have retrieved the Resource Group object, if we wanted it, with this code:


The aliases used to get the related objects from the intersect objects are documented here. Here's a direct link to the modResourceGroupResource one. You can see the fields and the ResourceGroup and Resource aliases.

Speed Considerations

For getting Resources in the group, you might wonder about the alternative method of getting the modResourceGroup object, then getting its Resources with code like this:


This would actually be a tiny bit slower, because getResources() gets the Resources via the modUserGroupResource intersect objects just like we did. Doing it as we did above eliminates the extra function call to getResources().


There are a couple of things we might want to do to enhance our snippet. First, we might want to let users select the Resource Group by name, rather than ID. This will slow things down slightly, but probably not enough to notice. If we send the name of the Resource Group in the snippet tag, (say, in a &groupName property), we'd just need to add this at the top of our code, replacing the $groupId line:

$name = $scriptProperties['groupName']
$group = $modx->getObject('modResourceGroup', array('name' => $name));
if ($group) {
    $groupId = $group->get('id');
} else {
    return 'Group not found';

We've added a sanity check here to make sure the group is found. It's not needed in our earlier version because an invalid group would just return no resources. In this case, though, an invalid group name would throw a confusing PHP fatal error when we tried to call $group->get().

A second enhancement would be to allow the user to send a comma-separated list of multiple group IDs or names in the snippet tag. Then, for IDs, our "where" clause section would look something like this:

/* convert comma-separated list to an array */
$groupIds = explode(',' $scriptProperties['groupIds']);

/* set the query */
if ($inGroup) {
    $query->where(array('document_group:IN' => $groupIds));
} else {
    $query->where(array('document_group:NOT IN' => $groupIds));

For a comma-separated list of group names, we'd have to do something like this instead:

$groupNames = explode(',' $scriptProperties['groupNames']);
$groupIds = array();

/* build the $groupIds array */
foreach($groupNames as $groupName) {
    $group = $modx->getObject('modUserGroup', array('name' => $groupName));
    if (! $group) {
        return 'No such user group: ' . $groupName;
    $groupIds[] = $group->get('name');

/* set the query */
if ($inGroup) {
    $query->where(array('document_group:IN' => $groupIds));
} else {
    $query->where(array('document_group:NOT IN' => $groupIds));

Comments (2)

  1. Susan OttwellAug 18, 2013 at 11:35 PM

    How does this compare to using the MIGx snippet migxLoopCollection?

  2. Bob RaySep 23, 2013 at 12:23 AM

    TBH, I have no idea. I've never used migxLoopCollection.

Please login to comment.