 Let's talk about Drupal 8. That's what we're doing today. So who's got this kind of attitude? Wow, nobody? This is gonna be really easy. Okay, a few people, yeah. It sounds like it's gonna be so different than what we're used to. How am I ever gonna learn that? But from everyone else, this is what I'm hearing. Finally, not weird. Does this mean I can actually learn it? Finally, can we actually get non-Drupalers working on Drupal? And I'm happy to say that the answers to both of these questions is yes, and I'll take care of the first half of this today. The key thing is that the more things change, the more they stay the same. Quite simply, the concepts are still Drupal. The idea of nodes, of views, of fields, of pages, of blocks are all still there. A lot of the code for it has changed, but the concepts that make Drupal Drupal are all still there. There's only an hour-long talk and we're starting a little bit late, so I'm gonna be focusing on broad strokes. Don't try and look at specific API syntax I show you. Please focus on the concepts and themes behind them. You can look up the APIs later. That's what documentation is for. The goal here is to get a feel for the modernized architecture and see the consistency in the APIs so that when you then go to learn things yourself, Drupal 8 makes sense to you and it'll flow naturally. So let's start with just enough theory. This is how any PHP 4 application was built. In the back, it might be a little hard to see. You start off PHP boots, you've got a bunch of superglobals and you have your code that does stuff and stuff is a fan theory of saying I'm gonna print a bunch of strings and call the header function to generate HTTP headers and hope I do it in the right order, otherwise PHP is gonna break. And that's how PHP 4 applications are written and that's how Drupal is written despite its version requirements is fundamentally a PHP 4-based system. Whereas HTTP, as in the web, actually works like this. A request message comes in, your server does black magic of some kind and a response goes out. These requests and response are messages. They are structured objects. They are not just blob of string. There's a very clear pattern to them. And that's the model used by the symphony HTTP kernel components, which is now the core of Drupal 8 as well as about two dozen other systems where you've got a request object that you never touch superglobals again in Drupal 8. If you use a superglobal in Drupal 8, you're doing it wrong. Instead, we've got this request object that comes in that represents the incoming request and then we have an object called a kernel. HTTP kernel that does black magic and outputs a response that gets sent back to the browser or to the client application or whatever. And everything Drupal does is inside here. That HTTP kernel interface is very, very simple. This is the entirety of that interface. It's a single class with one method that takes a request and returns a response. And a few other things we can ignore for right now. That's it. So we're done. Go home. Okay, not quite. You can have any implementation of this interface you want and, in fact, as of about three weeks ago, you can stack them in Drupal 8, which is awesome. The main one we're using that is that the main core pipeline of Drupal is the symphony stock HTTP kernel implementation, which, again, might be a little hard to read here. If you've got a request, comes in. The first thing that happens is a request event is fired. An event is basically the symphony version of a hook or, rather, hooks are an event system written for PHP 4. So you can kind of think of the request event as hook request alter. Same kind of idea. And various things happen there. Most importantly, routing and access control. And we'll cover those a little bit. And the job of routing is to determine a controller. We used to call those page callbacks. Now we call them controllers. Same idea. Then there's a controller event that fires, which hook controller alter, essentially, which, in practice, I doubt you'll ever use. I mentioned it for completeness, but in practice, it can be extremely rare for contrib developers to have to use that hook or that event. And then your controller fires. The controller is the callable thing that is responsible for this request. There's a standard terminology from other web architectures. If you feel like talking in MVC terms, it's the C from that, but don't talk in MVC terms because that's wrong on the web. But that's another story. Your controller can return a response. And then you're done. You have complete control over what's coming back. Or it can return something that is not a response. Then a view event fires, which is things registered like a hook that will take whatever that is and try and turn it into a response. So if you return a string, you return a render array, you return some object of some type, view listeners catch those and can render them up. And that's where all of the page rendering happens. Either way, then get a response event. Think hook response alter. This is where we can add cash tags to headers to the response. This is where we can do a lot of other black magic there, take care of standardizing response codes and that sort of thing. Then the response gets sent. And then we have a terminate event. Hook exit, essentially. So conceptually, it's not that different than what we've been doing. It's just structured in a much cleaner fashion. Instead of a whole bunch of steps that are hard coded together, it's a framework into which we can put steps. And those steps are very similar to what we've been doing before. It's just a much neater organization for them. So controllers, I mentioned that a couple of times. We used to call these things page callbacks. In Drupal 7 and earlier, we had a function that was responsible for the body of a page. Now we have any callable thing. Callable in PHP is basically a thing you can stick parentheses after and PHP won't complain. So a function, a method of an object, a static method of an object, an anonymous function, and so on. In practice, in Drupal 8, you will 99% of the time be using a method of an object. So here's an example. Controllers do not need to get any more complex than this. The method here is the controller. Again, the method is the controller, not the class. And we return a new response object. And we give it the body that we want to turn. Which is the entire HTML page. Who's run into the problem in Drupal 7 where to take control of the page, you have to print something yourself and then call exit or return null and the system somehow magically knows that it's not supposed to actually then try and theme... No, forget that. If you return the entire return of everything, you return a response object yourself, bypass the entire theme system, bypass everything. You're in control. You will rarely do that, but you can. You can also... There's various other response objects you can send. They're just subclasses. Like in this case, we're returning a JSON response. So we build up an array that is something we want converted to JSON. Doss it into a JSON response and that gets sent. It gets converted to JSON. The correct HTTP headers are set so that the content type of the response is set to JSON instead of HTML. All that stuff gets taken care of for you. And again, note, two methods in the same class, separate controllers, totally cool. We can get a bit fancier with it. So let's say, in this case, we have... you're gonna get a bunch of data out of our system, database query, whatever. And we want to create a streamed response. A streamed response is something that will send data incrementally. So rather than, say... In this case, we want to send a CSV file back. We could take 10,000 records out of the database and build up a gigantic string that is their entire CSV file and pass this gigantic string around and chew up tons and tons of memory and finally print it. Or we could say, on this streamed response, it's gonna be a CSV file. And we set our callback that will get run when we send the response. It's an anonymous function. We have access to that data and just print out the data for that CSV one row at a time. This way, we still have access to the view event, the response event. All the caching still happens correctly. Drupal still takes care of all of the HTTP handling for you, but you can stream out two gigabytes worth of data without having two gigabytes worth of memory usage. Anyone else think that's cool? Someone does, okay. So you can do a lot of things like this for more esoteric cases that used to be crazy hard, now they're crazy easy. You can, of course, just return a string. If you return a string, then that is the body of the page. Thank you, goodbye. Go home. Just like returning a string from a page callback. What you're most likely to return, however, in the majority case, is a render array. We have not completely destroyed render arrays in Drupal 8. I apologize. They're still around, but hopefully you can use them much more simply. So in this case, we return a render array that has a theme key and whatever the other values are going to be for it. We'll cover this in a bit more detail in a bit. And that render array will get rendered and there's your body of the page. This is what you'll do most often. And most often, your controllers will not be much more complicated than this. Your controllers should not have significant logic in them. You can also pass parameters in. In Drupal 7, in hook menu, you would specify, you know, percent, percent, percent, percent node, whatever. And you have to match things up in order. In Drupal 8, we're doing things the symphony way, which is we have our controller, which has these two parameters, and the request. So the request will just get magically passed in to us. We'll have access to the request object if we want it. If we don't need it, it won't get passed. Magic. And then we just return, you know, a render array for some template we define with those values. And then we hook it up to a routing system, which replaces hook menu, by saying, all right, we've got this route, which is mapping from incoming request to controller. We're going to call it Hello World. We're going to say it's at the path Hello World curly braces from curly braces to. This is actually an IETF standard for doing URI templates. So this is not a symphonyism. This is an internetism. And you can specify, you know, the content callable is that method of that class. You need the access content permission to get to it. And these have to be strings. You can restrict them with regular expressions. Say these have to be strings. Now, one thing to note here. I've got the order here as from to, and here is to from. Does that matter? Is that a bug? No. It matches on name. You can put your parameters in whatever order you want. You line up the names, which makes the code a lot more self-documenting. All right. Larry, stop talking. Tell me something useful. No one's going to laugh at that? I thought you were going to be thinking that by now. Anything you do in Drupal, or most things you do in Drupal, are going to come down to a two-step process. Build some kind of tool and then wire it into the system. You don't solve a problem directly. You build a tool that will solve it and then wire it into the system. You connect it in. In practice, that generally means you're going to implement an interface and tell Drupal about it. That's it. That's everything you do. Is that. Create a class, extend a base class, and tell Drupal about it. So how do we start off? How do we define a module? Well, use to have an info file. Now we've got an info YAML file. YAML is a widely used text format. So it's no longer proprietary Drupalism. This is a standard format used by hundreds of projects. The information in it is pretty much the same. We have a module called Hugs because this is me we're talking about. We have a description for it. We have to specify the subtype module. That's really the only thing that's changed. Specify its core version. There's various other properties here, exactly the same as in Drupal 7. There's short talks. I won't go into those in detail. And that's it. It now shows up on the modules page, which means for the first time in Drupal history, the .module file is not required. And now we have a module. Great. Four lines of YAML and we have a module. Done. So how do we make a page? Well, as we said, extend the base class, wire it up. So let's start by creating a controller. We're going to create a Hugs controller here. It's going to go in our module slash source, SRC, controller, Hugs controller. That's the name of our class, which maps one-to-ones the namespace of it. Drupal, our module name, controller, and the classical Hugs controller. This is using the PSR4 auto-loading standard, which means this class will magically become available to PHP the interpreter when it needs to, and you never have to think about when you need to call require or include again. It's all just taken care of for you. Just put things in the right place. We're going to extend this base class, which gives us some nice utility methods, and we have our controller. It's going to take two parameters, two and from, and we're going to return a translated string. That says, from sends Hugs to, to. Great. Notice we're not calling the T function. We're calling a T method, which is a method on this controller base class. It's exactly the same way, but because it's a method, it's abstracting out some logic behind the scenes to wire things together, which makes code testable. And then we tell Drupal about it. We have a routing file. We say, all right, at the path, hug slash from to, we want that controller we just defined. We want the page to have a title of hug, and you need to have permission to access content. You can't get hugged unless you can access content. And boom. Done. We have our site slash hug slash Larry slash Drupalcon, and it says, Larry sends hugs to Drupalcon. Is there a Drupalcon here I can hug? Okay. Done. We've just created our first page. One class, one YAML file. Create a class, tell Drupal about it. Of course, we want to make it themeable because we don't want Morton DK angry at us, at least I don't. So hooks are still there, but they're far less used. So we have got hook theme. We're going to define a theme item. And it works essentially the same way as it did before. We're going to create a theme key called hug page. It has two parameters to it, from and to. And if not specified, they just get null as value. And the template file is hug page. And so we stick this in our .module file. Okay, fine, whatever. The template, however, looks like this. So we're putting it into a template directory for organization. And it's called hug page.html.twig. Who's heard about twig? Anyone who doesn't have your hand up, there's plenty of twig sessions for this conference, please go to them. This is another, it's a third party template language used by a number of PHP projects. It's written by the same lead developer as symphony itself. And you can do string translation in it natively. It's awesome. So we're going to make this a section tag. And we're going to make this string translatable, just as if we had called t on it. And we just plug in our parameters. And then change our controller to say theme hug page from two. We're just returning a render array. This is it. All of the display logic happens at the display layer. Where it belongs. Where your themeer can modify it without coming back to harass you, the module developer. And everyone is happier. And we had a strong and an italics. We've got a strong and an italics. Great. The themeer wants to change it, change the markup, add classes, do whatever. They don't have to override the template. They're good. They never have to talk to you again. I like talking to my themeers, but they don't always like talking to me. All right. Forms. How do we make a configuration form? What do we say? Extend a class, tell Drupal about it. We first have to define our configuration. This is the new configuration management system. In Drupal 7 there was no configuration system. We had variable get and variable set and variable set. In Drupal 7 there was no actual configuration system. Sometimes people use them as such. But it wasn't really. Beyond that, you were on your own and had to make your own SQL tables. This was bad. Anyone who has wasted more than 100 hours of their life with the features module? Gone. Problem solved. Instead, there is a standard config system with configuration objects. We have a file in our config install sub directory. It has one property in it. Default count of three. We're providing our default values right there. When you install the module, this file gets copied into Drupal's active storage and never touched again. Now you've got your default present in the system as soon as you install the module. You also need to define a schema for it. There is actually a standard YAML schema format. I'm not familiar with it in detail. This describes your config file. This is mainly for translation. You can tell Drupal which strings in my configuration are supposed to be translatable, which means if you turn on your translation modules, of which there are only three, your configuration becomes translatable too, unlike in every version of Drupal ever. All right. Now we've got our configuration defined. So let's set up a config form. There's a number of base classes. For this one, we want config form base. So instead of using a callback function, it's now a class. So we've got config form, extends config form base. We specify our form ID explicitly. And then we've got a build form function, which works pretty much exactly the same way as form builder functions worked in the past. So, you know, form array definitions are pretty much the same. Except, oh, yeah, number. We now support all of the new HTML5 fancy form elements. They're supported in FAPI. So in this case, we're going to create a number element. We're going to give it a title of default hug count. And the default value for it is going to be our current configuration hug.settings, default count. Hug.settings, that's the file we created just a moment ago. So we're loading up that config object to our system. And we're setting that as the default value. Just like you would use variable get in the past. And then there's a validate form method, optional, and a submit form method. On submit form, you get the form object, just like you have in the past, and form state. By the way, form state is now an object. It is no longer a gigantic array of who knows what. It is a defined object with methods on it, and your IDE will auto-complete it for you. Thanks, Tim Plunkett, for that. So we let the parent form do what it's going to do. We load up our config object. We set our value on it off of form state with this nice get value method, so you don't have to guess where things are going to be. And then we save it again. Done. Almost done. We find our class, we tell Drupal about it. In our routing system, we said, all right, hugs.config, that's the name of our route. I'm going to put it at admin config system hugs, just, you know, nice place in the admin section somewhere. And we're going to say it's a form with this underscore form, and that's the class. This is the equivalent of, you know, page callback is Drupal get form and page arguments is the name of the form. This is a much more direct way of specifying that. And so we're saying this form, we're going to shove at this URL. And you need this configure hugs permission for it. Oh, yeah, permissions. You can define those in YAML now, too. So we've got our permission machine name, which has a title and a description. Just like it did in the past, it's just in a YAML file instead of in PHP code, which means it doesn't need to get reloaded on every single page request. And we also put it into the menu. This is yet another YAML file to tell a different subsystem about something. In Drupal 7, HookMenu did about nine different things. And so, you know, it was impossible to deal with menu links, but not the menu routing system and, you know, to have actions and why were those in there. We had far too many things integrated together. Those have all been pulled apart to separate systems now in Drupal 8. So we create a menu link. We have the same name as our route, that's fine. And it has a title, description. The route by name that it applies to. And it's parent. We explicitly specify what the parent is rather than trying to guess based on the path. So a couple of the lines of YAML. And here we go. There's our new menu link that points to our configuration form and has that one form field on it that we specified before. And it has the default value we gave it. And a save button. Great. Push save, we're done. Just like, you know, system settings form in the past. One other note. This is an HTML5 number field, which means if you're in Chrome, you will not be able to enter a string. If you're in Firefox, they still don't actually support the number field. I don't know why, but when they eventually do it'll work. In the meantime, that'll still get enforced on the server side. So let's go ahead and use HTML5 elements all over the place. Use the new form elements. Now we modify our controller to say, all right, we're gonna take a to and from and a count. And if there's no count or if it's zero, then we're going to pull out of, we're gonna load that config object we had that we defined before. And we're going to get that default count value. Same thing as doing a variable get, same concept, and make that the default. And then hug page, we just pass three values to. Again, all display logic goes in the template. We modify the route for that to say, all right, it's at hug from to count. And we do these two other things. Under defaults, we can provide a default value. If count is provided in the URL, it has whatever value that is. If not, its value is gonna be zero because we said so. Anything in this list here and anything from here are what you can get passed to you in your controller. We're also gonna say count has to be a digit. It has to be a number. It has to match that regular expression. So if you go, you know, slash hug, slash larry, slash duplicon, slash hi, you'll get a 404 because there is no such page that accepts a string there, only an integer. You can do other regular expressions there too. So if you've only got two or three legal values, you can specify those just pipe the limited. It's a regular expression. And then only those three values will ever get routed. You don't need to check out for that in your controller anymore. The routing system takes care of it. So we modify our theme. Just add another property. And, oh, my God, we can handle plurals in the template too. So if there's one, if count is one, then we use this string. If count is plural, we use that string. In this case, all I'm doing is adding an s on the end because that's all I need to do. If you want to do something different, have fun. For far more on this capability, go talk to Morton. He's giving a session. And now we go to slash hug, slash larry, slash duplicon. We don't specify any count. And we get a default of three. We get an s on the end. If we do specify one, specify only one time, because I'm in a bad mood today, I'm only going to hug duplicon once. Then we get one time. And again, all of that logic is overwriteable, including not having plural handling. It's entirely up to the theme. Blocks. The other major thing. Who has worked with the block system in Drupal 7? Who understands the block system in Drupal 7? A lot fewer hands. Okay. We need to back up a little on this one. Definition. A plugin. We've heard about the plugin system in Drupal 8. A few people. There's a session on that, too. I love going as the first slot from Joe Schindler. It's an excellent session. Go to it. It's all about plugins. But a plugin is a swappable, user-defined piece of functionality. User-defined meaning a user configures it to work rather than it just magically being part of the system. A plugin type is a category of interchangeable plugins. So think views. You have style plugins. View style plugin is a type. A plugin is an individual implementation of that. And then discovery is the concept of getting a list of all the style plugins. Figuring out what modules are providing. Put another way. Plug-in type is an interface. A plugin is a class that implements that interface. And discovery is find me all instance of. It's a little bit more complex than this. But conceptually, this is all it is. We're just abstracting good OO practices to make them easy. In Drupal 7, if you wanted to have an extensible system, sometimes you had info hooks, which may or may not have magic callbacks. Otherwise, every system did it differently. You want to add a new text format? It was different than how you added a new block. It was different than how you added a new... I don't know, what other things are extensible. Image style. Everyone reinvented the wheel themselves. If you were lucky and contrib, someone used the Ctools mechanism, which, ugly as it was, meant to integrate with features, you hope. You know, Ctools plugins, they work is about the only thing I can say about them, and I'm quoting the Ctools plugins team. It's old PHP 4, you know, insanity. Nothing in common between these different systems. In Drupal 8, if you want to expose user configurable logic, as in users can configure which of these things to use, you want plugins. That is your answer. That is the answer you want, always. You define an interface that is, you know, your plugin, how you want it to work. You have a manager class for it, you go to his talk, and you're done. That's what you do every single time. It's very consistent. Learn once, apply everywhere. I love this line. If you want to expose user configurable stuff in your modules, you use plugins. That is the answer. If you want to plug into some other system, you use plugins. That is the answer. What's plugins, Drupal 8? Almost everything. Anything that users can configure should be a plugin. The important part is that you're going to minimize the number of products and your current time for beta, not because it shouldn't be. So, actions, blocks, text formatters, everything in field API. Who knows all seven field API callback functions? Because not actually hooks. They're callback functions. Who knows all seven? Exactly. For the recording, nobody raised their hand. Now, plugins, which means you're going to look at the interface So conditional logic can be done with plugins. Editor extensions for the new in-place editor. Entity reference selections, rest resources, all of these things, the new tour system, views. All of these are using the same underlying system. Learn once, apply everywhere. So what does this look like in practice? So this is our block. We're gonna put it in the plugin namespace, and it's a plugin of type block, and hug status is our class thing. The location matters. In most of these cases, there's a convention around where you put things, but it doesn't actually matter. In the case of plugins, it matters. Just put the file in the right place, and Drupal will find it. So we've got hug status, extends block base. We've got a base class, extend it. And we've got a build method, where we return a render array that is the body of the block. Cool. That's the sense of our code. And we have to tell Drupal about it. In this case, we don't use YAML. We use an annotation. An annotation is metadata about the code put into a comment, because PHP, the language, doesn't have support for annotations natively, because PHP sucks like that. The number of languages do in PHP, we have to do it in the comment. This is not documentation. This is definition. Essentially, it's the same information that the language would go in an info hook. We're defining an ID, so we have an at block, specifies what type of thingy this is. Our ID is hug status. We give it a label, it's a translatable string. We give it a category, which is a translatable string. And that's it. This is the entirety of what you have to do to make a block. It now shows up on the block admin page right over here, in the system category, you create a new one of those, because you're instantiating it. You put it into the sidebar, hit save. You now have a new instance of that object, new instance of that block that will show up in the left sidebar. Guess what? You can make two or three or four or twelve of them. You can finally, for the first time in Drupal history, place a block multiple times. It's fascinating the things people actually are plotting for. So you care about not using features, and you care about placing blocks multiple times. Got it. All right, let's make that block configurable. Add a couple more methods. These are in the base class. They've got default implementations. It should be familiar. We have a default configuration where we've got an enabled property, which is a value of one, or true. Block form works exactly like a form build. And so, all right, here's our form-enabled checkbox field. We give it a title, and we get our configuration out of the disconfiguration array. That's the default. This is the current value. So first time, it'll be enabled by default. Block submit. We save it back as a boolean to configuration enabled. And actually, it'll save automatically for us. And then we modify our build method to say, all right, if the block is enabled, then we say now accepting hugs. If not, we say no hugs. Sad face. And we return that markup. And then on the configuration form, we now get that additional checkbox. It is, in fact, possible to override this entire form. Don't do so. Most of the time. You may occasionally need to do so, but most of the time, you shouldn't. But you could by just extending the right, and that's all you need to do. Everything for that block is in one small class. Right there, and if you look at the parent class, it'll tell you exactly what you can do and what you need to do with it. That's just good object-oriented code. Services. Services, what's that? Are we talking about web services? No. We're talking about service objects, which is an object that does something useful. And it is stateless, which means if you call it once, you call it 100 times, you get back to the same results for the same input. Think of it as a, you know, a pure function that's been in any of my functional programming talks. Okay, no one gets that reference then. But it's a class that you can reuse as a tool over and over again. Generally, there'll only be one of them, but it's not a singleton because it does not enforce that there's only one of it itself. The system takes care of that. So let's create a service called HugTracker. We're gonna keep track of how many times something's been hugged or what's been hugged. And we're going to take, you know, as a constructor parameter, the state system. What's the state system? The state system replaces variable get. Variable get, variable set are no longer there. If you want to just store key values, use the state system. If you want to store configuration that you want to be pushed forward from dev to stage to production, use configuration. If you want information that does not get pushed and is not part of deployment and, you know, changes on live and it's supposed to change on live, like last time Cron ran, you don't want to deploy that. Or the number of times someone's been hugged, you don't want to deploy that. That's state information for the system itself. It's just a key value store. And so we've got on our service a method add hug and we record to the state this key and that value. Hugs last recipient and whoever we just hugged. And then we've got to get last recipient where we get that value. Note this means we're abstracting around the state system. This is a good thing because this service, this object means something to our module. Someone reading the code, looking at this class being used, can understand what it's trying to do rather than just, oh, it's reading something out of state. What does that mean? We can't say, oh, I'm getting the last recipient hugged. And then we tell Drupal about it. Notice a pattern emerging. Hugs.service.yaml. This is where we wire up all of our services. It can get fairly involved. This is using the symphony dependency injection container. So there's a lot of functionality in here. Basics for now. We've got a services key. You can also specify parameters. Our service is hugs, our module name, not whatever you want. Name, space, or periods. It's a class called Drupal HugsHugTracker and takes as an argument the state service. And that tells Drupal when I ask for the hugged service, also go instantiate the state service and pass it in. And you just take care of that. I don't want to deal with it. Drupal, you do it. Then in our controller, this gets a little bit more verbose. In our controller, we have a constructor which takes the hugged service, the hugged tracker service, and saves it to a protected value. Same way. But how do we tell Drupal we need that? Well, controllers are not registered in the container because there's way too many of them to do that. So instead, there's this static method called create, which if you define it, that gets called to create your controller class. We do this in a number of places, blocks through it too, works on forms as well. And so it gets passed the container and you can just return new instance of this class, that's what static means, and get that service out of the container. There's no actual magic happening here. This method gets called, it's a factory, it gets given the container. We pull a service out, we pull a value out of the container, create a new instance of this class, and return it. And then Drupal will dutifully call the hug method on that object. Great. So every time we get hugged, we add the two person to the hug tracker. Whatever that username is, whatever that string was, we save that. And then same code we had before. If, you know, except that it falls from the end of the thing and so on. Then in the block, we can do the same thing, except this case we have to implement another interface for it, which lets us do exactly the same thing. So plug-ins have a couple more parameters to pass through, just pass them through and ignore them. It's part of the internals of the system. And we pass the same service in, save it to a property. And then in our build method, we can say, all right, if the block is enabled, then get the last recipient's name out of that hug service and make that part of the message. And now our block says, Drupalcon was the last person hugged. And if I went to some other page, it'll say that it'll keep saying that until I come back here and hug someone else. So that's the architecture part. That's all Drupal framework-y stuff. But Drupal is a CMS. How do I actually work with content? What happened to nodes? Well, I started about entities in Drupal 7, but they were kind of half-baked if I'm being generous. An entity is simply storable content with an ID. Storable content with an ID is an entity. Content entities are fieldable. Those are the kinds we're used to, like nodes. You can put fields on them. There's also configuration entities which are not fieldable. They're a useful shortcut. We talked about those directly, but the configuration we were talking about inside of a block, that's a configuration entity. Again, probably not going to deal with those directly, mostly just through plugins. And the actual API works now. So if you work with the entity API in core in Drupal 7, I apologize. If you work with the entity API module in Drupal 7, I apologize a bit less. If you work with the entity API in Drupal 8, awesome. So content entities are the things we're used to being entities. Nodes, users, taxonomy terms, comments, and so on. So let's go ahead and create an article node. We're going to give it a title of my post. It's an article. We're going to give it a nice body. Hey, look, a wissy bag built in. We're going to give it a couple of tags, Drupal Symphony and PHP. Cool. All right. Save that. Let's make a new controller. We're going to put it at slash node slash node object slash hug. You know, same kind of a thing we've done in the past. We're going to say our controller for it is this hugs controller node hug. Need access content permission. Great. All right. Whatever. And because our parameter is called node, and because we have the node, we're type hinting the parameter, it'll get automatically upcast to an actual node object. Same kind of parameter conversion that we have in triple seven is still there. It's now triggered by an interface on the entity. So, boom, you have your node. Node is an object. It's a class object. All entities are class objects. They're no longer arrays that happen to use arrows, which means they are structured. There are methods on them that mean something. So on nodes, we can say, is this node published? No. Rather than remembering the name of the property and hoping that it's set correctly, no. There's a method for it. There's a method to the madness. Oh, you want fields? Who always forgets a piece of that field, you know, array to get something out of a node in triple seven? Everyone who's actually tried to do so. Do you remember actually you're supposed to use field get item or field get items? Who actually remembers to do that? My hand is not up. You just access it directly. Node, body, and grab the value off it. Body's the name of the field. Value is the individual property out of it. Oh, which is the same as body zero. What is going on here? How does that syntax make any sense? We're using the PHP magic get and set methods to dig in and grab these objects. So body here is an object. Whoa. Sorry. There we go. Yeah. Body here is an object. Actually, it's an object that also implements array access. So if you want a specific index out of a field because all fields are multi-value, specify it. If it's a single-value field, you don't need to specify the zero it's assumed. So those are exactly equivalent. This makes the code way easier to read. But you don't want the raw value because that's unfiltered. Print that and you've got a security hole. And then you've got a text. So just access that. And that gives you, on the fly, the processed value for that text field. You want to, you know, field tags, let's say, that's a reference, iterate over that. For each one of those, because it's an entity reference field, there's an entity property that gets you the actual object. And it does some fanciness behind the scenes to make it not slow. And we can get the label off of it. What's label? No title or comment subject because all of the different names, they're all called label. Any entity you're being pointed at, label is the method to get the human readable name. Nice and consistent. So let's just build up a list of the title of all those terms. Great. Then we create, you know, string. Everyone get hug name because reasons. Our reasons, so let's implode those terms. The name, node getOwner. We could do node arrow, what is it? Author is the field name? I don't know. But to node, we know that property is going to be there always. So we've got a method that makes it easier. GetOwner, we know it's single value. And we can get the label. This is the username of the user that authored that node. Nice and compact. And build up a string. Give it a title. And a markup. And we return that. If it's not published though, we just send it back a message. This is horribly contrived. Don't actually do it this way. Use a template. I just wanted to show off the entity API. Because the entity API is really nice. It's worth showing off. I believe Fago is doing a session on that later as well. Yeah. Put that in a template in practice. But here we go. We've got our title. And we've got our body that we created. And we're good to go. And notice it's living at node slash one slash hug. Awesome. So let's review. Drupal in two steps. Build a tool. Wire it up. That wiring will either be YAML or an annotation. When do you use which one? Here's a tip. If you are telling Drupal what something is, that's probably an annotation. If you're telling Drupal where to put it, that's a YAML file. That's not a hard rule. That wasn't even deliberate. But I found that's a pretty good approximation of what you're supposed to do. If you're describing the thing, if you're telling Drupal what this thing is, annotation. If you're telling Drupal where to put something, YAML file. General guidelines to keep in mind. Keep your classes small. Keep your classes single purpose. Keep your classes focused. And your methods should be even more focused. Your code should read nicely. Just looking at the code you should be able to tell, this does one thing and does it well. I'm not doing two things. I'm not doing four things. I'm doing one thing and doing it well. That's the job for a method and for a class. Break things up, wiring them up in the service container makes it easy to break things apart and then assemble them on the fly as needed. Your controllers should be stupid. All the logic of your module belongs in a service. The controllers are simply glue code. So if you're used to having a 300 line page callback, please stop. Don't do that. Move that logic into services so it can be reused and then have a controller that simply takes parameters, calls a service, passes stuff off to the template. That's all your controller should do. Your business logic belongs in your services. That's where the actual meat of your module is. Blocks too. You should just be dealing with putting stuff on screen. Templates and services should handle your display logic and business logic. One other thing. I spent probably $1,000 in Drupal 7 building a nice new, complete, easy to use database layer. And I'm going to tell you in Drupal 8, please never use it. In Drupal 8, there is almost never a reason for you to touch the database yourself. If you want to store information, you've got entities. You've got configuration. You've got state. Your odds of needing something other than those are really, really slim. If you don't want to use the state system, you can use any key value set, actually. So you can have your own key value index independent of the core state system. That's fine. Configuration, entities, and key value. Why? All of those are pluggable. You can change the backend on them if your code doesn't break. If you touch the database yourself, I can't run your code on MongoDB. If you just use entities, configuration, and state, someone can use your module on MongoDB without ever talking to you and it just works. They can move portions of the system to Cassandra. They can use a mixture of SQLite and Postgres if they want. Your logic should not depend on the storage engine. 98% of the time, if you're writing an SQL query yourself in Drupal 8, you're doing it wrong. Please do not use my API. And also, if you find yourself trying to unit test your code, and when I say unit test, I mean PHP unit, which is worth a whole session on its own, not simple tests, and it's hard to do, that probably means your code is too entangled. Refactor it. Break it out in separate classes. If it's hard to test, you are doing it wrong. It's a good guideline to have. And that is Drupal 8. Thank you. Once again, my name is Larry Garfield. I'm a senior architect with Palantir.net. You can stalk us on Twitter. We do have a newsletter. It's very lightweight. These slides will be online very shortly. I'll be tweeting them from at Krill and from at Palantir. Please do review this session on Drupal.org and on joined in. We have a joined in account. This session should be listed on joined in. Please go review it there, too, or there instead. And I think this is the end of the session, but if you have questions, I'll be hanging around up here for a while. Thank you.