Skip to content
Everett Griffiths edited this page Feb 23, 2015 · 4 revisions

Sometimes you need your packages to create MODX resources. This can get tricky because resources rely on primary keys more than other objects. Remember that a primary key is an artifact of installation in a particular environment: there's nothing inherent in any of your package's objects that should require a specific primary key. Your relations should work equally well if the primary key is 1 or 1000. Repoman's seed data attempts to represent all objects and their relations without any primary keys. This works pretty well when the objects and relations are simple, but as you'll see with resources, it can get complicated.

Repoman tries to handle these complex relationships automatically whenever it can, and for a majority of the use-cases, it does. In this tutorial, however, we cannot escape paying attention to how object relations are defined.

Simple Resources as Seed Data

MODX pages (i.e. resources) are represented by Seed-Data. Any time we use seed data, we need to specify the build_attributes in our composer.json so Repoman knows how to handle the objects without needing to know the primary key.

It can be useful to use Repoman's graph function to get a reference of all the available fields:

php repoman graph modResource

Remember that the output includes both aggregate and composite relations (relation names begin with a capital letter).

Using the graph output as a reference, we could come up with an object like the following, saved as model/seeds/modResource.php, or identify it uniquely by setting a prefix model/seeds/coffee.modResource.php:

<?php
return array
(
    'type' => 'document',
    'contentType' => 'text/html',
    'pagetitle' => 'Cup of Coffee',
    'longtitle' => 'This is my big cup of coffee',
    'description' => 'Roasted and ground high in the mountains....',
    'alias' => 'coffee',
    'published' => 1,
    'parent' => 0,
    'isfolder' => 0,
    'introtext' => 'Brief summary here',
    'content' => 'All my content goes here',
    //'template' => 0,
    'menuindex' => 0,
    'searchable' => 1,
    'menutitle' => 'Coffee',
    'class_key' => 'modDocument',
    'context_key' => 'web',
    'content_type' => 1,
    'uri' => 'coffee',
    'uri_override' => 0,
    'show_in_tree' => 1,
);
/*EOF*/

In your composer.json file, you will need to define a section in the "extra" node for "build_attributes". In it, you will define the "unique_key", which determines which field will uniquely identify your resources. Good candidates for this field would be the "alias" or "uri" field.

"build_attributes": {
    "modResource": {
        "preserve_keys": true,
        "update_object": true,
        "unique_key": "alias"
    }
}

WARNING: If you do not define "build_attributes" for the modResource class in your composer.json, each resource imported will be imported as resource 1!

When you run repoman install or update, this will cause the resource to be imported, but you'll notice that no template is set (indeed, it is commented out). You could add a primary key integer to the template attribute, but that would not be guaranteed to work on every installation. You need a way to relate this specific resource to a specific template without a primary key. Hmmmm.... tricky.

Relating a Resource to a Template

So if we go a bit further, we can define a relation from the modResource to its Template. This is upper-case Template, not lowercase template. The lowercase attribute is the column name (which expects a primary key), the uppercase Template is the modResource's alias for a modTemplate object. You can use the graph function again to pass our sample resource a sample template:

<?php
return array
(
    'type' => 'document',
    'contentType' => 'text/html',
    'pagetitle' => 'Cup of Coffee',
    'longtitle' => 'This is my big cup of coffee',
    'description' => 'Roasted and ground high in the mountains....',
    'alias' => 'coffee',
    'published' => 1,
    'parent' => 0,
    'isfolder' => 0,
    'introtext' => 'Brief summary here',
    'content' => 'All my content goes here',
    //'template' => 0,
    'Template' => array(
        'templatename' => 'Grande',
        'description' => 'Huge layout with cream',
        'content' => '
<!DOCTYPE html>
<html>
<head>
    <title>[[*pagetitle]]</title>
    <meta name="description" content="[[*description]]"/>
    <link rel="stylesheet" type="text/css" href="[[++templatetest.assets_url]]main.css">
</head>
<body>
<h1>[[*longtitle]]</h1>

- GRANDE -
<hr/>

[[*content]]

</body>
</html>',
    ),
    'menuindex' => 0,
    'searchable' => 1,
    'menutitle' => 'Coffee',
    'class_key' => 'modDocument',
    'context_key' => 'web',
    'content_type' => 1,
    'uri' => 'coffee',
    'uri_override' => 0,
    'show_in_tree' => 1,
);
/*EOF*/

Defining Relations: build_attributes

Running php repoman install at this point will likely generate an error:

build_attributes not set for modResource-->Template in composer.json. 
Make sure your definitions include "related_objects" and "related_object_attributes"

If you follow the examples on the composer.json page and read-up on Repoman's Configuration-Attributes, you would learn to define the relationship in your composer.json like this:

  "extra": {
    "build_attributes":{
      "modResource": {
        "preserve_keys":true,
        "update_object":true,
        "unique_key":"alias",
        "related_object_attributes": {
          "Template": {
            "preserve_keys":true,
            "update_object":true,
            "unique_key":"templatename"
          }
        }
      }
    }
  }

It's important here that we are defining ways to identify objects without primary keys. In the above configuration we are instructing Repoman to identify resources by their alias and templates by their templatename -- see the "unique_key" parameter. Depending on your needs, you may need to identify your objects using different columns ("unique_key" does accept an array).

Problems and Solutions

If you're paying close attention here, you should notice a couple problems with the above solution:

  1. What happens if I want multiple resources to use the same template?
  2. Defining a template inside a PHP array makes it very difficult to edit.

You could define the relationship the other way around: you could create a modTemplate.php seed file and define an array of Resources. But there is an easier solution that solves both the above problems.

Define your Template attribute so that it references only the templatename of an existing template (i.e. a template you created normally as an Element).

Your model/seeds/modResource.php file ends up looking something like this:

<?php
return array
(
    'type' => 'document',
    'contentType' => 'text/html',
    'pagetitle' => 'Cup of Coffee',
    'longtitle' => 'This is my big cup of coffee',
    'description' => 'Roasted and ground high in the mountains....',
    'alias' => 'coffee',
    'published' => 1,
    'parent' => 0,
    'isfolder' => 0,
    'introtext' => 'Brief summary here',
    'content' => 'All my content goes here',
    'Template' => array(
        'templatename' => 'yourTemplate',
    ),
    'menuindex' => 0,
    'searchable' => 1,
    'menutitle' => 'Coffee',
    'class_key' => 'modDocument',
    'context_key' => 'web',
    'content_type' => 1,
    'uri' => 'coffee',
    'uri_override' => 0,
    'show_in_tree' => 1,
);
/*EOF*/

This will trigger Repoman to look for an existing template by the name of "yourTemplate" so long as you have defined your relations in your composer.json build_attributes:

  "extra": {
    "build_attributes":{
      "modResource": {
        "preserve_keys":true,
        "update_object":true,
        "unique_key":"alias",
        "related_object_attributes": {
          "Template": {
            "preserve_keys":true,
            "update_object":true,
            "unique_key":"templatename"
          }
        }
      }
    }
  }

Clone this wiki locally