 Good afternoon everyone, welcome to developing custom views plugins and really my story of falling back in love with views. I've actually told a similar presentation at a couple of Drupal camps earlier this year where I talked about the business case of using views plugins and then I spent a little bit of time looking at some code and the feedback I kept getting was we want to see more of that code. So this presentation is sort of the spiritual successor to that. Let's actually look at what defines a views plugin or what parts are pluggable. And I hope you leave this feeling of power to use views in new ways, really to develop complex features and to build site building workflows that are just sort of inclusive across your team by using views in our sort of standard, our standard ways. So my name is Jim Romero and I'm a senior engineer at Four Kitchens. I have over 10 years of Drupal experience and I use the framework for all minority hobbies. So running calculations for brewing beer to planning, retro or arcade projects to teaching kids to prepare for the zombie apocalypse at local summer camps. If you have any questions after this presentation please reach out. I'm always excited to talk shop and keep the conversation going. And I am in Jim on all of the things including our Drupal Slack. I'm a web chef at Four Kitchens and we build websites and apps for organizations that depend on large scale or unconventional content. So we work with associations, publishers, enterprise organizations and media and in higher ed. And we make big websites. Now for today's presentation I have created a demo site for experimenting with views and as a millennial nostalgia is a bit of a drug. So it's going to center around 90s movies that I saw while growing up. All of our examples are actually inspired by real client work in particular for a multimedia company that was doing streaming TV shows. Our demo site is going to include three content types. So we have the movie. That's our core content. A cast is a role on that film. So a character that may be played by zero or more actors and a person is that actor. So they might have one or more roles as a cast member of a movie. So the relationships are movies, have cast, have people. There's also some taxonomy terms. So genres is our evergreen categories like a comedy or an action. And then there are keywords which have temporary significance. So maybe this fictitious site is going to do movies based on a book. And all of the data it comes from the movie database API and all of my code is on GitHub. So I will share that link with you at the end of the presentation with my slides as well. I also want to mention I am doing an awful job at looking at any conversation going on. So I will do my best if I see questions, but mostly we'll try to leave plenty of time at the end to have that conversation to have questions. So let's talk about views. And it's played an important role in my history with Drupal. Views allows site builders to fetch content from the database and represent it in a collection of renderable elements. So it can deliver it to users through all kinds of display mechanisms and all this without writing a single line of code. And personally, I learned how to use views before I understood how databases worked. In my early years with Drupal, I learned the concepts of selecting and joining tables or traversing normalized versus denormalized databases. And practical limits of filters, groups, and aggregation, I learned all these principles without knowing it by name. I was simply interacting with my content types. And a lot of site builders are in a similar space. They're very familiar with Drupal's information architecture, but lacking the formal computer science knowledge. So views is a wonderful tool for folks of, I would say, every skill level to get started. And since there's no learning curve, there's, or since it's a, while there is a learning curve, there's no shortage of materials to get started. So I find it's great for projects that I'm building for clients where new folks are joining the team or we're handing it off, views becomes that common API. And let's see if we can dig in a little bit more. So benefits of views. I'm already talking them up. And the benefits go hand in hand with why we would use an open source CMS like Drupal in the first place. It's feature rich. It is a mature product. And when I make a view, I know that I'm not going to have to worry about routing and caching and header and footer controls, template switching, a lot of this context, a lot of these Drupal bits are baked into the views interface in one way or another. It's actually way more than view in an MVC stance. It's extensible. It has a large family of modules on D.O. that integrate with it. And there's no shortage of code examples of how you can extend views on your own. It's accessible to site builders regardless of their program experience. It's very consistent in how it's ported from one version of a site to another or from one version of Drupal to another. I'm sure that the views I built on Drupal 8 are going to map to my Drupal 9 site very well. And if not, there'll be a simple configuration upgrade path. So I'm excited for that. And it's just well supported by the community. It's nice to know that what I build in views is not building in tech debt for the future. Oh, I should also mention that in the spirit of today's presentation, we're building collections of movies. So I have some here and there's always a theme to them. The movies here on the right, I'm calling this Ferris Bueller's Off Day. And that's movies of Matthew Broderick that just got panned by critics. So see if you can guess the other ones. Now I'm talking about views like they're the best thing ever, but the truth is for most of my career, I tended to avoid them, I mean, with a passion. And I think my reasons were shared by a lot of other developers. I like to reinvent the wheel. I want to make the best and the leanest tool for whatever jobs at hand. And that often means building something from the ground up. I can stay more organized with custom code. Now there's little room for annotations in a view. And for very complicated views, it just becomes impossible for the next person to figure out what the heck you were working on. You get into the kinds of relationships and aggregation and just good luck. And truthfully, we hit practical limits of what views is capable of doing out of the box. So clients are always going to have special requests. And they shouldn't know how the database or the content model works under the hood. We just say, oh, views can't do that. So in working with views like any framework, it can feel restrictive. If you share these feelings, please keep it open mind. For most sites, I can overcome these limits by writing a little bit of custom code, usually in a plugin. Oh, and by the way, this collection of movies on the right, I call this icy dead people. And that's movies where someone freezes the death. All right, I keep saying plugin. And what does that mean? If you're new to the Drupal space or Drupal 89 space, plugin is a way for us to organize code. And it's actually pretty generic on purpose. And views is leveraging plugins very well. This is just a sample of some of the plugins you might find out of the box in views already. Things like access, argument, cache, display. These are baked into Drupal and access, for example, there's three out of the box in core. You get no access rules is one of the definitions. You can access a view by permission and you can access a view by roles. There's nothing stopping you from creating another view plugin, which is access a view based on the time of day. And it might be a weird one with some caching, but you could or access a view based on your standing in the community. Maybe it's a community site, your gaining points or membership status beyond roles. So by all means, these views are a way for you to build off the logic that's there in actually a very easily interchangeable way. I don't have time to look at all of them. So the ones that I have bold, we're gonna try to take a little more in-depth look at in particular field, filter and sort, but we'll talk about arguments, relationships and queries. Know that what you learn here, you can start looking at views pager, views join, views cache and get a sense of other ways. Oh, I didn't say what this one was. This collection of movies here on the right. This is called, told you I'll be back. And that's movies of Arnold Schwarzenegger that are sequels. All right, let's do it. Nuff Drupal, let's look at some code. In fact, let's start by looking at the site. This is my movie site. And these are simple views. I just threw a jQuery slider plugin on. Not that I use jQuery often, but I'm not a themeer. So forgive me. And I can look at it. I'm just getting a node teaser of each one of these. So action and adventure. And I will spend some time looking up what each one of these is. Oh, it looks like I need to log into the site. Always fun doing live demos is we get to see what's going to work and what won't. So let's give this a shot. I'm using Lando to log in link. Everything's always slower when you're screen sharing too. By the way, some great movies on here. The net, fantastic. Okay, don't know why Lando's going, or why my local VM's going so slow. So we'll keep looking at some other stuff while this goes. Let's open up the net and take a look at what's going on in this content type. This is the movie content type. And in it, we have quite a few properties. All these are pulled from the movie API, our movie database API. The ones I want to call most attention to for a couple of demos here is poster path, which is just given as an image location. There's also this credit. So this is the name of the role. So in this case, Angela slash Ruth. If I were to open that node up, so my movie node contains a credit node contains an actor node, so Sandra Bullock. We'll keep that in mind for one of our examples. Let's log into my site. Some other ones that are interesting. There's a popularity. We'll probably sort by that in one of the examples, revenue and budget. So we can kind of see how well the movies have done. All right, we've hacked it. We're in the system. I'm gonna assume most folks on this session are familiar with views. So I'm not going to dive too deep, but let's at least take a look at one for a superficial orientation of how views works at this view. So this is the action and adventure. And I will zoom in a little bit for anyone who's maybe still green with views or not very familiar. It's a nice little dashboard. It's a lot to take on your first time, but like I said, it taught me a lot about being a better programmer slowly or surely as I understood how each piece worked. So fields in this case, I'm not using any fields, but this is our select statement. We're just selecting the entire object. What are we selecting? We're gonna filter it then. So we're filtering by it being published, by it being a movie and by the genre being action, but not including family because there's some action movies that would might be like spy kids. And let's say for this website, I didn't want to include that. So we're getting rid of the family movies and I'm sorting them randomly. What's fun is I scroll down here. If you don't have this feature turned on in your view settings, you can say show me query statistics and show me the query we're building. And this is where I said I've learned a lot and we can see that we're selecting all of our nodes. We're including just sort of a way to randomize them. We're gonna join them on the genre and just by the nature of use, we're gonna do that twice because of how we're connecting these and these are the genre IDs. So we're including family or excluding family. We're including action and we're looking at ones that are status of published in the type of movie. This is a lot. We're gonna build some from the ground up and we'll get a better sense of what's going on. In fact, let's do that now. I go to manage structure, views. I created one just called demo for today. Says I have some unsafe changes. Let's get us back to a good starting spot. So at the moment, I am looking at published movies and I am just rendering the title. I also have results heading just so we can see what's going on. When I look at the query, I can see I'm selecting nodes and it's selecting nodes by node ID. Really the whole node is passed to the render portion of a view and we'll look at that in a minute. Where the status is one. So the node is published and the type is movie. Excellent. And I'm looking at a hundred for the sake of this presentation. We're gonna bump this up and down for right now. Let's just look at 10. And I see how do you get these queries to work in something like SQL Pro, SQL Workbench is the one I use, my SQL Workbench. One of the biggest things is this escaped portion right here. This is very much interpreted of how the query would look. I mean, realistically, it builds queries for all kinds of systems. It's database agnostic. It can even fetch data from external APIs. So you do end up with some notation that doesn't quite translate. The most important thing for me is I would probably just delete this whole thing because it's not adding value. And I can just call it node field data. And if I remove this part in the queries, it will just start working. So hopefully that will help you. But there we go. Here are, in this case, 10 movies that are published. Let's have some fun with this. Like I said, I've done a presentation on this before. I don't wanna repeat those examples but I will do this just one because I think it highlights the use case for custom fields. And there is, I showed before, a field called poster, poster path. And if I add this to the view and I'll just continue to use its default settings, we're gonna see a file extension. It's not super useful. So golden eye, here's the poster for golden eye. That doesn't make a lot of sense to me. However, I looked at the movie database API and I just happened to know that I can have some fun one second. So I don't mess this up in a live demo and copying and pasting code. So let's override the output of this field and make this big enough so everyone can see. What I can do is take that poster path value and I can prepend this URL and that's gonna render it as an image from the movie database server. Also through in the title value is an alt text and I wrapped it in a link. The link's not going anywhere at the moment. But what happens, I took this ugly field which didn't mean much and I've given it something of value. I click apply, we should now see our poster path. Hooray, we've used views. I already hate this. I already hate the direction this is going for a couple of reasons. For one, we're mixing markup into configuration. We have that HTML that's stored in a view config file. There's not really much in the way of fallback. If let's say I didn't have an image, this rewrite wouldn't make any sense. I might end up coming down to the no results behavior and then giving it a fallback image. But now I have even more code thrown in where I don't really want it. And it's kind of hard coded. If I want to do this another view, I have to copy and paste that somewhere else. It's not very reusable. It's also very fragile. So in this case, and if you've mind of experiences and views before, I'm using another field. You can kind of see it grayed out here. I already have access to the title field because the alt is the title and I can render it. And you can see the field you have access to down below in replacement patterns. There it is. There's one called title. But if I were to rearrange these, I would no longer have access to the title because you only have access to the fields that were called ahead of it in the render. So it's super annoying. It's super fragile. I don't like it. Let's not do this. Let's create something custom and talk through some of the positives doing a custom view plugin. So our very first plugin is going to be a field plugin. I'll probably do a couple of field plugins, a sort and a filter, and you're going to be so empowered here. I love it, I hope. All right. Taking a look at code. And let's make the screen a little bit bigger. All of this is shared on GitHub. And I am not going to spend too much time on things because in all reality, the plugin piece of this is fairly easy to wrap your head around. So I'm going to probably push through some things that are just Drupal APIs, but really the sky is the limit. I'll try to show you the core of today's presentation and then use some real useful examples where I can. So most important thing is I have a module. It's called movie views. And inside that module, there is a movie.views.inc. And this has a hook in it. So this is the first step you need to create a custom plugin. You need to have this hook views data alter. Like I said, right now, this is your way to register a new plugin with Drupal when Drupal is collecting information about what plugins are available. It's going to look through and in this case have the base table. So when you create a new view, you usually say, is this view a node or actually I think it says content or is it a user view? Is there a taxonomy view, a solar API view? So we have that piece right there and then the name of the thing that I'm adding. So the one we're going to go with first is called demo field plugin. And I have some very generic language. It's just literally called demo field plugin as a title. I'm defining a field and the field has the same ID demo field plugin. So a field plugin for a live demo. I could scroll down the rest of this. It gets very redundant. So the examples are there if you're interested, here's an example of one that I hope to show off today called a word count where we're going to count how many words are in a title. I have a field for it so we can display it. I also have a sort. So we can sort by the number of words in a title. There's some joins, some relationships but that's the heart of it. So it's demo field plugin is the ID of it and where is that file located? Once again, if you're new to this this might seem a little confusing. It is strict though. So within my module in source plugin views there's going to be a number of folders for each type of plugin. And the one we're interested in right now is the field plugin. So source plugin views field here are all of our field plugins. This class here called demo field plugin has here's the glue that holds it all together. This annotation at views field demo field plugin this matches what was in my views.inc. And so Drupal will know that when you pick that plugin it's going to execute whatever is defined in this class. For any of our fields we have two methods. So the query method is our chance to change the MySQL query that let me make it so I can flip back and forth and hopefully not give anyone kind of motion sickness. Oops, looks like I went back. So we can first alter this query. So that's the query method. Sometimes I don't want to though. In fact, in this case just out of the box all I want to do is render this the string example plugin. So there's this render method and it needs to pass back something renderable by default Drupal will take a string as a renderable element but you can use a template or something more complicated some sort of render structure. I am leaving this in here as an empty method because it's being enforced through the plugin field base. So you see that it feels weird it's because we're satisfying sort of a contract made with his parents. So rendering example plugin let's add it to our screen. Oh, did I already do it? I did demo field plugin. I added it to the view. It did not change our query at all. Did I not do it? I hit the browser back button and I think we're seeing a cached version of what I was working on right before this to make sure everything worked. There it goes. Okay, let's add it. So it was called demo field plugin. It had that title and there's our description. I added, let's just add it with its default values. And when I scroll down we'll see that for every movie it just says example plugin. Hooray! Seems like a lot of work so far but trust me it's gonna pay off. Did not alter the query at all. This is the exact same object that was here before. Let's see if we can make this do something more useful. So I wanna render that image that we had before. And in this render method we have this thing values and I just happened to know so I'm passing my knowledge onto you that in that value is the entity for that row. So let me try to do it a little slower. This row is golden eye. That's a node, right? This is a node, a view of nodes. So this row is for the golden eye node, whatever node ID that is and get node one. That must be the default search, a sort. So I have access to node one here. It's already loaded. I don't need to alter the query to have access to the fields inside there. Now because I just need to have lots of things spelled out here, let's throw a little code in here for safety. I need, I wanna have access to a field. And so our field is called, field was called poster. Maybe it's poster path. I don't know, we'll find out if it fails. It was poster path, yeah, thank you. So the field is called field poster path. And because I'm neurotic, I'm gonna check, does this entity have that field or is that field currently empty? If so, return nothing for this row, just a healthy check. And then I wanna get the value from this field. So this is just Drupal standard APIs. So in this case, traversing an entity, trying to make this a little bit bigger here. So the path, the value of that field is entity, get Drupal field poster path and send that to a string. So get string is gonna return the value for that field. And if I just take this path and I return it, we should have a string, which I know we can return in a render method. And while we just did a bunch of boilerplate code, it might be a little disappointing because Tadah, we're right back, oh man, I messed something up, didn't like that. Unexpected if, because I need a Sammy. Been running a lot of JavaScript lately. It's been messing me up. So there we are. We're all the way back to where we started like five minutes ago, pretty anticlimactic. But what can we do to make this more interesting? Well, I mean, I have the poster path. Maybe I can natively return a renderable Drupal element. Once again, I'm just copying and pasting. So I don't mess anything up. So in this case, theme of image, reducing the Drupal theme object here for an image, hard coding this URL, and I have alt text. So I'm gonna take the title of the entity since I have access to the entity already. I click save, let's update this preview. And wow, we're doing so great. This felt like a lot of work to get a field that I already mocked up pretty well in a view, but let's talk about a couple of positives. One, I have a separation now of my programming logic from my configuration, and I'd like to keep it that way. If I wanna reuse this in multiple parts of a view or multiple views, they can all call the same plugin, which is nice. And if I wanna take on complicated programming tasks like a fallback or the movie database will let me pick posters from different languages. So let's say my user was in a Spanish-speaking area and I knew that from their browser or that their browser was set to that as a native language, I could switch out which poster you have. All of this logic is baked into this PHP file, and it gives me as a developer a place to comment it and organize it in the way that I'm most comfortable with. There's one other power that I love and I think it's worth, and that is, so we started by looking at the query method and the render method. There are two other methods and I'm not gonna go too slow other than just show you that they exist. But the first one is called, first one I show is build options form. So when you're adding a new field, you get that pop, that modal of what kind of options you wanna have on that field, you have access to change it. So I have a thing here called image size. And I just on this form, I wanna add a new field called image size. There's the title of image size and it has, it's a select list. So it has thumbnail or box and it's width 92 or width 500. And because we are calling form state, so I need to add that into my uses. The second method, which is just a set of default is define options. I'm setting my default option to that width 92. Now when I go down to my render method and we have this width 92, I can pull that in programmatically. And so let's get the size of the image is equal to this object options image size. And I hope this is finally highlighting the power of creating your own custom, which has seen nothing change. We still have that width 92 image, highlighting the power of why you would wanna organize your code in these custom plugins. When I edit this form now, I now have a new field that was in a normal Drupal one called image size. It has my own descriptions. I could put instructions to whoever my site editors are. I change it to width 500 and now we have that large image. So it started to feel good about it. It's more foolproof and I can start to create my own coding conventions. All right, that was fun. What else can we do? Actually, I might end up reusing that. Let me delete everything in here and let me just check this file out to a working demo. So that was an example of where I only access things through the render a method. But we also have this query method. And as I said, there's times you're gonna wanna do this. And it's almost out of art at this point. Do you do something in query versus render when you have so many caching layers behind it? But in general, anything I can do in the query that makes sense, I will do in the query. So if I have a calculation that I wanna compare two fields and later render results, I will do any calculation I can in the MySQL query rather than in the render, which is gonna be a little more intense on a per row loading node basis. So let's look at an example of that. And for the sake of time, actually I'm not gonna do any more live coding. Let's look at this word count one. I wanted to show off that there were two ways to do this. So there is a query method and a render method. And the only important part that you need to remember from today's session is if you add a field in the query, you can render it down below. So I added a field with the alias of word count and I can access it by saying values word count and I'm just rendering it. So in this example, I have my query object. It's available in this views field plugin. And the field I'm interested in is I'm gonna look at the node title. So the title of the node, it's already available since we're looking at the node base field. And I have a calculation. So here's my MySQL query. I had to look this one up. I didn't realize that this was a way of calculating how many words are in something. Count the characters in a string. Then, I don't know why I won't let me scroll. Then get rid of all the spaces and count the characters again and subtract the two and add one. And if you do that, it'll give you a word count. That's not a completely accurate word count because if you had a hyphenated word, it's gonna count as one word. I'm all using spaces to note that we have a new word count. But there's my formula. I've used the database API to add that field to the query. And then I'm going to render it by accessing it by its alias name. Let's find the look at here. We'll get rid of this. And word count. I might not have added this one. That's okay. I'll add it to my demo. I've been having lots of fun creating these. As a result, I haven't kept up on which ones are available in Drupal. And so one of the first things I'll see is this query has changed now. We are still selecting the node ID is just a record that we're selecting a list of nodes. And then we have this calculated value which is alias does word count. And when I scroll down, I see that that is here. So gold and I is recorded as one words as we're like splitting on spaces where the credit kid is three words earn a saves Christmas is three. There's some really good movies in here, right? If you have fun with performances, I do. This is an opportunity to start looking at things like query and execution time. Now, for an operation this small, it's kind of trivial, but as you get into bigger and bigger performance issues, let me go back to my word count one. This was an example of doing a calculation in SQL, but I easily could have also have done this in my render method, not change the query at all, but in the render, I could get the title and do the exact same operation. I probably wouldn't do it in PHP, but I wanted to show the exact same ones to get the length of the titles, subtract the length of the title without spaces and have one, and if it's still work. So that's an example, maybe an overview of how fields work. I might look at a couple of others when we have some questions at the end. I'm gonna keep pushing on. I'm trying to look at the slides in case there's any big pressing questions and sorry if I'm missing them, all right. Let's talk about filters. It's gonna look very similar. So in this case, I have, I'm calling this the excited filter, and a filter method or a filter class is gonna look just like the other ones. There's a lot of boilerplate here. If you're not used to that as a Drupal developer, copy and paste it and you'll learn the patterns as you go. It really is boilerplate. The only thing that's important for you writing code, if you're new to this, is that we have this query method that we have access to alter the query. And anything that I do inside this query method is an opportunity to change the query as Drupal sees it. So the first thing I'm doing, this is just sort of for sanity, is using this and share my table. This makes sure that we have access to the data that we need to. I know that's the case, but I'm thinking future developers as the site gets more complicated. And if you've used the database API before, this shouldn't surprise you, but a filter is a where clause and database API has this method called add where. So anything I add in here is going to alter the query. And in this case, I just wanted to show this off options group. This is a zero for us. We have no groups, but I'll mention when we look at the interface, what it means. We're gonna query on the title. We're gonna look for an exclamation mark and we're gonna use a regex. So we're looking for titles that have an exclamation mark. And this is called excited. So let me go back to our example site, get rid of this demo field plugin and let's look at an example of a filter now. So we're already looking at published movies, where let's apply the defaults. So now we have where it's published, the type is movie, and it has this regex where the title has an exclamation mark. And here we go. Oh, stop or my mom will shoot. How can people forget that one? Great movie, not even a little. So that's an example of using a filter. There's a lot of other practical examples of what you may wanna filter, but realistically the custom views plugin, we're now in a state to do things that are complicated that don't make sense for Drupal views out of the box. If you can do it in MySQL, you can do it here. And I keep saying MySQL, but really it's database agnostic. That's just the place I'm most used to. I'm gonna keep pushing through. I wanna look at a sort because it's actually the exact same thing. I know big spoiler. So in our, let's look at the example sort because that has things pretty empty. Just like the others, there's a lot of boilerplate. It goes in the folder structure. So source plugins, source plugins views. I don't know if I was gonna say the whole thing. I could open it up a little bit more. Goes in a folder called source plugins views and then slash sort. That's where all our sorts are gonna go. It has a lot of boilerplate. Feel free to copy and paste it. The most important line here is at views sort. This is gonna let the plugin system know that we've defined a plugin. It has this ID. This is the same ID that I used in my views.inc in my module file. The only method I have to override once again is the query. So this is working exactly like the filters and I'm using and sure my table just for sanity. I have access to a query. Anything I wanna do to change this object I can do now. So for a sort that for MySQL at least is gonna be most relational database is gonna be some sort of order by clause. And so this query order by and whatever the heck I'd like that to be. So if for example, for the word count that we looked at before, I have that formula that we talked about before and the sort was is just right here. So order by, if you look at the documentation for this when you order by a formula, the first argument is null. The second is the formula. The third is the sort order. This options order, this is gonna pull it in from the Drupal interface from the options. We don't have it in our plugin, but it's inheriting from its parent and then an alias for it. So it's called word count. Let's see if I have this one added to Drupal. And if so, we can take a peek. I'll remove this other filter and let's add a sort. You have it great. So ascending or descending, this is that option that's gonna get passed and it's gonna be ASC or DESC. I scroll down to look at the query, make sure it's up to date. And we see that we, once again, we have this field being added that has an alias of word count and then down here order by word count ascending. And so there's our ascending with our movies that are just one word as we're calculating it. I'm curious what the longest ones are because we didn't hard code a sort order and instead we use this options order by passed to the order by operations. We can flip it right in that interface. I scroll down, there will be Ninja Turtles too. The secret of the ooze takes the record for the longest movie title based on what I saw in the 90s. So anyway, oh, there's some great movies. Oh, my weekend's planned now. That is the bulk of it. It's actually pretty anticlimactic. The amount of work that you need to make a views plug in the sky really is the limit after that. Let's look at a complicated example. One thing in particular I want to mention is relationships because they get complicated quick. Relations are complicated. Let's remove. Let's go back to our base. So I mentioned how a movie has a cast member who has an actor. And if I wanted to have a list of all the actors in a movie, one of the things I would have to do is add that relationship. It was called cast. No, in a second, I don't remember actor. All right, I have to look it up because it's too important. I can't let it go. Structure, content types. Let's look at our movie content type. There is an entity relationship on our movie. That's gonna relate to the person and it's called credit. So I'm gonna add a relationship in our view for credit. Perfect, here's the movie. So what this will do is you might have used Drupal relationships in the past, this is a join. And it's almost always an inner join, although there are some ways to tweak it out, especially with different modules. So we've now done a join that says, let's create a join between our field credit data and our movie data and connect them where they can be. And you see a ton of golden eye references. And the reason is I'm getting one record for every cast member. Let's do one more join while we're at it before I show you what the end result is. So the cast member would have an actor on it and I think it was called person. Perfect, okay, reference from a person. And then you might have done this in the past before having multiple relationships feed into each other. Okay, so from our movie, we are loading the node that's the credit that's on that movie, the entity reference. And we're loading the person in there. And I wanna just add a second field, which is also a node title. There's not the movie node title. It's that two references removed node title. So I use this relationship of the person's name. And we should see, I think golden eye was the top Pierce Bross and top build along with the heck of a cast. One of the things that blew out here is our displays. We had a couple hundred movies. Now we have over 10,000 rows. This becomes a real problem to try to wrangle. And definitely a case where I would use custom views. So while I don't have time to go through every piece of it, I wanna show you a solution. And that would be this field I have called actors shortlist. So once again, it's just a field. So there is a query method, which I have left blank because we would be in some query hell in our render method. And in the render method, what I've done, same things as before, let's scroll down to the meat of it, is I have my movie node and I say load all the credits associated with it. And then for each credit, the actor is let's load all the people associated with it. And we're just gonna get the name, get the title of the person associated with that credit. And I'm doing an erase slice here. So let's just get the top three. And what I've created, and I guess you'll have to look at this in your code or hit me up and I'll show you some examples of it. Remove all the things I didn't want. And we'll just show off this one more example of what I'm calling the actor shortlist, which is a very reasonable request for a site owner that they want a list of the top three build with maybe the suffix that says how many other actors are a part of this project. And I scroll down and now I see them back to having only 436 records, one per movie, because I've created, I've defined my own field. So GoldenEyes, Pierce Brosnan, et cetera, plus 19 more. The code is just PHP, it has nothing to do with the talk, so feel free to go nuts. I just wanna show you sort of a more complicated example. The other things that are in here, make it hard, where's my sidebar? Okay, I'm not gonna worry about the moment. Other things that are in here are entity relationships and some joins. So I have some example join handlers for you to check out too. But as long as I'm working in nodes, there's very little for me to show off. And so let me just finish up here by saying that I really hope you can embrace Drupal's UIs. I, for years, did not wanna use views. I wanted to write my own code. Now I've realized I can write my own code and organize it through Drupal's levels of abstraction, which I absolutely love. They've helped me stay organized. They allow me to put comments and modern programming structures to work. And they keep my code highly functional rather than sets of configuration and configuration overrides through all kinds of entity hook alters. So thank you so much. My demo, all this code is available at that GitHub link in Jim slash movies. I'm in Jim on all of the social things. Please reach out to me, even Drupal Slack right now, I love it. Also shameless plug. I'm gonna post the intro game table tonight at our gaming thing. So if you wanna learn some new games. And what's up next? So we have four sessions starting at two o'clock, Advanced Google Analytics and integration with GTN, getting started with layout builder for eight and nine, inclusive leadership for managers, tech leads and open source maintainers, true life and introvert in an extroverts world. And then immediately following that, implementing a top notch search and social media meta tag strategy. So some exciting things along with a coffee break coming up right after this. And thank you so much. Like I said, this was a spiritual successor to a previous session. So you'll find more businessy talk in the other one. If you wanna look it up on any of the Drupal TV sites but the code, this was about the code. So what kind of questions you got? I'll stay on here until I get booted from the call or moderator can tell me I need to wrap it up. Well, thank you all. Can you please post the link again? Sure, let me post the link to the repo even. Or actually, I will upload it to, let me go back to the slide at least and I will upload all my slides to the Drupal, the Bad Camp website too. Cheers to you all too. And not only is this powerful Adam, there are so many different types of plugins in Drupal. It becomes a little addictive when you learn all the things you can change. And then when you learn how to make your own, you're unstoppable. Well, thank you all. Any questions? All right. Is there a way to do layout overrides? There are. I am not a front-end person, so I will admit I do not get too involved in here, but we do have several parts. So for instance, the pager plugin you've probably used as a mini pager and a longer pager and you can download some examples of folks who define new pagers. So just scrap the existing one and put your own thing if you have say an accessible and infinite scroll. There's likewise a row division, what defines a row, which isn't exactly the template, but it's connected to the template. And similarly with displays, and you've probably used these before too, where you can create a page or a block view or a feed or I don't even know all the different types of displays you can have in Drupal. So this gives you the ability to really redirect where the view looks for those pieces of code. Thank you, Chris for sharing the link. And then I see, like for the formatters, Eric, I struggle on this because we use design systems behind a lot of what we do. So very often I end up passing the entire node object to something else that's gonna render it. So I might have a node that has multiple views, a teaser view, a card view. And then in my view, I just pass the entire node, which is fine most of the time, but on a site that really needs performance enhancements, it pays to do the select of just the fields you need. So there are times where I need to break even my own workflow, in which case I am back into the default Drupal templates and I am overriding them, but I'm not a front-end person. So I don't have any good advice on how to override what's rendered, just what the contents of the field are. So in that renderer method, I can put anything I want in there. Can I let you know how to get a copy of the database too? You know what? Feel free to email me in case I forget, but I will upload it to GitHub because I've been doing this presentation for a while and I emailed it out to several people. I'm using Lando, so I would think just a Lando start actually, I'll put a command in there to import it, but you should be able to get up and running with just a couple of commands if you're used to using that tool chain. Let's put up that. There we go. Thank you all so much. I love this conference and I can't wait to visit the Bay Area and do it again in person, but hopefully I'll see a bunch of folks tonight. Thank you.