Easy MODX Custom Manager Pages (CMPs) II

Make our easy CMP do an actual search

In the last article, we saw a really easy way to create a Custom Manager Page (CMP), but our page didn't actually perform any actions. In this article, we'll make some changes so it actually searches for resource and shows the results as a list with links to edit the resources.

MODX logo

New Code in the Controller

See the previous article to look at the namespace, menu, Tpl chunk, and the original controller.

In this article, all we'll change is the code in the controller file (home.class.php) we created in the last article. Here's the new controller code:


class mysearchHomeManagerController extends modParsedManagerController {

    protected $maxResults = 5;
    protected $results = '';
    protected $resultTpl = ''; // Tpl for each line of results (see initialize())

    public function getPageTitle() {
        return 'My Search';

    public function initialize() {

        /* Change $maxResults if there's a Setting ?*/
        $maxResults = $this->modx->getOption('ms_max_results', null, false, true);
        if ($maxResults) {
            $this->maxResults = $maxResults;

        /* Set result Tpl */
        $this->resultTpl = "\n<li>  " . '<a href="' . MODX_MANAGER_URL .

    public function find($searchTerm, $objectType = 'modResource',
            $nameField = 'pagetitle',
            $action = 'resource/update', $fields = array()) {

        $query = $this->modx->newQuery($objectType);
        $criteria = array(
            'pagetitle:LIKE' => '%' . $searchTerm . '%',
            'OR:longtitle:LIKE' => '%' . $searchTerm . '%',
            'OR:alias:LIKE' => '%' . $searchTerm . '%',
            'OR:description:LIKE' => '%' . $searchTerm . '%',
            'OR:introtext:LIKE' => '%' . $searchTerm . '%',
            'OR:id:=' => $searchTerm,


        $collection = $this->modx->getCollection('modResource', $query);

        /* All members of $collection match the search term */
        foreach( $collection as $object) {
            $id = $object->get('id');
            $name = $object->get($nameField);
            $this->addToResults($action, $name, $id);

        return empty($collection)? false : true;

    public function addToResults($action, $name, $id) {
        /** @var $this->resultTplChunk modChunk */
        $line = str_replace('[[+action]]', $action, $this->resultTpl);
        $line = str_replace('[[+id]]', $id, $line);
        $line = str_replace('[[+name]]', $name, $line);
        $this->results .= $line;

    public function process() {
        if (isset($_POST)) {
            if (!empty($_POST['search_term'])) {
                $fields = $this->modx->getOption('resourceFields',
                    $_POST, array(), true);
                if ($this->find($_POST['search_term'])) {
                    $this->results = "\n<br><b>Resources</b><ul>" .
                    $this->results . '</ul>';
                } else {
                    '<p>No Results</p>');

             // $this->modx->setPlaceholder('mysearch_results',
             // '<pre>' . print_r($_POST, true) . '</pre>');
        $output = '[[!$MySearch]]';

        return $output;

We've added a few class variables, $maxResults, $results, and $resultTpl at the top. The first one limits the number of results returned. The second one holds the final search results that will be displayed on the page. We've made $results a class variable so we won't have to pass it around to methods and return it. This allows us to return true or false from our find() method to indicate whether results were found or not. The third class variable holds the Tpl used for each line of the results. This gives us the option of using an actual Tpl chunk pulled in with $modx->getChunk(), which we'll do in the next article.

When MODX runs a CMP, it automatically calls the controller's initialize() and process methods.

We've added an initialize() method. This method will be called first by MODX when the controller code executes. It overrides the parent class's initialize() method, so we call parent::initialize() ahead of our own initialization code. All we do in our initialize() method is change $maxResults if there is a setting for it (e.g., a System Setting or User Setting), and initialize the ResultTpl variable. If we were going to use an actual Tpl chunk, we would assign the value here.

After the initialize() method is finished, MODX will call the process() method. If the form has been submitted, the find() method will be called with the search term as the only argument. If there are results, we add a heading and ul tag to the beginning of the output and a /ul tag to the end.

No matter what, we display the form retrieved from the MySearch chunk in case the user wants to search again.

The find() method, which is only called once, actually queries the database and builds the final output. After running the database query, we loop though the results (if any), all of which contain the search term. For each one, we send the required information to addToResults() where one new line gets added to the results using the resultTpl chunk to format it.

If you modify the controller file with the code above and clear the site cache, you should be able to use our CMP to search resources.

Looking Ahead

We could actually have put all our code in the process() method. Separating it into separate functions makes it easier to maintain (though a few millisecond slower). More importantly, it helps set things up for modifications we might want to make later. Say, for example, we want to search other objects like chunks and templates. With our find() and addToResults() methods, we're more than halfway there.

Coming Up

In the following article, we'll add some checkboxes so users can decide which objects to search and which resource fields to search. We'll also redesign the controller so that it will be easier to do the processing with JavaScript to avoid reloading the whole Manager page.

Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)

Comments (0)

Please login to comment.