Drupal Slugs: Menu-driven Paths Using Pathauto

So, you have installed Drupal for the first time, built your new About Page, gave it the path “about,” and put it on the menu. You then create a Contact Page, give it the path “contact,” and make its menu item a child of the About Page’s menu item. But…hey, why is the path “contact” and not “about/contact”? It seems like it should be possible to get logical, menu-driven, automated, editorially-controlled paths for pages on your Drupal site using modules like Pathauto, but how?

Something that people who are new to Drupal find perplexing is how paths and menus are managed. What appears most mystifying of all is that the two systems are completely independent of one another and seemingly impossible to link. The wonderful Pathauto module ultimately provides most of the functionality most people need, but it’s not immediately clear how one would link it up to the menu system in a meaningful way.

This is endlessly frustrating to people who are new to Drupal but familiar with other content management systems. WordPress and even older systems like Stellent (what is now Oracle UCM) have good solutions for this. Drupal does too, but you have to work for it.

Out of the box, Drupal also lacks the “slug” or URL-friendly string that can be used as the raw material for URL path building. Instead, Drupal typically relies on the Token module to “tokenize” the title (that is, make it all lowercase, remove spaces, “a,” “is,” “and,” etc.) This works great unless of course, you want to write your slug by hand (because, oh yeah, you can’t).

Pathauto

The typical solution used by people looking to have logical, automated paths for content is the incredibly powerful Pathauto module. It allows paths to be built based on tokens, which are effectively variables “about” that pertain to the specific piece of content.

For example, the default path for nodes is content/[node:title]. This would mean that the pages from our example would get the paths “content/about” and “content/contact,” which is a far cry from what we’re looking for.

Like I said though, Pathauto is powerful and it can do a lot more than that. A different pattern [node:menu-link:parent:url:path]/[node:title] gives us exactly what we’re looking for. Now our paths would be “about” and “about/contact.” Perfect. The only problem is that we’re still missing slugs, and more specifically, ones you can write by hand if you so choose.

One snag is bulk update, a process by which all missing paths are generated on the fly by Pathauto. Your paths basically have to be created in order, parents then children, in order for the alias pattern to work. Bulk update doesn’t care about menu depth, so we need to make it care about menu depth. Fortunately this is just a matter of a simple query alter hook, but more on that later.

Safeword

While the module name is slightly misleading, the functionality is great. Safeword creates a field that can be tied to the node title to create a “safe” version of your title, i.e. a slug. The logic for how the URL-friendly version of the title is calculated is driven by a regular expression, making the module as configurable as it is powerful.

Unfortunately, for our purposes, the regular expression isn’t enough, as it’s used both for validation and automatic title generation, meaning if you go back and add one of the words that was filtered out, the form will yell at you on validation.

To get around this, you should patch the Safeword module using the patch I made that adds a “Words to remove” field to the Safeword field settings. If you’ve never patched a module before, follow the patch instructions here.

Menu-driven paths, now with slugs!

So now that we have all the ingredients for our solution. Let’s tie it all together. My number 1 assumption is that your editorial users don’t otherwise have access to modify path aliases. The point of this solution is to have a site with super-uniform paths where the end user can control a big part of the node’s path, but not necessarily the whole thing.

Also, for the sake of this example, I’ll assume you’re adding this to the Basic Page content type, but you can add it any content type.

  1. Start by downloading and enabling Token, Pathauto and Safeword. Use Drush? Here’s a quick command:
    $ drush dl token pathauto safeword-7.x-1.x-dev; drush en -y token pathauto safeword
    It’s at this point you’ll want to apply the patch I listed above. You can do it all with one simple step, though (although you will have to manually type the path to the module):
    $ wget http://goo.gl/mAqjNl; patch -p0 < safeword_limit_words_in_machine_name-2458115-2.patch
  2. Next, go to /admin/structure/types/manage/page/fields and add a new field of type “Machine name from Title.” Call the new field “Slug.”
    Screen Shot 2015-03-12 at 9.39.05 AM
  3. When you create the field, there are a number of options available. You can pretty much leave all the options alone except the one for field we added, “Words to remove”. For our purposes, I’d recommend the following: a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via.
    Screen Shot 2015-03-23 at 10.41.20 PM
  4. Decide whether you want your users to be able to modify paths after content is created. Generally the answer is yes, but if you’re squeamish about this, uncheck the last checkbox in the configuration for this field. Once you’ve done that, save the field.
    Screen Shot 2015-03-23 at 10.44.03 PM
  5. Go to /admin/config/search/path/patterns and under “Content Paths” in the “Pattern for all Basic Page paths” put [node:menu-link:parent:url:path]/[node:field_slug].
    Screen Shot 2015-03-12 at 9.50.06 AM
  6. Now go to /admin/config/search/path/settings and uncheck “Reduce strings to letters and numbers.” This will allow our paths to be fully editorially controlled.
    Screen Shot 2015-03-12 at 9.52.04 AM
  7. If you plan on using Pathauto’s bulk update at any point, make sure you implement my quick little query alter snippet. You’ll need to stick this into a custom module somewhere, or even in the .module file of a feature, if you feel so inclined:

So now if you create a new page and start typing your title, the URL-safe version of the title will appear along side it with the connector words filtered out.

Screen Shot 2015-03-12 at 10.01.55 AM

If you want to modify the URL-safe slug directly, you can just click the “edit” link and now you can edit it directly.

Screen Shot 2015-03-12 at 10.02.21 AM

Other Solutions

If you follow everything I’ve told you up until this point, you’ll have a solution that’s robust and a pretty sure-fire crowd pleaser. If you’re less interested in giving editorial control of paths to your users, you can basically stop with Pathauto and Token and leave out Safeword.

I should point out that there is also a module called Slug which does attempt to solve the same problem a different way. The module works, but it’s still a little young yet. Plus, depending on the complexity of your site, you may want to utilize Pathauto to set up path aliases for content types that don’t appear in the menu. Either way, if you’re curious, I encourage you to check it out.

2 Responses to “Drupal Slugs: Menu-driven Paths Using Pathauto”

  1. Benji Fisher

    Thanks for the suggestions about configuring Pathauto. I like your menu-based paths better than the Pathauto default. I may use it on one of my own sites.

    What happens if the node does not have a menu entry?

    People often blame Drupal for the “content/what-i-wrote-about” URL’s. I think they do not realize that it is a symptom never taking the time to configure Pathauto.

    If you rearrange your menus, then Pathauto gives you a way to update all the path aliases (admin/config/search/path/update_bulk). It gives you some options about what to do with the old alias, but I think the Redirect module is your friend in this situation.

    Reply
    • Thomas MacLean

      No sense in having menu-driven paths without a menu 🙂 If a page doesn’t live on the menu for some reason, it’ll default to the root as if it just didn’t have a parent. The paths would be /about or /contact, from the example. Naturally if you have a content type that will never live on the menu, then just use a different pattern for that content type.

      Your point about bulk update reminds me: bulk update needs a little help to work with this solution. The problem is that bulk update will try to update the paths all willy nilly without taking into account menu depth, which is key, since the parent menu link is used in the child menu settings.

      I modified the article to include a code snippet that will make bulk update work as expected as a final step.

      Reply

Leave a Reply