ProcessWire 3.0.108

Continuing from last week's post, ProcessWire 3.0.108 is now available on the dev branch, and it adds support for a new, more powerful live search in the admin. This week we will take a closer look at how it works, as well as how module developers can make their modules searchable too.

If you haven't yet read last week's post, I recommend reading that first, as this post is a continuation of that post. The little search box in our admin themes has gained a whole lot of power. As always, you can just start typing something, and it'll start finding matching pages. The biggest differences now are that any module can implement the SearchableModule interface, and then make its items available in the search. In addition, the search box now lets you specify a single selector, for the times when you want to focus your search on a specific type of items, or on matching a particular property or field within those items. This opens up a whole lot of new options.

As a user in the admin, there's a lot of new stuff you can do with the search, and you don't want to have to remember what all the options might be. So just type "help" in the search box, and it'll show you all the types that are currently installed for search, a few examples to get you started, and a list of all the properties that are searchable for each type. In some cases, there might be a lot of properties, so you'll want to hover over the "properties" label for a tooltip that shows them all in a list. Here's what the "help" search looks like (in AdminThemeUikit):

Notice that first group of items, where it says to "click here" to configure the search settings. If we click that, it takes us to the ProcessPageSearch module configuration. This is where we can fine tune what order the search results are displayed in, what types should be excluded, what fields are searched on pages (if no field specified in the search) and what default operators are used.

Last week's post went into several examples of searches, which we won't do again in this week's post. However one shortcut has been added that I wanted to mention. You can now perform "field=value" searches on pages, which is a shortcut for "pages.field=value". For example, "body=ProcessWire" would find all pages having a "body" field that contains the term "ProcessWire". You can also perform "type=value" (like "comments=test"), and "template=value" (like "basic-page=test") searches. As a result, the "field=value" search can only be used if the field name you need to search doesn't collide with an existing type or template. Chances are it won't. But if it does, then you'd want to use longer "pages.field=value" instead.

How to implement search in a module

This next part is for module developers. Implementing search in a module is actually quite simple. The first step is just to decide whether your module deals with items that should be searchable in the admin. If it does, then the next step is to tell ProcessWire by adding [ "searchable" => "name" ] to the module's info array. You'd replace the "name" with whatever term should identify your items in the search. In this case, it is "widgets" (I'm not sure what widgets are, but lets just go with it for now):

public static function getModuleInfo() {
  return [
    'title' => 'Widgets',
    'summary' => 'Manage widgets',
    'version' => 1,
    'searchable' => 'widgets', // add this line
  ];
}

By adding that "searchable" property to the getModuleInfo(), ProcessWire knows to load your module and call a search() method from it, whenever an admin search is performed. Your module does not need to be auto-load, and no hooks are involved in the process.

Next we have to implement that search() method. It receives two arguments: the $text to search for, and an $options array. For a really simple search() method, we can just focus on the $text to search for:

public function search($text, $options) {
  // ...
}

ProcessWire expects an array return value with the following at minimum:

$result = [
  'title' => 'Widgets',
  'items' => []
]; 

The 'items' array is the most important part. Each item in that array represents a matching search result, and each item is itself an array that represents the item. Each item must have a minimum of "title" and "url" properties:

$result = [
  'title' => 'Widgets',
  'items' => [
    [
      'title' => 'Slovenly widget',
      'url' => '/url/to/slovenly-widget/',
    ],
    [
      'title' => 'Suspicious widget',
      'url' => '/url/to/suspicious-widget/',
    ],
    // and so on
  ]
]; 

So our fully implemented search() method could look like this below. Now "widgets" are a fictional data type for our example here. But we'll assume that our module has access to these widgets from $this->widgets, and that each Widget item has title and url properties:

public function search($text, $options) {
  $result = [
    'title' => 'Widgets',
    'items' => []
  ];
  foreach($this->widgets as $widget) {
    if($widget->name == $text) {
      // widget matches, so add it to items
      $result['items'][] = [
        'title' => $widget->title,
        'url' => $widget->url
      ];
    }
  }
  return $result;
}

That's all there is to it when it comes to the basics. All that's left to do is to test it out. However, ProcessWire might not yet know that your module is searchable. In order for it to find out, you'll have to do a Modules > Refresh. Once you've done that, ProcessWire will know about it.

To test out your search, start typing something that you know will match one or more of your widgets, like "test". They should appear in the drop-down search results under the "Widgets" heading. Notice that it also matches any other (non-widget) data types that contain the term you searched for (including pages, fields, templates, modules, etc.). If you want to limit your search to just widgets, then type "widgets=text", and it will only call your widgets search() method.

This is just a simple example, but if you are interested in implementing a search feature in your module, I'd encourage you to look closely at the SearchableModule interface, which explains all of the options and details. The purpose of this SearchableModule interface is purely for documentation purposes, so it's not necessary for your module to have an "implements SearchableModule" in its class definition, unless you want to. That's because ProcessWire instead uses the "searchable" module info property to determine if the module is searchable, since it is more efficient in our context. But it is a good idea to read all the documentation in the SearchableModule interface, because there's a lot more you can do when it comes to implementating a search. Examples of more complete search() methods can also be found in ProcessTemplate, ProcessField, ProcessPageType, and ProcessCommentsManager.

Thanks for reading, and I hope that you have a great weekend. Check in at the ProcessWire Weekly this weekend for the latest ProcessWire news.


Comments

  • AndZyk

    AndZyk 5 months ago 20

    Thank you for this nice addition. I prefer the page search over the page tree, so this will come in handy. :-)

Post a Comment

Your e-mail is kept confidential and not included with your comment. Website is optional.