By Daniel Sipos

Build a Drupal 8 Module: Routing, Controllers and Menu Links

By Daniel Sipos

How to Build a Drupal 8 Module

Please be aware that due to the development process Drupal 8 has been undergoing at the time of writing, some parts of the code might be outdated. Take a look at this repository in which I try to update the example code and make it work with the latest Drupal 8 release.

Drupal 8 brings about a lot of changes that seek to enroll it in the same club other modern PHP frameworks belong to. This means the old PHP 4 style procedural programming is heavily replaced with an object oriented architecture. To achieve this, under the initiative of Proudly Found Elsewhere, Drupal 8 includes code not developed specifically for Drupal.

One of the most important additions to Drupal are Symfony components, with 2 major implications for Drupal developers. First, it has the potential to greatly increase the number of devs that will now want to develop for Drupal. And second, it gives quite a scare to some of the current Drupal 7 developers who do not have much experience with modern PHP practices. But that’s ok, we all learn, and lessons taken from frameworks like Symfony (and hopefully Drupal 8), will be easily extensible and applicable to other PHP frameworks out there.

In the meantime, Drupal 8 is in a late stage of its release cycle, the current version at the time of writing being alpha11. We will use this version to show some of the basic changes to module development Drupal 7 devs will first encounter and should get familiar with. I set up a Git repo where you can find the code I write in this series so you can follow along like that if you want.

How do I create a module?

The first thing we are going to look at is defining the necessary files and folder structure to tell Drupal 8 about our new module. In Drupal 7 we had to create at least 2 files (.info and .module), but in Drupal 8, the YAML version of the former is enough. And yes, .info files are now replaced with .info.yml files and contain similar data but structured differently.

Another major change is that custom and contrib module folders now go straight into the root modules/ folder. This is because all of the core code has been moved into a separate core/ folder of its own. Of course, within the modules/ directory, you are encouraged to separate modules between custom and contrib like in Drupal 7.

Let’s go ahead and create a module called demo (very original) and place it in the modules/custom/ directory. And as I mentioned, inside of this newly created demo/ folder, all we need to begin with is a file with the following required content:

name: Drupal 8 Demo module
description: 'Demo module for Drupal 8 alpha11'
type: module
core: 8.x

Three out of four you should be familiar with (name, description and core). The type is now also a requirement as you can have yml files for themes as well. Another important thing to keep in mind is that white spaces in yml files mean something and proper indentation is used to organize data in array-like structures.

You can check out this documentation page for other key|value pairs that can go into a module .info.yml file and the change notice that announced the switch to this format.

And that’s it, one file. You can now navigate to the Extend page, find the Demo module and enable it.

As I mentioned, we are no longer required to create a .module file before we can enable the module. And architecturally speaking, the .module files will be significantly reduced in size due to most of the business logic moving to service classes, controllers and plugins, but we’ll see some of that later.

What is ‘routing’ and what happened to hook_menu() and its callbacks?

In Drupal 7, hook_menu() was probably the most implemented hook because it was used to define paths to Drupal and connect these paths with callback functions. It was also responsible for creating menu links and a bunch of other stuff.

In Drupal 8 we won’t need hook_menu() anymore as we make heavy use of the Symfony2 components to handle the routing. This involves defining the routes as configuration and handling the callback in a controller (the method of a Controller class). Let’s see how that works by creating a simple page that outputs the classic Hello world!.

