Bob's Blog

Why Subscribe?

As a Subscriber, you will receive access to certain parts
of the site that are hidden from others. You'll also get
email updates when new content is available
on the site or existing content is updated.

When logged in, you'll no longer see this request to
subscribe.

As an added incentive to subscribe, I have a bunch
of new and updated MODX extras I plan to release
as soon as I have a significant number of subscribers
to notify.

Privacy Policy

I'm too lazy to collect any information about you other
than the data you submit when you subscribe, which I will
never intentionally share with anyone.

As usual, Google and other entities may also collect
information about you during your visit.

Easy MODX Custom Manager Pages (CMPs) V

In the last article, we had our search working, but with a page reload. In this final article, we'll move much of our controller code into a processor (where it belongs), and use JavaScript to call that processor (via a connector) when the form is submitted.

See the previous articles for the Tpl chunks, menu, and namespace information.

Step One

The first step in making the form JS ready is to add an onClick() event to the submit button. Change the input for the submit button in your MySearchTpl chunk to look like this:

That will launch the JavaScript handleFormSubmit function rather than submitting the form. We'll see the JavaScript code in a bit. First, we'll look at the new, minimized controller file (home.class.php).

New Controller

This is basically our old controller minus the parts that go in the processor. Here's the code:

As you can see, we're down to three methods, initialize, getPageTitle, and process(). Notice that we've uncommented the code in the initialize() method that loads our JavaScript (which we haven't created yet).

Creating the Connector

Ajax calls to processors in MODX are almost always done via a connector for better security. Our connector goes in the assets/components/mysearch/ directory and is called connector.php. Here's the code:

The first include is for my development environment, so you can leave it out if your connector file is in the traditional location. If not, you can adjust the include to find any of the three MODX config.core.php file — usually, the one in the MODX root directory. For each level up, you add another dirname( at the front and another ) before the dot.

Adding the JavaScript

The usual method for using JavaScript is to put it in a file and inject some separate JS code in to the page that holds the config data the JS needs to do it's work. If you have a fair amount of data to pass to the JS code, that works well, but it usually involves instantiating a class file just to get the config data. For simple cases, I prefer to just put the JS code in a chunk with placeholders for the data you need to pass.

In our case, the only thing we need to pass is the URL for our Ajax call.

I was determined to make this JavaScript work without JQuery (or extJS/modExt). I was able to do it, but it taught me a lot about the value of JQuery when making Ajax requests. I estimate that using JQuery would have cut the programming time by at least 80%.

In many cases, MODX JavaScript returns a JSON object or array from a processor. Commonly, there's a success member set to true of false and often a count member returning the number of results. That kind of return value needs to be decoded and processed in the JS code. In our case, though, the processor itself is returning either the full output HTML or an error message. If the status (200) indicates that the processor was found, all we do is insert whatever the processor sends us into the dataContainer element.

Note that we never bothered to remove the mysearch_results placeholder in our MySearchTpl chunk. It's no longer necessary since that placeholder will never be set. Our JavaScript will be setting the entire content of that div with the code below. Nothing will be displayed there until that code executes, but the placeholder serves as a reminder of where the results will appear.

The most challenging part of the JS code is the formToJSON() function. When we were doing everything in PHP, we simply modified the form to create the $_POST array we needed. That $_POST array was sent automatically when the form was submitted. Now that we're doing everything in JavaScript without reloading the page, we need JS code that will create that same array from the form on the screen. The form itself is passed in onClick="handleFormSubmit(this.form). Our formToJSON() function just walks through the form's elements, storing the names and values in an array as it goes. That array is what we'll send to our processor via the connector. I'm curious to know if JQuery's serialize would have handled this correctly.

dataContainer.innerHTML = request.responseText;

Adding the Processor

Now that we have our JavaScript loaded and calling the processor (via the connector), we need an actual processor to handle the submission and return the results. You'll see a lot of familiar functions here that were removed from the home.class.php controller file. Here's the code:

The code above is explained by the comments (and we've seen most of it before), but we've added one new method: getContentField(). This method allows us to use the term content in any of the sections of the form even though the "content" field of snippets and plugins is not called that. We could have renamed the name field for those inputs, but this method makes that unnecessary. The method returns the correct name of the content field for all objects except modUser, where it wouldn't make sense.

Wrapping Up

We now have a MODX CMP that uses chunks for its Tpls and a real processor to communicate with the database. With the addition of a few more lines in the MySearchTpl chunk's form, it would search for users, resources, chunks, templates, snippets, and/or plugins. It will even search in the content of any of those objects. If you do add sections, be sure to use the actual field names in your form. MySQL will complain, for example, if you try to search the pagetitle field of a chunk.

Along the way, I hope you've learned a little about the structure of MODX CMPs and how to implement them.

Improvements

One obvious improvement would be to add an input field to the form so users could select the maximum number of results to return for each object. That would be quite easy. The current JavaScript that creates the data array would insert is automatically, so it would just be a matter of extracting it in the processor's initialize() method and using it to set the limit for the query.

Another, not-so-easy, improvement would be to make the search itself more sophisticated. It could search for multiple words (a la Google) with both an and and or option. It would also be nice to be able to exclude results with a certain word or phrase and allow grouping parts of the search with parentheses, so a search term like (bread && butter) || (toast && jam) -(wheat || rye), would find objects containing both bread and butter or both toast and jam, but only if they do not contain wheat or rye. The icing on the cake would be to allow regular expression searches, which would accomplish all of the above.

At some point, I will be releasing a premium extra called CustomSearch that incorporates all of those options and does searches of resources, users, and all elements. It will even search the code of snippets and plugins, and the values of most TVs. It will include a regular expression option. Until I do, you can use the code above to develop your own search algorithms.

Coming Up

In the next article, we'll look at how APIs work.

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.)

Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. MODX will work fine at most hosting services, but having a MODX-friendly host can prevent a lot of frustration. Better yet, the A2 Solid-State-Drive servers are configured to handle the many Ajax and database calls made by MODX — especially the MODX Manager. My Manager runs about four times as fast as it did on my previous host. I particularly recommend the Swift package with the Performance Plus option.