 All right, so we hit 10.50, you've all had five extra minutes for coffee, hopefully that was enough. Again, PDF is already posted if you want to follow along on your laptop or make a copy of that for later. Thanks for coming, everyone. Amanda here told me we don't have a session right after, so if we run over a couple minutes for questions, that will be okay, too. This talk is titled, Drupal 8, where did the code go from info hook to plug-in? And the broad idea behind this talk is I wanted to really make Drupal 8 feel a little bit more accessible for people coming from Drupal 7, it might be trying to get their head around where some of the familiar pieces of code in Drupal 7 went in Drupal 8. This is also a good talk if you're just interested in the Drupal 8 plug-in system, but I'm gonna spend some time talking about a comparison between the code in Drupal 7 and Drupal 8. So if you've looked at Drupal 8, you might realize there's a lot of things that are gone. All these hooks, they're dead. Completely removed from the code base, you can't use them anymore. So what do we do? We replace them with plug-ins. So what I'm gonna talk about is really just give you a little bit of background about some, a couple key terms or changes in Drupal 8 just so you don't get lost when I keep talking about them over and over again. Spend a little while telling you what a plug-in is, which is, you know, an important thing that you get to wrap your head around the concept. Then I'm gonna dive into a first example. We're showing you where the code in a Drupal 7 info hook and corresponding implementation hooks went in a plug-in in Drupal 8. Then start showing you some code for a little custom module and how you can implement plug-ins in a custom module. So we'll start with putting a couple of tabs on a page as local tasks. Then get into a little more advanced examples where I'll show you how you can define a custom block, how you can define a custom text filter. And finally, a couple of best practices if you want your module to define its own plug-in type. So that's the roadmap for today's talk. This is me. Hopefully I look like that picture. I've been a Drupal core contributor since Drupal 5. I'm on the Drupal security team. I work for Biaraff now, which makes a web application for self-laboratory safety and compliance. And I helped create some of the Drupal 8 plug-ins that we're gonna be talking about. So in particular, I helped write the local tasks and a bunch of other menu-related plug-ins that we're gonna talk about. So we'll certainly be happy to take questions on any of those. And I also help organize Drupal Camp New Jersey. So if you're in that area, I look for it next winter. So this is the key Drupal 8 background. Some things that you need to know without which you'll be lost. So I'll keep referencing them the rest of the talk. So the first thing is the dependency injection container, sometimes called the DIC, more commonly just the container or the service container. And this is an object that's available in Drupal 8. And it just contains instances of other objects. And each of these objects is a service. And what that means is you can grab that service, have it do some work for you, and then let it go. And they're stateless. That means they don't change. You use them to do some unit of work. But the service itself doesn't retain any state. So it has things like, who's the current logged in user? How do I take a route and render it into a URL? Things like that. But when the service is done, the service itself has not changed. It's stateless. So that's important. We'll be talking about services a lot in terms of how you get and interact with plug-ins. The other thing I'm gonna mention quite a few times is the new routing system in Drupal 8. So the key conceptual difference really between Drupal 7 and Drupal 8 is that we now have route names instead of using system paths. So the route name is just a machine name. And that machine name is connected to a path and to call backs to provide the page content, the title, all the things that a Drupal 7 menu router would have given you. But again, we're using these machine names. And so there's not a direct one-to-one correlation between the machine name and a path necessarily. Finally, you'll see a lot of full namespace class names in this talk. So if you're not familiar with namespacing, this is a feature that came in PHP 5.3. So it's been around a little while now. And what that means is a PHP class name can look like that full string on the bottom. So it's a Drupal search plug-in block search block. And that entire string is the class name. But you see each of those backslashes separates a section of that class name as part of the namespace. And that gives you information about where that class lives and what it does. So you can see that obviously it's part of Drupal. It's provided by the search module. It's a plug-in. It's a block plug-in. And then we get to the specific actual plug-in that it is. So this namespace is kind of like a little map. And it's also really important for auto-loading, which is something I'll talk about later. OK, so that's all the background. Hopefully that's enough to get you through the rest of the talk. The big topic here is plug-ins. And plug-ins encapsulate reusable functionality in a class, and they implement one or more specific interfaces. And all three of those things are really important to wrap your head around for plug-ins. So they're reusable. We can have multiple plug-ins and keep using them over and over again. We're not constrained to use it only once. It's all class-based. And what's really important is they use interfaces. And interfaces are something that make Drupal 8 actually much more powerful and much more malleable than previous versions of Drupal. Because all these plug-ins, all the services I'm going to talk about in this talk use interfaces. What that means is if you don't like the way that the implementation works provided by Drupal Core, provided by some other module, you can substitute your own implementation as long as it follows the same interface. And the interface just means essentially what methods does the class have and what parameters they take and what kind of return value they give you. That's what the interface tells you. As long as your code implements the same interface, you can substitute your version for anything in Drupal Core or a contributed module. So imagine it's essentially like swapping out the module file in Drupal 7 of some module when you want it to work a little bit differently. That's what you can do in Drupal 8. And so that's an extremely powerful new feature of Drupal 8 that makes it more pluggable, more changeable, that I think a lot of people don't yet appreciate. So a little diversion there on interfaces. So if you think about what a plug-in does, again, it's a reusable functionality. And it combines in Drupal 7 what was generally an info hook and a number of implementation hooks. So if you implemented hook search info in Drupal 7, this is probably not too common to implement, but if you wanted a custom search, you're searching something that's not nodes or users, something custom, you would have to also implement in your module hook search execute. So there's the info hook that tells you that my module provides a search. Hook search execute actually does the work. And those two functions both had to be present, but they didn't necessarily have to be in the same place. They might not even be in the same file or next to each other. The documentation might not be clear if someone was reading your module that these two things were coupled together and essential that they both be present in order for your search system to work. Similarly, a lot of you probably implemented a custom block to implement hook block info in your module. That tells Drupal about the list of blocks that are available to be turned on. But of course, if you did that, you also had to implement hook block view, which actually returned the render array for that custom block. If you only implemented hook block info, it was no good. Drupal thought there was a block, and it had no way to render it. Similarly, you might implement hook block configure, hook block save, in order to capture configuration about your block. But these different functions might not have been in the same file. Again, collated might not be documented as being connected to each other in your module. So it was often hard to understand the code in Drupal 7 how these different hooks actually work together to make your custom block work. So if you dove pretty deep into Drupal 7, especially something like C-Tools and Views, you might have seen that those modules in Drupal 7 had a plug-in system. So what we have in Drupal 8, in a lot of ways, is kind of a grandchild, you might say, evolved from C-Tools and Views plug-ins in Drupal 7. But a lot of the mechanisms used to find those plug-ins and create instances are completely different from Drupal 7. So there's a connection there. But you really can't have a one-to-one, you can't just copy over a Drupal 7 plug-in and have it work in Drupal 8. OK. So how do I get an instance of a plug-in? How do I do something with it? So that's through plug-in managers. So every plug-in type, so like blocks, I have a plug-in manager that's registered as a service in that service container that I mentioned earlier as an important background. So you basically say, hey, service container, can I have the block manager? I'd like an instance of a block and the block plug-in manager will give you back the object that knows how to render that custom block. So that's how we get to those through a manager. Every plug-in has an ID, which may generally be in its derivative, just a static string. And we'll see a lot of those in this talk as examples. But we can also have derivatives. So a derivative, more commonly, would look like a static string, a colon, and then a UUID. So that's a very common pattern for derivatives. And so essentially, there's a base definition and then a lot of unique versions of that plug-in. And I'll come back to those later and give you a little more information about derivatives. But the thing you need to know is that for each plug-in ID, which might just be a simple string, that basically corresponds to a one-to-one mapping to a class. So I have a specific plug-in ID. Every time I ask the plug-in manager for a plug-in with that ID, it's going to give me an instance of a specific class. As I mentioned before, you can actually go behind the scenes and replace, substitute your preferred class for the class provided by a Drupal core module if you need to. But again, once you've done that, the one plug-in ID always maps to the same class. Finally, we want a plug-in instance. We want something that's actually working. And so we not only need to know the class, we need to be able to instantiate the class. And in general, a plug-in instance, the thing you actually use is a combination of that class, and then it is instantiated with specific configuration. And we'll get back to that a little more. But think about blocks. If you use blocks in Drupal 8, you know that you can take the same block, and you can place it as many times as you want in your site in different regions, in different themes. So every one of those is the same plug-in ID using the same class, but different configuration. So it's that combination of the plug-in ID slash class and the configuration that gives you an actual useful block instance. That configuration is often specified by a config entity. So just store that in your mind. We'll come back and talk about that a little bit more later. OK. So as I said, sort of an intro. This talk is a little bit designed to be reassuring for people coming from Drupal 7 that they can understand the code in Drupal 8. So you know the saying the drop is always moving means we kind of break things and rearrange things between versions of Drupal. But the message I have for you is that Drupal 8 and Drupal 7 still share sort of the fundamental DNA. And a lot of the code is actually line-to-line the same. It's just moved to a different place. So that's why the subtitle of the talk, Where Do The Code Go, this is intended to give you some sense of how the code moved from Drupal 7 and where it lives in Drupal 8 and how it works in parallel to how it worked in Drupal 7. OK. So the first info hook conversion I want to talk about is hook image toolkits. And you might be complaining that this does not have the word info in the hook name, sorry. But it is actually an info hook. Info hooks just generally returned you an array of metadata. And you see here the metadata has a top-level key of GD. So that's the image toolkit we want to use. We have a translated title. And then we have a key that's basically dynamically populated here with a method that says whether or not this image toolkit is available. So this is pretty simple info hook, right? If you've ever implemented something like that, you just return the metadata and that's it. So in Drupal 8, things end up working the same way, essentially, but they're organized very differently. And so in Drupal 8, this is a class that implements an image toolkit. And you see above the class definition is the special code comment, which is an annotation. You see that at image toolkit. And what Drupal 8 is actually doing is it parses these annotations out. So these annotations are not actually code comments in a pure sense. They're actually providing Drupal information about this class. And so they need to be formatted precisely. They need to use the right keys and values to tell Drupal what you want to do with this class. And so this at image toolkit annotation is parsed out. And the result of that is actually essentially the same as the info hook in Drupal 7. So what we get back at parsing this annotation is an array. And you'll see the top level key is this ID GD. That's exactly the same as the top level key that we used in the Drupal 7 return value from the info hook. We have, again, a translated title, which is, I think, the same string as it was in Drupal 7. So the result of finding the plugins, this process called plugin discovery, gives us an array of metadata that's essentially the same as the info hook return value in Drupal 7. And again, you see we have a method here is available. So this method is used, then, by the manager to do this sort of dynamic flagging that we did in Drupal 7. So the plugins themselves can't give you this metadata back. We actually need the manager. So I told you every plugin type has a plugin manager. Plugin manager has a method to pull back the list available plugins, this particular one get available toolkits. And it's a little different than other plugin managers, because it's going ahead and is running this method on each image toolkit. This is available method. So just like in Drupal 7, we were dynamically setting the property saying, is this toolkit actually available in the version of PHP I'm running? In Drupal 8, we're doing exactly the same thing. We're checking if the image toolkit is available. And if it's available, we include it in the list. If it's not available, we leave it out of the list. So hopefully that gives you a little sense. Again, these two things work almost exactly the same. It's just the way we get to that information is a little bit different. So let's dive in a little more. So image toolkits aren't very useful if you can't actually work on images. So you have operations that happen on images. So the image to saturate function is an example of this in Drupal 7. You see this function. We're passing in a standard class object. So that's a really boring object. It just has some properties we set on it. It doesn't have any methods. It doesn't tell us anything special about what it does. And how do we actually carry out a method on this image? We call image toolkit invoke. So we delegate the actual work to the current image toolkit. We're just passing the string saying desaturate. So here's the object. Here's the operation. Image toolkit, please handle this for us. Drupal 8 works actually in a very similar way. So we have the plugin, the image toolkit plugin that we need to call basically to have this desaturate operation happen. In Drupal 8, we've turned the relationship a little bit on its head. So instead of a standard class object, we have a proper object now. We have an image class. And the image class has methods, has predefined properties. And so the image object essentially knows how to call desaturate on itself. So it has a desaturate method. You see that desaturate method that works basically the same as Drupal 7. It says here's a string operation desaturate. And I'm going to just pass that off to the image toolkit that is currently active and ask the image toolkit to actually do the operation of desaturate. So again, Drupal 7 and Drupal 8 working almost exactly the same way. It's just the organization of the code. Here it's inside the image object. In Drupal 7, it was inside that image toolkit function. Finally, we go one level deeper and want to look at how this image toolkit actually did that work. In Drupal 7, for hooks and many things like these image toolkit operations, we had this process of building up a function name by prefixes. We'd say, hey, all right, so this is an image operation. So the first part of the function name must be image. It's the GD toolkit. So the second part of the function name must be GD. And the operations desaturate. So that must be the third part of the function name. And if that function exists, I'm going to call it. And nothing would go wrong. Did everyone ever have a case where you accidentally implemented a hook or something just by accidentally naming it? Yeah. That could be a little scary. So again, like Drupal doesn't actually know that this function is going to exist ahead of time. It just builds up the name and calls, hey, function exists. Great, call it. But you'll see inside there, it's pretty simple code. We're just checking if the PHP version we have supports this operation. If not, we log a warning or a notice and return. And then we call the built-in function on the resource and say convert it to grayscale. Drupal 8, if we go this one level deeper, each individual operation is also a plug-in. And as I talked about, your ability to substitute things in Drupal 8, if you didn't like the way this particular operation worked on images, you wanted it to behave differently. You could substitute a different class for this specific operation, and none of the rest of Drupal would be any of the wiser. We call this your preferred class instead of the built-in one that comes with core. So here you see this is, again, a plug-in. We have a different annotation. We have an image tool kit operation annotation, an ID. We're told it's associated with the G tool kit and that it's going to do the desaturate operation. But you'll see that inside there, the code is essentially identical to the Drupal 7 code. So executing this desaturate option, we are, again, checking if exactly the same built-in function exists. If it doesn't exist, we log a notice. And if it does exist, we go ahead and we perform the grayscale operation on the related image resource. So again, there's a one-to-one mapping between what happened in Drupal 7, what's happening in Drupal 8, where the code went from those hooks, from those functions named based on prefixes, into these plug-in classes. So code is still there. It still works the same way. It's still kind of Drupal, but it went into these class-based plug-ins instead of being in functions. OK, so you may be thinking, what about hooks? I remember hooks as being the most important thing in Drupal. And for good or ill, hooks still have their place in Drupal 8. In fact, there might even be more hooks in Drupal 8. And the reason for that is that every one of these plug-in managers basically implements an alter hook, or provides an alter hook, so that this is the way you and your module can go in and change what these plug-in managers are doing, which classes they're using for a given plug-in, or even which plug-ins are available in the system. So this is, if you're thinking about, how can I change the behavior of my site? I want to change the way one of these plug-ins operates. This alter hook would usually be the first place you'd want to look. And you could go in your module and just do a quick substitution, say, use my class instead of the built-in class from Drupal Core. So an example, hook block alter. This might be one you want to use. You're not quite happy with the way a core block works. You want to subclass it, change its behavior. You could go in and in hook block alter, say, use my modules block instead of Drupal Core's block. And that would happen. So there's also hooks that really weren't candidates for being converted to plug-ins. So there were info hooks that just returned a data array. And they didn't have any associated code. They didn't have that implementation hook. And so there was no reason that those would become plug-ins, because all they were was metadata. But you may find some of these hooks, probably most of them, got removed also. And what happened to those instead of making them plug-ins, we just turned them into static YAML files. If people aren't familiar with YAML, this is a fairly simple human-readable way to write a data file, basically to write out arrays into a file in a way that's safe for Drupal to parse it back in. So an example of this in Drupal 8 is hook permissions. If you remember, hook permissions from Drupal 7. Hook permissions just gave you back an array. Said, here's the permissions. Here's what I call them in the admin UI. Here's a flag that says if it's a dangerous admin permission, things like that. But there's no logic. There's no execute permission function. So in Drupal 8, permissions are actually stored in these YAML files associated with your module. So in Drupal just knows how to parse the YAML file into an array. So the end result is the same. In Drupal 7, you've got an array of permissions. In Drupal 8, you've got an array of permissions. It's just stored now in a file instead of being in PHP code. So there's a few other great hooks that we all love, like hook cron. That actually is still more or less the same in Drupal 8. Things like hook entity access. You might want to mess around with access controls. Some of these weren't converted because of lack of time, or some of them it's just still, it's such a Drupal thing to change entity access that there wasn't a clear mapping and just some more object oriented strategy there. OK, so I've mentioned plugin discovery already. Plugin discovery is how the plugin manager gets the metadata about your plugins. And I basically told you it's the same more or less in the end result as invoking an info hook. You get an array of metadata. It tells you about the available plugins and some things about their definitions. In fact, if you wanted to, you could actually implement a plugin type that had an info hook as this discovery mechanism. I wouldn't recommend this. Drupal Core doesn't do it except in a test to verify that it still works. But you can think about it again. The result of plugin discovery is just an array of metadata, and that gives you an array of plugin definitions. And that's just string keys and values. It's nothing exciting, but it just tells you the basic information about things like plugin IDs, which class to use, various things. The plugin process also fills in defaults, so every plugin wants to know it's provider, which is the module that's defining that specific plugin. And discovery can make derivatives. And so derivatives are a case of where you get many plugins, basically out of one plugin definition. And so let me explain that a little more and then I won't talk about it again. But an interesting example in Drupal Core is the field UI local task. So field UI local task is a plugin, and it wants to put a local task, which is a tab, usually in the UI, on every bundle of every entity, so that I can manage the fields associated with every bundle of every entity. So if you think about that right away, you say, well, I can't know in advance which entities are gonna be defined in my system and which bundles they're gonna be. So bundles are the same, basically, as node types in Drupal 7. So I have different entity types, they have different bundles like node types. For every single one of those, I need to define a tab that's in the admin interface associated with that entity and its bundle so I can manage the fields associated with that entity and bundle. So derivatives are the way we handle that case. So that plugin knows how to query Drupal, find the list of all entity types and all bundles, and dynamically make a derivative plugin associated with each one of those entity types and entity bundles, stick that tab on the associated admin page, allow you to manage the fields. So this is basically where derivatives are useful. You have something in your system that's dynamic, something like entity types, the bundles, the available languages, for example, and I wanna either define a tab or I wanna create some available behavior associated with each one of those dynamic things so I can have a derivative, I can query Drupal, find all those dynamic things and associate a plugin with each one of them. So that's it for derivatives. If people have questions at the end, we can talk more about them. Let me just give you a little overview of how plugin discovery works most commonly in Drupal Core. So we have two sort of broad categories, I would say, which is the YAML-based discovery and then annotation-based discovery and I showed you a couple examples already of annotations. YAML-based are for a bunch of these things that we pulled out of hook menu in Drupal 7. So we have the menu links, local tasks, local actions, contextual links. These are the things you most often use YAML-based discovery in Drupal 8, which means you would basically just author a YAML file in your modules directory and that would basically tell Drupal that there should be a plugin created with some specific definition. And we'll go into those and show you what a local task plugin implementation might look like. And the reason we do that is that basically all of these things, all the local tasks, more or less, or 95% of them, use the same class behind the scene. In contrast, most of the plugin systems in Drupal Core use annotation. So the couple of these, one I showed you already, the image toolkit, which uses annotation and maybe some configuration, but doesn't have a config entity. Then the vast, vast majority of plugins in Drupal 8 use a combination of an annotation-based plugin discovery system and a config entity in order to combine those things together and give you a specific plugin instance. So things like blocks, which I've referenced a bunch of times and we'll talk about more, views, all the views plugins, so like a views display, image effects, searches, if you wanna define a search. So those all use a combination of annotation and a config entity. Finally, just a little warning, if you wanna go look at the code and figure out how plugins work, the entity system in Drupal, more or less works like a plugin, it has annotations, but there's so many things layered on top of the entity system, you should not try to use the entity system to understand the plugin system. So start with a much simpler plugin system, even like the block system, which is a little bit complex, as much easier to understand than the entity system. So focus on one of these other ones if you wanna understand how plugins work. The entity system obviously is great for a lot of things, but it's just not an easy example to dive into first thing. Okay, so as I said at the beginning, we're gonna look at a little code. I'm gonna show you how you can implement in a module some of these plugins. So we're gonna start with the Drupal 8 plugin toolkit. So you need a module. You're gonna write your own module in Drupal 8. All you need to define a module in Drupal 8 is a .info.yml file, which is a .yml file. I said .yml is all over Drupal 8. So you don't even need an empty module file. In Drupal 7, you needed at least a .module file to exist in the file system. In Drupal 8, you don't even need that. You could just write this one .yml file and Drupal will recognize your module and allow you to enable it. The reason for that is that most of the module code is now in classes, it's not in the .module file. So it's perfectly fine for there not to be .module file because all you might have in there would be a few hooks anyway. The other thing that's fun to know about Drupal 8 is the code base was reorganized and if you ever developed a site and handed it over to someone in Drupal 7, you had to keep telling them over and over again, don't put modules in the modules directory. That would make sense, but don't do it. You have to put it in sites all modules. Something, right, Drupal 8 is different. Drupal 8, you can actually put all your modules directly in the modules directory. All the core modules now live under the core directory under core modules. So people, you don't see them, it's under the core directory and you can actually go ahead now and put all of your custom modules or you contributed modules under the top levels modules directory, it's a lot easier to find and manage them. So here's what basically all you would need to write to enable a custom module, have it appear in the UI and turn it on. All the code I'm showing you is in a sandbox. I'll give you a link at the end if you want to clone that sandbox and look at the code yourself. Turn it on in a Drupal 8 site, play with it. So the .info.yml file contents looks basically the same as a .info file in Drupal 7. It tells you the name, tells you this is a module. Options there are basically module, profile and theme for the type, description and of course that it's a Drupal 8 module. So once we've done that, we can start adding some plugins to our module. So I'm gonna add two local tasks. Local tasks, tabs, I don't know what term people prefer. So in order to that, all I have to do is write a YAML file. I just have to follow this naming pattern, mymodule, links.tas.yml. In that file, it's a very simple format. The top level key is the plugin ID. So I talked about plugin IDs, a plugin ID is gonna map to a class that's gonna provide the functionality. Route name, so I told you route name is a machine name as basically tells you how this tab is gonna have a link rendered to appear on the tab. So the link is gonna link to that route name wherever that goes. The title and then a base route. And so this is new in Drupal 8. When we rewrote this local task system, I was really sick of this default local task thing that you used to have to do in Drupal 7. Jump through hoops to make two tabs appear together. So in Drupal 8, basically this is all you have to do. You just tell it, okay, this base route is sort of the grouping principle. All my tabs are gonna appear together there. And the one, the first one you see, that route is the route that it's linked to. So that's gonna be the default tab. So the default tab is the one that links to the base route. All the other tabs are gonna be the secondary tabs associated with it. So hopefully this makes for a little easier developer experience, easier to wrap your head around. So I put this on my module, I turn it on. You may have to clear cache if you write a new file, of course, because the plugin managers cache the definitions they've found before. But once I do that, I get two tabs appearing on the page. You'll see the right tab, the right hand screenshot is the default tab, the list tab. And that has path admin config my module list. The screenshot behind on the left is the secondary tab has path admin config also my module settings. So what have I done here? If you remember we were using route names, route names use our machine names. So Drupal no longer cares about the two paths. I can put these two tabs right next to each other in the admin page, even though the paths are not hierarchically related in any way. So this is a cute little feature of Drupal 8, hopefully makes it easier to build admin interfaces because you no longer have to jump through hoops aligning the paths where your pages appear in order to have a group of tabs related to functionality. Okay, and you see I printed some output on these two pages and now you may be wondering wait a minute, I think he just skipped a step because he pointed to some routes and he didn't tell us about the routes. So yes, all right, I skipped a step. So we back up, we also need to define the two routes. So these are custom routes in the module. Again, if you pull down the code, you can look at these. So the most common way in Drupal 8 to define routes is again a YAML file. So again, if you're not familiar with YAML files please read up on them. So this looks very similar to the plugin definitions but it's a routing definition and most of the keys in here are actually taken from the symphony routing system. So you can actually find, discover most of the things that are going on here by looking at the symphony documentation. And again, it's pretty simple. The top level key is the route name. You see module.list or module.settings. Those are the two route names. There's a path. Where does this route actually go? What path is connected to this route? And then the controller, which is essentially the page callback. And these two controllers I defined are stupidly simple. I'm not even gonna show you the code. It basically just prints out the class name and method. It's in the example code. A title and then access check, which is just true. So anyone can access these two pages. So this is how you define a route in your module. And then when we have these routes defined again then we can use them for things like our local tasks linked to them or we can use them in the block. You'll see later linking to them from our block. Okay, great. So hopefully that's, moving us forward, but you may be wondering, okay, I saw you wrote those two YAML files to define the plugins, but how the hell do I know what valid keys can go in those YAML files and what do they mean? And so that's something you have to dig in a little bit and we've put that information on in the plugin managers. So the YAML files aren't really self-documenting the way that you'll see that class annotations can be. But in the plugin manager you see here is the list of all the valid keys if you're defining a local task or a tab. So some of them you already saw like the route name, the route parameters goes along with the route name and that's things like a node ID. So if you wanted to link to a specific node that would be a route parameter. Title, the base route I told you about is where they're grouped. A parent ID allows you to have a hierarchy of tasks of tabs, so if you remember making secondary tabs based again on a convoluted system of paths and previous versions Drupal. Now all you have to say is this particular plugin I want to be underneath it and that works with that parent ID, the weight, the options for rendering the URL and again the class. So if you want to have a specific class rather than the default class you can provide that in your plugin definition. Okay. So that was a pretty simple example in a way. All I had to do was essentially define some routes or use some existing routes and then write a YAML file to get two tabs to appear which are basically implementing plugins in my module. So now we're gonna go into blocks and this is I think a really common use case. One of the first things people might do in a custom module say I wanna block, exposes a widget, it gives me a list of something on the site custom and hopefully you will like the new block in Drupal 8 compared to Drupal 7. Again blocks are plugins now. So every custom block is its own class. It's completely encapsulated. It lives separate from every other block. When you as the administrator go to the back end and I'll show you some screenshots of this to place a block, so you're placing a block in a region in a theme with a specific weight, a configuration object is created to track that setting. So now we know there's a specific instance of this block in a specific region in a specific theme. That config object that's created is a config entity which I've mentioned several times now and we'll also pause to actually tell you what that is. So config entity is an abstraction on top of the configuration management initiative. So people remember the configuration management initiative was a big thing in Drupal 8 and that basically lets you export or import the configuration of your Drupal site as YAML files. Again YAML, so we'll also view them as YAML files. So what this config entity abstraction does is let you treat essentially that data object that's represented as a YAML file as an entity. So something that essentially shares a bunch of methods with things like nodes. And that means it's very easy for Drupal now to have an API that allows you to easily find, list, and load these configuration objects using the same basic API as you would to find, list, or load something like nodes. So that's why this abstraction is useful. So now Drupal can more easily just say, hey, I wanna do a query and find all the list of active block instances corresponding to which are basically these configuration entities. And so that just works now much more naturally in the API. In the example I'll show you, if you're writing just a basic block, you don't have to worry about this. So we're gonna extend a base class that Drupal 8 gives us. Drupal 8's already gonna take care of all the work of saving this configuration entity for us if we use that base class. So you don't have to worry about it all in the example I'm gonna show you. But in the back of your mind, if you wanna add your own specific configuration for your block, you're gonna have to add a little bit more code to add those settings to the configuration entity that's saved along with your block. Okay, so, how do we implement a block? We're gonna implement the Drupal core block plugin interface. Remember I told you about interfaces. Interfaces are very important because it lets us substitute or reuse code. So any block has to implement the block interface, block plugin interface, and any block essentially could be substituted for a different block as long as you're both implementing this interface. If you extend this abstract class that Drupal core provides, the Drupal core block block base class, all you need to do is implement one method, which is the build method, which is the same as hook block view in Drupal 7. So hook block view, you return a render array. Drupal 8, the build method, return a render array, that will be rendered for your block, you're done. It's very simple to implement a custom block in Drupal 8. So for this example module I added, Drupal my module, plugin block, my block. And a quick side note, people may be wondering again why this name space is so important, why I need to use this very long naming for classes, and that's because of the way that auto-loading works. So we're using the PSR4 auto-loading standard, and what that means is that when I add, when I want to add a block with this kind of name to my module, there's a one-to-one mapping of where that lives in terms of the file system. So it's at the path under modules, my module, SRC, then plugin block, my block.php. And if you look at that, you can see very clearly the mapping, of course this is Drupal, it's a module, it's under my module, so that's the first two parts of the name space, then plugin block, my block, are the last three parts of the name space. If you basically take those backslashes, you flip them to forward slashes, that defines the file path under that SRC directory. Right, so I'm gonna create a class in a specific name space, I have to create the corresponding directory under my module, under its SRC directory, and put the class file there. And then auto-loading works like magic, the auto-loader knows how to scan the file system, if I ask for a class of that name, it knows exactly which directory to find it in. Okay, so what does a block plugin class look like? This is basically, I cut a couple extraneous lines, but this is essentially all there is to it. If you wanna write a custom block, it can be this simple. You see this is annotated again, it's an at block annotation, it tells you the plugin ID and gives you a label that's gonna show up in the administrative interface, which I will show you in a second, a screenshot of, and then it just returns a render array. The render array has two links, they link to those two routes I defined in my module. So bringing it together here, so I defined the routes, defined a block, the block has two links that point to the routes, which have those two tabs. So hopefully, if you're trying to get started, that example code might be useful. So if I define this class, maybe clear the caches so that the plugin manager finds it, then I can create an instance of this block. If you haven't used the Drupal 8 blocks, I haven't paid yet, it's got a new functionality called place block. If you remember Drupal 7, it told you available blocks. In Drupal 8, we're gonna place a new block, and we can do that as many times as we want with the same block. So we can have as many instances that we want of the same block. So click place block, I do a search here, I find my module block, that was the string that was in the annotation as the admin label. Click place block again, and then I get a configuration page. It looks pretty similar to a Drupal 7 block configuration page, and if I click here, I've gone ahead and created a new plugin instance. I've created an instance of that block. So there's a configuration saying this block exists in this region, in this theme, with these settings, and so when I now go to my front page, that block will be rendered. You see there on the left, it's rendered, has two links that came from that render array in the build method, and it shows up on the page. So that's all there is to it. Block plugins, again, conceptually doing the same things as Drupal 7, but that code has all moved into the block plugin class instead of being present in hooks. And just to make that a little more clear, let's just do, again, a one-to-one comparisons. You can see how this code is moving from Drupal 7 to Drupal 8. At Drupal 7, we had hook block info, right, and that just returned an array, listing the blocks your module provided. In Drupal 8, that's handled by the block plugin manager. Block plugin manager knows how to get all the metadata from the annotations, and basically builds up an array of all the available block plugins. Hook block view. In Drupal 7, you had to give it a delta, right, because that one hook had to handle every block from your module. In Drupal 8, every block is its own individual class. It has its own specific build method on that class. So basically, we moved from having to know the delta to simply knowing which class corresponds to my block. In Drupal 7, there was really no good way to control access to your blocks. So you had to hack something maybe into the view hook. Drupal 8, if you want to, there's a specific method on every block class that allows you to control access to that block. Similarly, in Drupal 7, the way we handle configuration was a little bit like one-off. So there was hook block configure and hook block save, which again, you had to pass in a delta. Hook block save had a array, kind of a form values, an edit array, but it wasn't a standard form API thing. Instead, in Drupal 8, we have a very standard form API workflow. So we have the method that defines the form, the method that validates the form, the method that submits the form. So if you've used form API in Drupal 8 for anything, this flow here for a block plugin works exactly the same as every other form in Drupal 8. So I think that's nice. We've got more consistency now in how we're using our APIs. We've made it easier to control access and we made it easier here to validate the configuration for your block. We actually have a validate step, which we didn't have in Drupal 7. Okay, so that's the mapping there. So block discovery and annotations. Each plugin type has to be in the expected namespace. So this is partly how the block is found and then it has to use the right annotation. All the core plugins have a custom annotation class so that at block, annotation actually corresponds to a class I'll show you. The annotation class provides that documentation about what things are allowed to be in that definition and provides some default values. And then there's a generic one that you can use basically as a starting point to extend. So this is what the block annotation class looks like. It extends that plugin annotation and again tells you things like the ID and the admin label are valid keys to put in the annotation, right? So that's what these mean. These variables here are the valid keys in the annotation. Okay, so last example is filtering text. We had an info hook in Drupal 7, hook filter info, which would tell you about text filters and how Drupal could, you could build up a text format that would apply different filters to your text. This example is taken from project module, not from core. And if you use Drupal.org ever on an issue, you've seen people put issue number in brackets with a hash sign that links to the issue, right? That's what this text filter does. You see in the info hook, we have the title, the description, and then we have, we're defining callbacks. So the process callback, the tips callback, and then we say whether or not this thing is cacheable. So that's the metadata we get out of the info hook in Drupal 7. We're told, again, the title description and then two callbacks and cacheability. In Drupal 8, it works a little bit differently. So we have, again, a plugin. If I would, so I'm basically implementing, re-implementing the project module's functionality in a slightly simpler form in Drupal 8 in this example module. We have an at filter plugin with an ID and a title. And then instead of those two callbacks that were functions in Drupal 7, we have methods on the plugin. So we have a process method and a tips method. And those do basically exactly what you think. We get some text passed in to the process method. We process it and then we return it. So we return it wrapped in this other object called a filter process result. And that's basically serving the same role as that cacheability flag in Drupal 7. So this process result object carries along with it some information about how cacheable this filtered result is. So it might only be cacheable for the current user or it might be cacheable for everyone. In this case it's just a link to a node. It's the same for everyone. I don't have to provide any special extra cacheability data. But in other cases you would wanna say this is only valid for the current user. And then the tips method of course just returns the filter tips that you see below the text edit area. So if I define this class, again, show you how that works. So let's say I create a nice first node on my site, node 2058. Now I want to enable this new filter plugin. So I go to my text filters in the configure section, edit, scroll down. I see this title I define in my plugin, format and node ID as a link. Click that off and save it. And now if I go and edit my second node, I can basically use this special format, these brackets, hash sign, node ID to create a link back to my first node. And you'll see that in the tips below we're getting just the return value of that tips method showing up in my filter tips. So great, if I save this, it works. I get the node title, I get a link back to my first node, which you can see if I hover over. And that's it. So now this little plugin that just had a, a couple methods to implement is able to provide me new filtering functionality for my Drupal site. And I can basically transform text from that sort of token format into an actual link with the title of the node that I care about. Okay, so hopefully that was all informative. A brief pause here for best practices. If you're, want to define a plugin type for your own module. So this means, for example, you defined an info hook in a module or you defined like a C tools plugin in a Drupal 7 module. Define it means you actually called like module invoke all to get the results of all other modules info hooks. So that's something you might convert to a plugin if you upgrade your module to Drupal 8. My strong advice to you is to use annotation-based discovery by default because it keeps that metadata, that annotation together with the class that's actually doing the work. So you have those two things in the same file, you can basically look at them together and make sure that you understand that they match up. The YAML discovery as I mentioned before is really only good for a few edge cases where the class implementing the plugins is almost always the same. So if 95% of the time, the class is gonna be the same no matter what module is implementing the plugin, you might wanna use YAML-based discovery. An example of where you might provide a specific class in Drupal 8 is let's say you want a tab in the page that shows the current user's name. So it has to be dynamic, it has to be specific for the current user, it has to pull in some other information, it's not a fixed string. So that's where you might provide that class name in the YAML file and tell Drupal, I don't wanna use the default class, I wanna use a specific one that has a different method for the title, but most of the time you're not gonna do that, you're just gonna have a fixed string for the title, a fixed link to a route and you're done. Okay, so here's some links towards the end of the presentation. So the sandbox where the code is, handbook pages on converting your modules if you haven't done that already, another handbook page on the plugin system and one that I wanna highlight specifically if you wanna dive in a little more about the philosophy of why we adopted this plugin system so broadly in Drupal 8 and finally a blog post from DrupalizeMe that covers a lot of the same things as talk does, but from, of course, a slightly different perspective, so you might find it helpful to compare notes from that blog post to what I was telling you today. So let's sort of move to wrapping up and I wanna just again give you a little excitement about using Drupal 8s, it has so many great features. So widespread uses of interfaces, everything in Drupal 8 pretty much has an interface, all the plugins, all the plugin managers, almost all the services use an interface and that means that you can replace them. You can say I wanna use my class instead of the one that was provided by core, as long as you implement the right interfaces, the rest of Drupal doesn't know that you've substituted, you've tricked it and given it your class instead of the one that it was expecting. In terms of building admin interfaces, you can now group tabs together regardless of what path they're referencing and that's partly because it's the route name that's the unique thing and not the system path. So we care about those machine names, we don't care about the path. What's also interesting about that is I can have multiple routes that are possibly serving the same path and why might you wanna do that? So imagine you wanna render a node either as HTML or as JSON. Well, it might be at the same path and I can then append a query string and get a different format and the query string basically might let Drupal choose between two different routes. So both these routes say I wanna serve the node content but in one case, if the query string is present, I'm gonna go to the route that gives you JSON. In the other case, I'm gonna give you HTML. Another example is get and post. So if I do a get request on this particular path, it goes to one route. If I do a post request, it goes to a different route. So that's really nice. It allows you to segregate out this functionality for different cases. That's something that wasn't easy to do in Drupal 7. Again, YAML, I've mentioned YAML, YAML, YAML. So if you're not familiar with YAML as a standard for those configuration data files, please take a look at it. Great thing about Drupal 8 is we have multiple instances of the same plugin. The key feature of plugins is reusability. So we can have as many instances of the same block as we want and reuse them anywhere you want on your site. And the way we do that is basically that each block instance has a corresponding config entity that captures that key configuration necessary to instantiate that one block instance. Okay, so to sum it up, plugins combine the discovery-available functionality with the implementation. In Drupal 7, that was really the combination of an info hook and multiple other implementation hooks and possibly spread across different files. So in Drupal 7, they were spread out. You weren't always sure where to look. In Drupal 8, they're actually nicely grouped together that metadata and the annotation and all the implementation is all in that one class file. If you're gonna define your own plugin type, please do use annotation unless you have a really clear reason to do something else. So that's pretty much it. I'll get to questions in a second. If you haven't been to Drupal Con Sprint before, strongly encourage you to stay for Friday. It's a lot of fun. So there's a half day in the morning of a first-time Sprinter workshop. There are people there that will help you set up whether you wanna do documentation or code. If you wanna do code, they'll help you get your local development environment set up to edit Drupal. So a great thing to do there and then all day there's the metric course sprint and the general sprint. So general sprint, you'll find people working in different contributed modules, documentation, marketing initiatives, all kinds of different things. And the metric course sprint, is people who specifically wanna work on Drupal core issues. And an exciting part of that is at the end of the day, they do one or more live commits of changes people made that day, issues that got all the way from start, not necessarily from start, but got all the way to finish in that day and get committed to Drupal core right in front of your eyes. So that's it. Please tell me what you think. Please go to the session node 21.025 on the site and please fill out the evaluations. That really helps me and helps the conference organizers know whether you appreciated this content, whether you got something useful out of it. And please also take the Drupal con and Nashville survey to tell them about the overall event. With that, I would be happy to take your questions. So if your questions, please come to the mic. So we get on the recording. Yeah, you mentioned that you could load a config entity, correct? Is there a way to alter it? Is there anything that would help you to change it on the fly? So there is a system, which I'm lucky on now, which you can dynamically alter configuration when it's loaded, yes. Great, okay, thank you. Yep. Hi, you talked about plugins, being able to use your own plugin to replace another one. Yeah. Is there a way to ensure which plugin, if there are multiple ones in the system, which one wins out and is actually being used? So which one wins out? I mean, that's really the same problem as like hook ordering in Drupal 7. So there's not a guaranteed way other than you can make sure that your alter hook is the last one to go, if you're worried about that. How would you make it sure that it's the last one to go? So Drupal 8 provides a hook module implements alter hook, which allows you to alter the order of in which module hooks are called. Okay. There's maybe still a module await, but I would recommend that hook is probably the preferred way to do it. Hi, great talk. Thanks. But first off, wherever you're working with QWorkers, is there a guaranteed way to make sure that the process item method is called? With QWorkers. Yeah, supposedly it's supposed to replace cron and the job that I'm trying to run is too big for cron to handle. So I've been looking at the QWorkers and I can make everything work, but I have to manually call process item in the constructor, which I know is just wrong. So I'm trying to figure out a way to make it. Yeah, we could talk about that though. So you might, so it doesn't really replace cron often what happens is, I don't know if you define the cron key in the annotation for your QWorker? Yeah, I did. You did, okay. So that should get called on cron then. I mean, that's, I've had it work, but yeah, generally you just break up those jobs into smaller chunks and put them in the queue and when cron runs it should call them. Well, do I have to implement hook cron for that to ever fire? Your module should not have to implement hook cron as long as you define that key in the annotation QWorker. Okay, well I'll dig into it some more. Thank you though. Yeah, sure. That's full of great talk. My question is regarding YML files. Okay. Sometimes you have to basically, I was doing a content import. So I have basically few different environments. So for if it's a Dev environment, get it from here. If it's a staging environment, get it from this URL. So how you can change the URL in YML depending on your environment dynamically? So did you go to Dries keynote today? Okay, so that was one of the things he highlighted as a problem is altering those. So there's a few different strategies. I mean, so one thing is you can, I mean, Drupal does ship with a system for altering config dynamically and that's for example how translations of configuration happen. Is that dynamic altering? But that's a little bit tricky. There are a couple modules that will let you basically substitute in different pieces of configuration. It's, I would say it's not a well solved problem. Unfortunately, which is why it was in the keynote. Okay, so it is a problem. It is a problem. Yeah, so substituting those values in, yeah, there's different environments. Kind of figured out I couldn't find anything on. Yeah, so I mean, you can override config values and settings PHP. So that's how we've been doing it. That's the only way for basically write some script which will when on Acquia post deploy hooks to directly alter your config. Yeah, I mean, there are, again, there are hooks that will allow you to swap things out and config at runtime. So it is possible to do in a module, but it's, yeah, it's not beautiful at this point. Great, thank you. Yep. Great talk by the way. Thanks. After you showed your routing YAML file, you showed the defaults property to show what properties you could use in that YAML file. Right, that was for the local task plugins. Right, okay. Now is there a way to see the sub properties, like the ones that begin with the underscore, like an easy way to find those? Oh, so those, that's the routing definition. So that's where I'd say look at the, starting point would be to look at the symphony documentation. I mean, I think there's some Drupal algorithms also, but if you start basically the symphony ones, most of those are fairly standard. Drupal has kind of repurposed a couple of them. So things like where a route is a form instead of just a controller, it uses like underscore form, things like that. All right, so a lot of those come from symphony now. But a lot of them come from symphony. So that's kind of, if you just want to get an overview of what goes in there, I'd start with the symphony docs. Great, thanks a lot. Hi, thank you for the talk. I had a question about the annotations. You said you could locate the definitions of the annotations. Where does that live? So those are classes themselves, so they're annotation classes. And the class name is basically the same as that annotation. So at block basically says there's a block annotation class. That's actually the name of the class. So you basically just look for a class name block that was under the annotation namespace of, in this case, Drupal core or under some module. Okay, and then another question on the routing. In symphony console, there was a command that you can show your routes, router debug controllers. Is there something corresponding in Drupal 8 so you can tie routes to controllers? So the Drupal console does have basically an equivalent command. So we'll list all the routes and then you can dive into specific routes to see the controllers. So the Drupal console, which basically wraps symphony console and some other things. Ah, okay, all right, thank you. Yep, you're welcome. Okay, any other questions? If not, thank you. And again, please fill out the evaluations. Maybe each other, about here, maybe you remember the search module and bypassing something, you know, the permission, you know the permission, if you remember. I remember your name, I don't remember the specific question then.