First, we need to create a routing file for our module called demo.routing.yml. This file goes in the module root folder (next to Inside this file, we can have the following (simple) route definition:

  path: '/demo'
    _content: '\Drupal\demo\Controller\DemoController::demo'
    _title: 'Demo'
    _permission: 'access content'

The first line marks the beginning of a new route called demo for the module demo (the first is the module name and the second the route name). Under path, we specify the path we want this route to register. Under defaults, we have two things: the default page title (_title) and the _content which references a method on the DemoController class. Under requirements, we specify the permission the accessing user needs to have to be able to view the page. You should consult this documentation page for more options you can have for this routing file.

Now, let’s create our first controller called DemoController that will have a method named demo() getting called when a user requests this page.

Inside the module directory, create a folder called src/ and one called Controller/ inside of it. This will be the place to store the controller classes. Go ahead and create the first one: DemoController.php.

The placement of the Controllers and, as we will see, other classes, into the src/ folder is part of the adoption of the PSR-4 standard. Initially, there was a bigger folder structure we had to create (PSR-0 standard) but now there is a transition phase in which both will work. So if you still see code placed in a folder called lib/, that’s PSR-0.

Inside of our DemoController.php file, we can now declare our class:

 * @file
 * Contains \Drupal\demo\Controller\DemoController.

namespace Drupal\demo\Controller;

 * DemoController.
class DemoController {
   * Generates an example page.
  public function demo() {
    return array(
      '#markup' => t('Hello World!'),

This is the simplest and minimum we need to do in order to get something to display on the page. At the top, we specify the class namespace and below we declare the class.

Inside the DemoController class, we only have the demo() method that returns a Drupal 7-like renderable array. Nothing big. All we have to do now is clear the caches and navigate to and we should see a Drupal page with Hello World printed on it.

In Drupal 7, when we implement hook_menu(), we can also add the registered paths to menus in order to have menu links showing up on the site. This is again no longer handled with this hook but we use a yml file to declare the menu links as configuration.

Let’s see how we can create a menu link that shows up under the Structure menu of the administration. First, we need to create a file called demo.menu_links.yml in the root of our module. Inside this yml file we will define menu links and their position in existing menus on the site. To achieve what we set out to do, we need the following:

  title: Demo Link
  description: 'This is a demo link'
  parent: system.admin_structure
  route_name: demo.demo

Again we have a yml structure based on indentation in which we first define the machine name of the menu link (demo) for the module demo (like we did with the routing). Next, we have the link title and description followed by the parent of this link (where it should be placed) and what route it should use.

The value of parent is the parent menu link (appended by its module) and to find it you need to do a bit of digging in *.menu_links.yml files. I know that the Structure link is defined in the core System module so by looking into the system.menu_links.yml file I could determine the name of this link.

The route_name is the machine name of the route we want to use for this link. We defined ours earlier. And with this in place, you can clear the cache and navigate to where you should now see a brand new menu link with the right title and description and that links to the demo/ path. Not bad.


In this article we began exploring module development in Drupal 8. At this stage (alpha11 release), it is time to start learning how to work with the new APIs and port contrib modules. To this end, I am putting in writing my exploration of this new and exiting framework that will be Drupal 8 so that we can all learn the changes and hit the ground running when release day comes.

For starters, we looked at some basics: how you start a Drupal 8 module (files, folder structure etc), all compared with Drupal 7. We’ve also seen how to define routes and a Controller class with a method to be called by this route. And finally, we’ve seen how to create a menu link that uses the route we defined.

In the next tutorial, we will continue building this module and look at some other cool new things Drupal 8 works with. We will see how we can create blocks and how to work with forms and the configuration system. See you then.

  • Tatsh

    Symfony 2 but locked down (and hidden away basically) for no good reason. Forget about Composer and all the good that comes from that. It is also clear that PSR is not being used at all since I see 2 spaces instead of 4 in the Drupal code (minor complaint). Also, forget about the Symfony Routing component which solves all the problems Drupal was ever trying to solve in its routing system (hook_menu() and the like) other than form handling.

    How come the Drupal ‘community’ cannot stop being such recluses? Maybe it is licensing (which is just silly). They seem to stay in their own cocoon ( and the like) and pretend the rest of the PHP community does not exist or is not even necessary since ‘they can do it all’. Symfony 2 is basically the first time they actually acknowledged there is better code than their own that is well-written and tested. Maybe this even hurt Dries a little.

    As usual, code written for Drupal 8 will be stuck to Drupal 8, just as code written for 7 is stuck to that (back to all versions). There will probably zero migration path to 9 other than ‘porting’, which is the situation with 6-7 and 7-8. Those code-bases are also a nightmare to deploy using just the CLI. It is also clear that in 8, the database situation is still the same, which means your code depends on your database having the correct data in it. Wheels will be reinvented several times, guaranteed. Solved problems will be ‘re-solved’ in the ‘Drupal way’.

    This is why I do not get the point of using Drupal when Symfony provides almost everything in its components including form handling. Doctrine 2 can handle almost any kind of database people are using today. And Composer can put together all your libraries with a very nice and optimised auto-loader (one less thing for a developer to worry about).

    Another thing: no migration path. Most of the Drupal sites I maintained in 6 remain in version 6 today (and same for 7) and will be migrated off Drupal when 8 comes out and if they decide to kill 6 support (it is still an open question, since tons of sites still run on some version of 6, be it Pressflow or another profile).

    There’s only a few good things I see coming from Drupal using Symfony. There will actually be decent code in the Drupal code base for once. And not just all these crazy edge cases (Apache 1.x does this and IE 7 does that) and ‘free’ as in every developer acts differently. Standards might actually get enforced for once (but I have great doubts, especially based on 7’s modules).

    Another improvement is unit tests that can be run in CLI that do not rely upon a web-server running. These would be ‘pure’ unit tests. 7 and 6 both have ‘functionality tests’. Even with drush you are just running the same thing as what would be in the browser so there’s not much point to it. And unit testing is still a difficult thing to do in Drupal because you basically have to bootstrap the entire Drupal framework before you can run your test. That is not a unit test. That is like a ‘framework test’.

    Final improvement is the GUI. Clients I have worked with have generally always complained about how complicated the Drupal back-end is even with as many helpers I can give. The admin menu is one such helper, and the dashboard in Drupal 7. The GUI in 8 has been a little simplified but still does not come close to WordPress (which I am aware is a terrible code base but at least the GUI seems to be adopted by people of all different backgrounds, whereas Drupal mostly works with technical people even if they are not programmers).

    • Interesting perspective, thank you for taking the time to write it

    • chx

      I will leave it to others who know the Symfony – related code better to answer those.

      > Another thing: no migration path. Most of the Drupal sites I maintained in 6 …

      Funny you saying that with those exact words: Drupal 8 contains a migration path from D6 to D8.

      While judging the usability is not mine to do, have you seen quickedit in D8? :)

      • Tatsh

        Yes I have seen that in Drupal 8. I like it. It is a big improvement. Even clients have thought ‘that’s Drupal?’ after knowing what it is like to have a barely optimal WYSIWYG’d Drupal 7 experience.

    • Larry Garfield

      Greetings. It seems like the first part of your post may have been cut off, as it starts in the middle of a thought. I think there may be some confusion, though, so permit me to clarify.

      Symfony2 is a pure framework with a component base. Out of the box it doesn’t do anything; you have to write code to make it do anything useful. That is by design. Drupal, in contrast, is a full CMS application. Out of the box it can do all sorts of things, most of it from the GUI. So “I do not get the point of using Drupal when Symfony provides…” Symfony provides a lot of good things, certainly, and Drupal 8 makes use of many of them. But by design, Drupal does far more than Symfony does. For instance, Symfony has no standard menu link hierarchy tool; Drupal has one that scales to tens of thousands of items. Symfony has no content definition system; Drupal IS a content definition system. :-) Etc. So there are many many benefits to Drupal over “plain” Symfony, if what you want is a CMS. (If what you want is “just” a framework then I agree entirely; please go use Symfony2 fullstack, not Drupal.)

      Drupal 8 still has a lot of its own code in it that’s not from a 3rd party, but the last time I looked it had 3rd party code from 9 different sources, Symfony being just one of them. It’s actually the least insular version of Drupal ever, and actually the least “reclusive” PHP project around of those born before the Age of Composer. The Routing system you mention is the Symfony CMF Routing component, which was actually co-authored by Drupal and Symfony developers together (myself and David Buchmann, primarily) for both to use. Drupal has “gotten off the island” in a big way in recent years. DrupalCon Austin (last week) had a Symfony track; DrupalCon Amsterdam (this fall) has a general-PHP track. Lonestar PHP 2013 had 2 Drupal developers at it. This year there were 10. And so forth. There were people who bristled at replacing so much home-grown code with 3rd party components, from Symfony or elsewhere, but I can assure you Dries was not one of them. In fact, he’s said quite clearly that long-term he thinks hooks will go away and be replaced by Symfony events. It didn’t hurt him at all to say that.

      As for “PSR not being used”, as a member of the Framework Interoperability Group I must correct you that there is nothing called “PSR”. There are various FIG standard, labelled PSR-0, PSR-1, PSR-2, etc. The coding style spec is PSR-2, specifically. You’re correct that Drupal is not using PSR-2, as it has its own very well-developed coding standards. It is, however, using PSR-4 autoloading (via Composer) and PSR-3 logging, and *most* code is PSR-1 conformant by nature. If the number of spaces in the codebase is your biggest hangup with Drupal then it’s an even better platform than I think it is, which is saying a great deal. :-)

      Drupal 7 and earlier did make it difficult to write “Drupal-agnostic” code, that’s true. In Drupal 8, though, you’re limited only by your own skillset. Composer libraries are supported, and since it’s using the Symfony Dependency Injection Container you’re free to wire in any class you want, from whatever library you want, written however you want. Your module’s code is as Drupal-agnostic as you want to make it, and I very much encourage people to write code that is as Drupal-agnostic as possible. (That will also make moving modules from Drupal 8 to Drupal 9, whenever that happens, much easier.) We’ve also committed to having non-BC-breaking point releases of Drupal 8, unlike previous versions, which will make an investment in Drupal even safer, even if your code is Drupal-dependent.

      You mention “It is also clear that in 8 the database situation is still the same, which means your code depends on your database and having the correct data in it.” I don’t know where you got that idea. One of the biggest changes in Drupal 8 is that there is now a common, standard configuration system that has built-in universal export support. That means you can have your “configure everything in the UI” cake and eat your “check all of my configuration into Git and deploy that way” cake, too. It’s all based on YAML files. There are somewhere around a third as many SQL tables as there used to be, and almost all of them are for content. (That is, stuff you don’t want to deploy via Git anyway in a CMS.) The ability to define most of your site via the GUI has always been Drupal’s strongest suit, and it remains so. If you don’t want that, then Drupal is probably not the right tool for you. That’s OK. :-) But the deployment process in Drupal 8 is vastly improved over earlier versions and it’s disingenuous to claim otherwise unless you’ve talked to the team that was working on that support.

      As chx already noted, there WILL be a Drupal 6->8 migration path. He’s actually been leading that charge for some time. And there will be a 7->8 migration path afterwards. We decided specifically to work on the Drupal 6 migration first to help people get off of a version that is going to lose support sooner, which is something Drupal has never done before. If anything, Drupal 8 will have the best upgrade-ability from previous versions of the platform of any version of Drupal since I started 9 years ago. (And most of the enhancements from Pressflow were already baked into Drupal 7, and live on in Drupal 8.)

      As far as testing goes, extremely little of Drupal 7 was testable with “pure” unit tests; that is a entirely fair criticism and one that I’ve made for years. Drupal 8, however, is much more unit testable. There’s over 5000 straight-up PHPUnit tests in core now, and there’s work being done to clean that up further and replace even more integration tests with for-reals unit tests. The massive OOP-ification of Drupal over the past few years is the primary enabler of that. For those things that should be integration tests there is a team working to move those over to Behat. It won’t happen in time for Drupal 8.0.0, I expect, but it is and will continue to happen until we can finally kill off our home-grown integration testing suite.

      As far as comparing the GUI to WordPress, while a common trope it’s a misleading comparison. Drupal does vastly more than WordPress does; as there’s more things you can configure, there are more options to configure. It comes with the territory. That said, just this past Thursday we were running a training with a client’s content editor for a Drupal 7 site whose comment at the end was “wow, this is really so simple and easy!” And don’t under-estimate the impact of the inclusion of CKEditor 4 and the Quick-Edit in-place-editor. They’re much more than simply plopping a WYSIWYG into Drupal and going home. Actually I think the content editor experience of Drupal 8 is going to be one of its biggest wins.

      In all, it sounds like you’re writing a complaint of Drupal 7. Most of what you mention is a fair critique of Drupal historically, and is a critique I’ve made myself on many occasions. Drupal 8 is a whole new ballgame, though, and addresses most of what you mention.

      • chx

        Minor nit: about menu links you say “Drupal has one that scales to tens of thousands of items.” — it was designed for and tested with hundreds of thousands of items (I tested it with a subtree of the Open Directory Project so, so long ago).

        • Larry Garfield

          I stand corrected. :-) Thanks.

      • Tatsh

        You are correct. I do not like the idea of configuration via the GUI when it comes to adding or removing code or manipulating the database schema: modules, themes, the PHP filter module, etc (as in things that could easily break a site). In Drupal 6 and 7 the database is heavily used for querying states about code: what modules and themes are enabled? what views are enabled and displayable? what content types are there besides the ones defined in code and same for fields? As I am sure you know, security-wise the PHP filter presents a huge vulnerability similar to WordPress’ back-end where you can just edit any plugin or theme code (but not as bad). Developers are not reluctant to enable it, hoping the site never gets hacked.

        I would like to know if there is an improvement in Drupal 8, because when Drupal is ‘done right’ in 6/7 I almost have no problems with it. Code-based content types and views are so easily deployed with just drush. But here is what I think is a common scenario that is there to ‘save time and money’. From contractors I have seen this pattern or similar way too many times:

        – On the development site running locally (usually just the user’s own development database copy), create a new content type using the GUI (rather than in pure code in a module)
        – Check the database for changes made (yes, ‘SHOW TABLES’)
        – Write a module that depends on table field_somename_somefield existing
        – Test it (manually by hand 99% of the time), commit, etc

        Deployment steps:
        – On production, do the same thing and hope the same table name gets created
        – Pull code
        – Enable said module and ‘hope’ it works

        So why would developers do this? Because it is far easier to go into the GUI than it is to write a content or fields module by hand. And for views, there is not much of a way to create a view by hand (other than writing a menu hook, its page handler, and all the page rendering yourself skipping the Views module altogether; side note: at least this way you can optimise your queries yourself much more easily). I have no issues if you want to create a content type or anything in the GUI but you absolutely no plans to add your own code to. Still, I would recommend putting such things in code so database queries are avoided altogether just to initialise the content types during the re-cache process. But if you only have a few like that, the performance implications are pretty minimal compared to if you have code. You can drop the production database to staging’s and generally get the same result.

        Once you have enough of this kind of ‘code’ going on on a Drupal site you might as well put Varnish in front and call that a ‘solution’. Especially because cache rebuilds are essentially down-time which is not desirable ever. A decent size site with lots of hooks (especially theme hooks), custom content types and views (all in database), and modules can take a good 10+ seconds to bootstrap the first time while it rebuilds all cache (does not matter if database or another caching solution like Memcache is used really). How is this situation improved in Drupal 8?

        With Views you can export and for others, you can use Features to export. Some (but not all) developers do this. All it does is take away the GUI step in production deployment (a good thing). You can even use drush to really stay GUI-free. The drawback is that it does not remove the need to query the database about something that would be better off in code in terms of long-term maintenance.

        If you are saying that in Drupal 8, code lives with code and database is just content (and configuration is in YAML as Symfony 2 sort or prefers), then I am happy. However you already stated that configuration can all be done via the GUI as well. I am wondering then: are you writing the changes to the YAML files or the database? And regardless, this still makes it difficult for getting truly automated ‘always gets the same result’ deployments. People want to use Drupal, but an ops team wants to ensure that it is something easy to deploy, roll-back to working state (with talking to the developers being last resort), and so forth.

        I have never used CMF. Routing is also a stand-alone component that is fairly easy to use:

        Regarding WordPress, despite such a terrible code base and almost a lack of standards (and an unwillingness of the WordPress developers to ever truly fix it), WordPress is now pretty much a CMS as much as Drupal is. There are many plugins out there that make WordPress reach the initial feature set that Drupal comes with, and people are happy with this situation (mostly because they do not know or do not care how bad the code is and how bad maintenance can be). And on ‘just dropping a WYSIWYG’ in Drupal 6/7 as not being a complete solution, I will not disagree. But the problem lies in that both developer *and* clients do not generally ever want to do any kind of training beyond the absolute basics. Maybe there is a bigger issue here (and I think there are several). Still what I see is wider adoption of WordPress.

        • chx

          Enabled modules, enabled themes, views, content types, fields, field instances (and everything configuration) are all in a consistent config system. If you do it from the GUI, if you create it from code, doesn’t matter, it uses the same config API, stores them the same place, uses the same workflow. There is no need for ctools exports any more nor Features to do this sort of work (which was not the point of Features in the first place, it kinda grew into it).

          Code lives with the code but PHP code no longer defines configurable things. You can create a configuration object and then save it from PHP but that’s no different from doing it from the GUI. The configuration lives on the configuration storage and content lives in the content storage. Storages are pluggable, of course. By default the configuration and the content storage happens to be in SQL tables both but they can be in MongoDB for example as well. That is not relevant for what you are asking. Configuration deployment happens by exporting them into YAML files and then importing them into say a staging or production site.

          Of course WordPress has a wider adoption — there are more simple sites than complex sites, that’s a given.

          • Tatsh

            I hope you can understand where my scepticism comes from having worked on Drupal sites since version 5, then working on Symfony 2 sites and seeing how vastly behind Drupal 7 is by comparison when you look at mostly 2 things: configuration and deployment. If these things are resolved in Drupal 8 like the way you describe (being pluggable), then a lot of ops teams are going to be happy to deploy Drupal 8 sites compared to 6 and 7. Developers will be happier in that getting started will be easier (`drush rs` was a start (and then PHP’s own built-in server, which it now uses by default)). And outside developers will also be happy that they can easily put their 3rd party code into Drupal without (or with little) compromise.

          • You keep insisting on is this “developer-centric” view. Comparing Drupal (any version) with the Symfony framework is like comparing apples and oranges. Might as well throw Zend, Yii, and CakePHP into the mix…

            Drupal is a CMS, Symfony is a framework. Drupal can be installed and used by site builders as well, while frameworks are tools developers need time, skill and resource to use and turn into some application with an interface site builders can actually use. So please stop making this comparison.

            As for the developer experience, your grievances about Drupal 6 and 7 have been addressed already in the previous comments. Deployment and configuration have not been their strong suit indeed. But it’s also unfair to talk about Drupal 8 without having used it and based on a bias you have from 6 and 7. There are many improvements made, probably not all that will satisfy everyone fully, but that’s something left for Drupal 9 etc.

            So please stop comparing a CMS with a PHP framework. This comparison does not have it’s place.

          • Larry Garfield

            The expected workflow for the changes you describe in Drupal 8 would go something like this:

            * Create new content type through the GUI. Also create a View to go with it, maybe some display modes, an image style or two, etc.
            * Push “export configuration” (GUI button or CLI command)
            * Check your config/staging directory into Git.
            * Checkout from Git on your staging/production site.
            * Push “import configuration” (GUI button or CLI command)
            * Profit!!1!

            It’s similar to the recommend features-based workflow in Drupal 7 if you’re going all-in on features (which any Drupal shop that knows what it’s doing is already doing) but much more streamlined and a native “first-class citizen”; there’s far far far less hackery going on, so it should be vastly more stable than features-based deployment ever was.

            The exported files are all YAML, so you *could* craft them by hand and them import them if you wanted to, but I don’t expect most people will want to do so most of the time. Maybe for small edits and tweaks, but primary site building will still be a GUI experience. That said, there is work on a scaffolding tool for Drupal 8 similar to the one Symfony2 has, so you may have additional options there, too.

            Also, php module is dead and no one misses it. :-)

            Drupal 8 does use the Symfony2 Routing component you reference; the Symfony CMF Routing component is an extension for it, which we are also using. That’s why there’s so much similarity in the routing.yml file between Drupal and Symfony.

            If even that level of GUI is too much for you, the Entity Query system is now more robust, too. It’s not as powerful as Views, but it can do quite a bit if you really really really want to hard code a data query into a block or controller and do your own theming. If you want to do that, though, then as others have noted you probably don’t want to be using Drupal. Drupal’s whole point is that people who don’t write PHP can do useful things with their site. If you want to do everything with PHP and not have a GUI at all… just use Symfony2 directly or Symfony CMF and don’t spend your time fighting Drupal. It’s not the tool you’re looking for. *Jedi hand-wave*

            If you want a system that’s trying to balance developer power and site-builder power, and has managed to leap forward about 8 years of PHP evolution in 2.5 years (meaning we’ve only got another 2 years to go before we’re fully caught up! :-) ), then Drupal 8 is a system worth a serious look.

  • Roger Nyman

    Thanks Daniel, for this good and concrete intro. Looking forward to read more from you on the D8 track.

    • Danny

      Thanks and I am glad you found it useful. A couple of more articles are coming soon! Stay tuned!

  • Mike

    Hi! First of all: thanks for the post, it really helped me out.

    Second, in the current alpha (14), .menu_links.yml doesn’t work anymore. It has been changed to

    I realise Drupal 8 is still in alpha (it is hard to keep up with all of the changes), but I figured this could of some help for people who are trying to write their first module and are using the latest version to do so.


    • Thanks Mike,
      Thats helps and seems to be the way on the beta as well.

  • DarkPres

    Very smooth walk through till the ‘menu_links’ section when you go “digging around in the *.menu_links.yml files”. I could not replicate this. How precisely do I determine what my parent menu link is?

  • Hey there,

    You need to look in the core files and see the names of the menu links defined by core.


  • Антон

    Hi! Please change “demo.menu_links.yml” file name to “” to worked sample.

  • please_just_stop

    I saw “‘#markup’ => t(‘Hello World!’),” and thought, yeah, never again.

    How can this still be happening in 2014? The Drupal value proposition is to learn OO concepts and then somehow fudge them into the Drupal ‘way’ whilst dancing a circular jig to the sound of the Acquia money flute.

    If you’re going to this much trouble to route a module then why in the hell not just use a mature MVC framework? I’ve used Drupal since 6 but this direction makes no sense to me. Somehow we’re expected to find devs who understand higher level abstractions but at the same time are happy to put up with render arrays of doom?

    It’s almost as if the whole point of the project is to create expensive bi-annual upgrades to support a burgeoning group of cheerleading middlemen #sadface :(

  • cjwest

    Great demo! Simple and clear instructions.
    I could use some guidance. I’m able to enable the module and clear the cache, but I’m getting a “page not found” at /demo. I think I’m missing the secret handshake. Any suggestions for troubleshooting?

    • AbbyG

      Daniel – Thanks for this tutorial!

      I’m having the same problem as cjwest (am getting a ‘page not found’) error. Has anyone encountered this issue, then resolved it? If yes, please share solution.

      • Kathrin

        As Luis Eduardo Telaya Escobedo wrote in his post: in the demo.routing.yml-File change the String “_content” to “_controller”. I just had the same problem…

  • Luis Eduardo Telaya Escobedo

    Oh! change from _content to _controller so that it works perfect!

  • fabien

    Hi !
    Very good article but i’ve a question : How can we use our custom route in twig template ? I’ve trying {{ path(‘demo.demo’)}} but there is an error “Route does not exist”. Any idea ?

  • Dominique

    I am wondering if there is a simple way to have a dynamic title in the demo.menu_links.yml config file?

    something like:

    _title_callback: callbackroute::function

    I have been looking for a solution without success.

  • Arpit Rastogi

    Thanks for sharing such a nice post.
    Just a small fix instead of _content in routing.yml it should be _controller

  • Thomas Andrews

    After you change _content to _controller in demo.routing.yml, you may need to clear your caches again to stop getting “Page not found.”

  • Wolf_22

    What’s the deal with printing these pages in Firefox? I’m trying to print this in Firefox 38 and the preview only shows part of it. When I print it, same thing… In IE (Edge), it shows everything…

  • Diamond

    Very good article..thanks for sharing…

  • Jan Oostende

    Hi Daniel. Thanks for the information, I learned alot. I made a custom block module and also a custom form module. Is it possible to add the two modules together, so that the block is render the form as a block in the frontend.

  • Diamond

    Explained very well. Thanks for sharing.

  • Nick

    I’m not sure whether it’s changed or whether it’s a typo but I needed to use “_controller” instead of “_content” to remove the “Page not found” error.

Get the latest in PHP, once a week, for free.