Drupal 8 Plugin System

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
uh we will be talking about triple-a plug-in systems um sure had some sort of speech to go along with this but i kind of forgot how how this is gonna go um anyone here using triple eight are all just kind of experimenting looked at the code a little bit okay that's pretty much the extent of what you know anyone's looked at triple a um there's plenty of stuff to do on it it's it's a pretty high alpha but it's it's not ready for production since it's very slow and not optimized for a lot of things but it's it's there and it's a great platform to start learning already so it's great so if you haven't already met me my name is uh hellier colorado uh i'm at heller at uh twitter github and drupal and a few other places and uh so yeah my interest in uh in the plug-in system has been in since even before drupal 8 um i was very infatuated with c tools and uh just its mechanics and its extendability above whatever drupal 7 or even drupal 6 did um so i fell in love with the idea of plugins and all the benefits from it uh way back when and i'm super excited that it's happening on triple a a little bit very differently um but it's but it's there so um yeah that's what i'll be uh talking about today and i mentioned this also on the uh on the actual proposal uh session proposal that there's a certain prerequisite here that at least you understand uh basics of object-oriented programming since uh it could be kind of overwhelming to try to explain that from the very beginning so uh even though uh you know this it's open to anybody it's exclusively made for nerds and i mean that for the nicest way possible because i uh i love you nerds but uh yeah this is for you so a quick overview about what we're going to be talking about um just so we have an idea and give you guys enough time to walk out in case this doesn't uh interest you is i'm going to be explaining plug-ins from a very abstract idea um just kind of see like what they are how you could identify them and uh just so we all have a visual cue of you know when i say plug-in you know exactly what i mean um and then i'll talk about why this is important why the plug-in system itself is a is a huge impact in the way you develop and even how you want to extend your own systems and uh i'll have to talk about some core concepts before we start looking at some demo codes so it makes sense to you so briefly go over that and i'll give you a heads up i'll be talking about psr0 dependency injection service containers and annotations this is all a lot of uh buzz words i guess and triple a and if you haven't uh learned what those things are yet you're in for a great surprise because i'll be able to explain them within five minutes or so uh because they're not complicated at all um i can just water it down and show you the rare essentials um and i'll do a demo on writing a few plugins just so you can get a feel about how easy it is and uh and where you can actually start doing some integration um and this is a fun part my favorite part or at least the one that i spent most of the time actually investigating is the internals of the plug-in system not just how to implement or extend existing stuff but knowing how the plug-in system works from the inside out so you can then implement your own plug-in type and manage it and do a lot of cool stuff with that um so towards the end if we have time uh i'll demo how to create a plugin type and won't be able to get into creating user interfaces for them per se because that actually gets involved with configuration entities and plug-in bags and a bunch of other stuff that's very related but still a very broad topic in itself so i'm going to try to avoid talking about configuration entities as much as possible so yeah that's the overview so first off we'll start uh what is a plug-in and uh my definition of a plug-in is a discrete class that executes an operation within the context of a given scope as a means to extend drupal's functionality so within a small given scope meaning that you're not writing a huge boatload of code just to do this one operation it's actually small bits that do a very specific function and it does it well so it's limited in scope it's not trying to achieve the whole world by doing some massive operation it's just doing a small uh snippet of information it's not also it's not assuming too much about the context in which it's being um it's being rendered if you're doing some sort of operation against the node then you'll have a context of a node but not assume other things like its taxonomies or its users associated with it or whatever page it's on so plugins themselves are really small within scope and as i briefly mentioned it's intended to do just one thing but to do it well and this is kind of like a unix philosophy where you have one utility that does this one operation which you can then send off to the next utility um and these are all specialized so there's no leakage of a of of responsibilities um so that is a part of what a plug-in is and also another thing that excites me about plugins is that they are reusable they're not just one-off implementations that you would say do inside of a theme layer where it's stuck and that implementation can't necessarily be copied on other parts of the site if it's a plug-in you could actually reuse them in different parts of your site a great example of this would be something like a field formatter and then also plugins themselves can be instantiated with configurations so even though you might have a core operation or functionality within a plugin you can kind of permutate based on like the configurations that you pass into it so you can really extend the ability of a plugin without having to do too much and also assume too much as well so to give a context as to where plugins are already existing in drupal let's kind of scan the interface and this is a drupal 8 interface and it's not too far different from what you're already familiar with but an example of a plugin would be a block everything that's listed on the right hand side right here is a block or is a plug-in in itself so that is a plug-in also field types when we're creating different ways of storing a field storing its data that is defined as a as a plug-in field widgets themselves those can be plugins field formatters right when you're you're deciding how a certain piece of data is going to be represented on the screen those themselves are plugins uh the actions that could be done on a node or on a user or whatever each one of those is a specific plugin and this could also be related to like say rules where you have conditions and you have the actions that go along with them and in core we have a subset of rules being involved in that with you know having conditions and and the action plugins themselves so that's baked in now uh image effects right all the effects that you apply onto the image um those themselves are plugins and uh here we also have input formatters or input filters uh not the formatters themselves that will kind of constitute as being a configuration entity because it's collecting all the configuration that you would set on the plugin itself and storing that as like some sort of exportable so there is a very thin line between what those two things are but the actual processing part of it these items themselves is a plug-in so that gives you a general idea and so we also have ck editor buttons so i have a bunch of these just so you get an idea at least of what exactly a plug-in is and you understand like oh that's that should be a plug-in or this should be plug-in if it's not listed here so you pretty much have uh plug-ins everywhere and this is a diagram coming from a phpstorm where we have all the different plug-in types that are extending the the base plug-in itself and the views uh has it's i mean since it's object-oriented uh you know you can have a base view plug-in which manages a whole boatload of other ones so i believe in total views has like 22 or 25 different plugins just on its own so that gives you some sort of an idea of the power behind this uh this abstraction in which you can branch out and do some pretty creative stuff so yeah plugins are everywhere um and the thing is uh with all of these items they're not unique to drupal 8 you've probably already seen them in drupal 7 obviously um so it's you know somewhat familiar to you um but how these have been implemented in the past have probably been something like a hook something info right where you're defining uh that i have a thing or some sort of extension um so each one of these plugins that i showed prior and drupal 7 there was like an equivalent four hook info um essentially what the plugin system is trying to do in aaa is get rid of all of these hook info and implementations so everything that is associated with the hook info would be within the plug-in system itself so that's uh that's something important to uh to understand um so yeah i'll talk about the benefits of plugins and why these uh matter and even if you're not interested in doing aaa development today you can still take a lot of these concepts and apply them now in drupal 7 assuming you've adopted z tools or something that is extendable by nature such as rules or the entity system or or views um so yeah uh one benefit to this is that the definitions and implementations are all in one single place and this is with aaa plugins as opposed to what you've probably been used to where you'd have a hook info in one place and then you have the actual implementation like a page callback or just some sort of procedural function being executed but in a different place altogether so you have a sort of a disassociation between these two i don't know if you could consider this kind of a spaghetti code type of deal where one isn't one item is dependent on another to exist but in aaa you can have both the implementation and the metadata that's defining it together through annotations and i'll explain what annotations are in a second um but yeah plugins themselves are lazy loaded and this is one of the things that really bothered me about drupal 7 module development is that when you have some sort of functionality that you need available globally you'd have to have it inside your modules directory or at least have some sort of a way to call it from your modules well not the module directory the module file and so you would have a lot of logic and functions uh stashed into the module itself uh you would have to store it inside of memory and even if the page request doesn't require that plugin to execute or that functionality to execute it was just a thing that you would have to have in drupal 7. but in drupal 8 all these plugins are implemented in an external file and the system is not aware of them as far as the implementation goes until you actually use them so it's better for resources also code is unified um since i'm assuming you guys are developers you probably have done stuff like implemented a field type or a field format or a field widget and you might have noticed that to implement one of those things you'd have to actually invoke several different hooks like hook field widget info and then the hook field widget form and then hook widget validator and same thing with the field types themselves you have like five or six different hooks that you'd have to implement just to talk about this one thing um so that's kind of a pain and even to maintain like multiple hook or multiple field items in a single module you'd have to have like switch statements inside your your single hook so this is kind of a a thing to to not want to tolerate as a developer but in drupal 7 or android rather all of these hooks that you would typically do are now stashed into a method inside of a class which is all unified so your one concept that you're creating whether it's a fields widget or a formatter or a type can all be nested together in a single class so it's easier to find things and you see the unity within the code um also by nature that you know things are object oriented and we're extending classes all your plugins are extendable and going back into the example with the you know creating field types or field widgets you can't necessarily borrow the functionality that drupal or any contrib module's already doing because these were all built with procedural functions it's very difficult to extend on top of that unless you had some sort of a wrapper function but then it would still be very messy to implement with classes since they're you know these classes and plugins are extending themselves you can override that class by extending it and you can override a method saying i don't want the parents method to be ran at all or i want to do something different where i'm going to alter whatever the method is doing by running it first and then doing other kind of modifications on top of it or just completely removing it all together so it's kind of like what you're familiar with any kind of altar hook but it can be used in much more sophisticated way and uh plugins are classes and in classes you can have interfaces which describe the methods that are available for a class and previously uh if you're not using interfaces at all it's kind of a guess or of some sort of a promise that a method is going to exist on on the class but there's no guarantee of it at least with using interfaces you can always guarantee that a certain method is going to exist in the plug-in so you have a sense of consistency or you can expect that a method is going to exist on a class that implements an interface so again i'm assuming you understand at least the basics of the object oriented also since we're using interfaces and we can reliably say that these classes are going to have these methods and it's all used within the same scope it's pretty safe to say that you can move these plugins around within the same scope because they all kind of operate the same deal it takes in the same input it's probably giving you the same output so these are interchangeable in a sense for instance uh an image effect right it takes in an image it processes it and it returns an image right and that is you can say the interface that you put upon that plug-in so you can swap out all the different image effects because they're all going to give you the same input output so they're interchangeable in that sense and also they are reusable um assume you have these plugins baked into a module but these this module doesn't necessarily have to be project specific and really this depends on how well you abstract the logic or you can have this plug-in exist in multiple projects so it's not just tied down to this one particular use case i'll show you some code and then you'll have the idea um yeah and uh yeah uh distinction being that a module holds in all implementation of interacting with drupal so all of your hooks um all your schemas but a plug-in is smaller in scope where it's just extending a very specific part of the drupal system so a module could contain one or more plug-ins precisely and it can contain a lot of other stuff modules have a larger like a broader responsibility so not everything is implemented plug-in but as you saw on the screenshots a lot of familiar interface components are what is a plugin it plugs into a system that is defined so okay let's step back how is it different from just a class how is it different from a class a plugin is implemented as a class right but what makes it plug in a plugin and not just a class okay a plugin is a pl a plugin is a class so the thing is though where's the plugin which was your original question assuming you had some sort of a system that takes in an image and it does stuff with it that's what the system is supposed to do the business rules you can separate or abstract out the responsibilities of that system by saying hey i have a class that is responsible only for the processing and i have a separate class that's responsible for actually writing it to file right and i have a separate class that's responsible for collecting the file right each one of those responsibilities can be separated out into a plugin system in which you can have classes that do that responsibility but those are interchangeable but overall you still have a system or a concept in which you are processing a file from beginning to end does that make sense versus just a class that has those responsibilities okay the reason why a class would be considered a plug-in is if it implements an interface that you define for a plug-in base if it's under a certain namespace right that's how you define whether a class is a plugin or a class is just a regular class exactly and the registration part is something that i'll walk through when we're defining like the internal systems correct me if i'm wrong but plugins are a subset of classes right i mean all plug-ins all plug-ins will be classes yes but not all classes are plugins no so plug into our subset of classes so they're basically a class that defines some defined standard interface yes absolutely okay that's unique all right cool so um yeah benefits of of using plug-ins and stuff and if you wanted to you can carry this into using drupal 7's uh c tools or views or anything that's really class oriented and has some sort of a concept of interfaces um so the core concepts that i'll talk about that are used within drupal 8 dependency injection service containers annotations and psr 0. we'll get into that right now um so first off psr 0 um is a it's a specification request that says hey all of the framework uh all the frameworks that sit on top of php need to have some sort of a way to understand that we have a standard of autoloading files um psr0 defines what those rules are and really it's only two rules first off is that you have a fully qualified namespace um and it should be formatted in the in the way of like vendor so our vendor would be drupal and then the name space and that could be something like the uh the path to a module or just the module and the type of plug-in class that it has and then finally just the class name itself and i'll show example of this towards the bottom the second component to this is once you've already defined what your namespace is your fully qualified namespace your file structure has to match the namespace so that way autoloaders can automatically know where the files exist just based on the name the namespace that you have in the class so here's an example and that might be a bit too small and i apologize for that but in this file we have the namespace drupal mymodule plugin block myblock that's the fully qualified name saying where this plugin or where this class exists and also where exists in the file system not necessarily just the space that we have because the name space is just a conceptual thing that's floating around in php memory um so you'll see that we're declaring the namespace that we have in code not just in the comments so it's dribble the vendor and then the namespace for this plugin my module plugin block block is a system right and then the class is called my block because we have these name spaces we can have this class be called whatever we want without having to deal with like namespace clashes with other modules that was the thing that we had to deal with with procedural functions all of our functions had to be prefixed with the name of the module so by using this namespace system we don't have to deal with that and since we're doing some autoloading we're basically leveraging that namespace and conventions that we have if you want to read more about this there's a link at the bottom phpfig.org and they list all the different psrs which are php specification requests supposedly uh psrro4 is going to be out before google yep is there any difference at all for functionality uh now is functionality what you'll notice is inside of every module directory uh there is a lib folder and inside you have a huge list of nested folders because it's basically it's com it's compliant to psr0s so if the name space is long your file structures are going to be long themselves and that kind of sucks because it's you know it's hard to get around now with psr4 the only twist here is that it will interpret dashes inside of your um or forward slashes in your namespace it will interpret those as dashes inside of your file names so that means now instead of having a huge list of directory like nested in you can have just like one huge folder well not one folder that has a long namespace with dash delimiters yeah exactly so but at least they're not nested as deeply as possible so that's the only change you'd have to deal with but functionally it's not going to change how you're dealing with name spaces internally i don't know i hope so yeah and we can thank the guy who um wrote the uh x autoload module he's been doing all the work for that so kudos to him um and here's the last part of that slide we're showing you the correlation between the namespace so here's the actual file and then where it actually exists inside of the file directory so modules my module lib dribble plug-in block etc just goes on forever so yeah that's psr0 internet so next annotations um all annotations are is metadata that's inside of a doc block and there's a distinction between actual comments and a doc block uh comments you'll see them here's a single line comment again i apologize if that's too small but that's two forward slashes will make a single line comment and then you have four slash an asterisk that would make a multi-line comment so those are comments and those are actually ignored by opcash but a doc block which you probably have already seen many times is cached by op cache um so that means it has some sort of a functional component to it it's not necessarily php code but it's just it's like you're declaring certain values you're defining an array of items or you're just declaring a name putting a description or at least saying like where classes are so it's very simple uh value or name value or key value i should say um structure here and that's all that annotations are you will see the at symbol which is a marker for saying hey this is a thing to pay attention to one you've probably seen before is at deprecated you've also seen at param at return these are all different markers but what we're doing with annotations is we're just repurposing those markers to say hey you can read this so have some sort of a parser which we use the doctrine annotation reader it reads through these doctypes and it can find metadata out of it and store that in a cache or something and this is how class implementations can have their metadata baked in together without having to have that disconnect anymore so again that's all the annotations are metadata inside of the doc blocks dependency injection um this is a pretty funny one um it's they call it inversion of control but really it takes code to really see what that means so assuming that you are defining a class some class and inside of the constructor this class depends on another class called handler and in the constructor it's defining or not defining but it's actually instantiating the handler class so it can be used within its own methods this works fine until the point where you need to override or extend that class because you need to change that handler and this can be uh it's a pain in the butt to try to do something like this because now you have to override all the methods that reference creating or instantiating this handler class what you can do instead is you could define the sum class and instead of instantiating the handler within the constructor you can just pass in the handler object to the constructor and so what happens is now the implementer of this class can define what handler they want to have and then they can instantiate the mic or sum class by passing in the handler to it and that's what what it means by inversion of control the user that's using the class gets to choose which classes are used within the other class the calling class yeah i need to find better words to articulate this but yeah essentially class instantiation happens outside of the scope of the of the class not inside of the scope and that's all that dependency injection is uh and this leads to uh service containers um as you would imagine uh having to do all the instantiation of the dependent classes uh could uh become overwhelming uh what service containers are it auto instantiates the service oriented classes which basically means a class that is usable within a global scope with all of its registered dependencies so it assumes that if you're going to instantiate a class through a service container at some point you've registered what your dependencies are so we'll look back at the example that i had prior where you're instantiating a handler so you can pass it into the sum class so that works well that's dependency injection but let's say that somewhere else we are registering and this is drupal specific not how service containers are used like in symfony but in drupal you have a mymodule.services.yaml file in which you can define what your dependencies are and you're defining basically service so here i have services my module sum class is what the name of the container is going to be i specify what the class namespace is so it's under the namespace of drupal mymodule namespace some class and i'm defining what the arguments are in this case i'm putting in a an app handler which is assuming that this handler name has been defined elsewhere but it follows the same exact model where it will define where the class is specified and so the point with this is that now instead of having to find the class names and instantiate them yourself and use like a used directive you can just instantiate it by using drupal service and then pass in the name of the service container that you have so this line will do that for you automatically and you'll see in drupal 8 that there's a lot of dependency injection being used and it could be it could be really overwhelming of how many different classes you would have to instantiate on your own had it not be for the service containers that are available for you so that's all the service containers are automating that dribble uh that dependency injection so to me it looks like you are defeating the purpose that you have this dependency in the process because in this case you are defining the angular class in a yml5 yeah you totally are if you just have that one but you can register many different service containers that have different interfaces pertaining to whatever use cases because typically when you override a class it's not a runtime decision it's like something that could be cached right so you can have different registered service containers that do exactly what you're describing so you need to create multiple service containers exactly so so what you have here in your code is just the generic word service would that be replaced by a paper service name uh so within the method right here uh that is the method that's tied to the drupal class so that is what you'd have to type in the only part uh that would be custom your own would be what you see in yellow that's string that's the name that i've given the service container the one that's registered through the yaml file wait so so my module is the service container or some classes uh mymodule.sum class is the the name of the service container it has to be namespace otherwise it'll be like namespace clashes uh okay it would be exactly what we have up there it would do that part for you so it's getting back something from a container uh it is it is is is literally doing that code up there in a separate file you see you might notice that there is um inside of the defaults directory a php folder and it's actually a bunch of php code that's been regenerated when it's looking at the yaml file all every all the service containers that you've generated it's turning that into actual php code right so there's actual php code that's implementing that top line for you so it's literally doing that for you when you call that function okay i'm still not clear on how to pick out the container versus the instance that's going to be provided by the container oh okay um so i'll talk okay the link at the bottom shows you how like the service container is supposed to function and literally a service container is just an array of uh anonymous functions inside of those anonymous functions is where it's popping in that that code itself where it pulls in the class is from itself it's a recursive thing because the service container has reference to each one of the independent classes the my or the sum class that i defined and also the handler so it can instantiate it all within itself and inside of that anonymous function it's pulling it from itself because it has its own like protected access to that class it instantiates it one time and it just passes it along by reference or by property rather so yeah um the the link underneath it it describes exactly how service containers work and so you have a better idea but as far as actually implementing in drupal 8 this is the only thing that you'd have to do anyway frankie yeah okay all right so a quick review um talked about what plug-ins are um and also why they're uh why they're important what the benefits are um and we talked a bit about the the core concepts um so essentially that is all you need to prepare yourself for drupal 8 model development uh so we'll go into some demos right now um hop in over here get this out of the way all right cool so i'm gonna swipe this over and show you the directory structure of my module okay so this is a pilot module um it's basically where i'm doing all of my i guess uh trying out drupal 8 stuff and it's on github and there'll be a link available for you um so you can check it out but what i will be implementing here is a block right previously in drupal 7 you would have to implement a hook block info and hook block view and hook block whatever inside of the module file you don't have to do that anymore all you need to do is put in a file in the right place and that place is under lib inside the module directory the fully qualified name space since we're doing the whole psr zero so that's gonna be drupal pilots plugin block and then just throw in my class i'm calling it snowman that's all since we have this place in this particular file structure i have to make sure that my namespace is corresponding with that so i have here and i'll go ahead and bump this up since i can control this bam right cool so i'm putting in the name space that uh that is associated with the file path that i have and here i have class snowman extends block base and i'm just going to be undoing text so you can see what's uh what's what's happening here uh here i am providing uh some annotations basically it's just a document block but this part is important so that way the annotation reader whatever is going on finding where all the plugins are knows that hey this is pertaining to the block plugin and i use that by using block and i'm defining what my id is for this plugin and i'm putting in an administrative label and you can put in a couple other things like a category or whatever the plugin type wants right this is essentially the hook block info that you would have done but it's all nested within the document block type to also go in on this annotation so this is replacing code block info yes and so cache type will go in on the annotation oh yeah it totally could yes they're totally good all right um one uh distinction as well since uh i'm using blockblade blockbase here um but you know as far as this class is concerned it doesn't know where it is i have to call it in by using the use directive so this lets autoloader pull it in for me so now we have the context for it that's just a requirement here and that's why you see a lot of use directives inside of the php files in aaa next snippet to this is uh just implementing the build method so i mentioned uh that uh all these plugins have an interface to it uh build is one of those and i'll go ahead and uh pop this uh pop that file open lock base oh i don't have an entire project i just have that one project yeah it's not important yeah we'll skip that part so anyway um there's an interface to block base and one of the methods is built so build is a required thing to be uh invoked within this plugin and all i'm doing is returning uh basically just a a render array of some markup and this markup is just saying as little snowman on it no i have to do this oh yeah now i can now i can do this so basically i'm just popping in the snowman uh character unicode character and uh and that's the class that's all i have to do i can probably pop into my servers okay we'll not do that i've had to install triple-a many times because uh sometimes it gets broken uh all i was going to show you was uh that this block now exists within the blocking listing page but really that detail is not too important um all i need to know is uh this code itself was all that is necessary to build a block so think of the contrast between what we used to do in triple seven which is invoke a lot of uh hooks just to do this one little functionality so uh next i'll jump into uh an image effect and you know this is a yeah it's too bad so um in this image effect i'll go ahead and show you the name space for it this is also within library drupal pilot plugin but now it's under image effect um this is processing an image and i should probably reinstall dribble to to demo this but basically it pulls in an image and it pixelizes it and you can determine how many you know how thick you want the pixel uh blocks to be itself whether you want to use advanced rendering for the for the pixelization um so here's the code for it again we're just defining our namespace that's associated with our file structure class pixel extends image effect base here i'm specifying that hey this also has configuration so this plugin this class is implementing this interface so you should expect some uh some some methods in here like build form you know here's my configuration default or whatever so here are my annotations basically i'm defining hey this class is an image effect plugin here's the id label description and i'm using all my use directives because i'm calling out these uh classes by name here so i have to reference them there so they're pulled in by autoloader and then here because of the config configurable image effect interface i'm providing a getform method so the plugin type system would know hey this plugin implements this interface so this has a configuration let's run through that avenue and provide some uh some routers for that sorry uh uh just for a second um so you're you're extending uh the image back base i see that you can figure out the image effect interface uh image interface did you did you know that you needed that because you pulled it out of the uh configurable image effect interface as if i um so i noticed that when it wasn't there white screen so that was apparently a requirement to pull it in um i'm not exactly sure what the requirement is there as far as php goes if the parent class already has that included then it's fine but if it doesn't that means i have to do it itself so yeah it's a caveat in there um and that's yeah that's the thing that bugged me about doing this kind of development because i'm not always sure when i have to put in the name space sometimes so yeah those are um so here on my method get form um basically just sending in the form array i also have a default configuration method that's providing the size and whether or not it's boolean value for advanced settings and then ultimately at the end all i'm doing is the apply effect which again is part of the interface for the image effect this is the only thing that it expects to have as far as processing and i'm passing it through image filter um i'm grabbing the image context that i have because it's all the context that i have i'm saying to use this filter image filter pixl8 and passing in the configurations of how big i want these things to be pixelized and whether i'm going to use the advanced rendering method or not so yeah and so here's another image effect it's inside the same directory but the spin on this is that this plugin itself is not a singular plugin but it also cues in to create all kinds of plugin derivatives meaning that i can have some sort of a way to figure out when to use this plugin over and over again based on a slightly different context based on configuration so i'll talk about that in a second here is my annotation block so image effect and i'm saying hey there's a derivative in here look for that class and i'll show you that class in a second and i'll go ahead and pull out all the code that's in here basically i'm applying an effect and that effect is image filter colorized but i'm getting the configuration that i would pop into like say for instance if i'm going to change the hue of this image uh what are my rgb values i'm pulling that out from a from a plug-in definition what essentially would have been inside of this uh this annotation right this itself is the plug-in definition uh with derivatives i'm able to uh derive different plug-in types or or plug-ins themselves that have those values inside their annotations so when it's constructing the list of plugins that are available it's going to look at this derivative class which is right here and in a sense get derivative definitions is just managing a kind of a for each loop right you typically see a for each loop inside of your hook info file to do something like this but now that's actually abstracted into its own class and in here i'm actually just pulling in a list of values from a function but this could itself been from like the database or it could have came from some sort of external service or whatever it's just a list of of colors that i'm using to manipulate an image and i'm just doing a 4-h for that and i'm providing the values for the annotations that would have been within this plugin so basically the the definition of this plugin so here's my label colorize to the name of the color that's inside of the the list of effects that i have and then also what colors am i pulling in and that itself is an array value within uh the the colors that i provide from pilot colorized colors um and so with this i'm able to now have three plugins or however many i have defined within this pilot colorize colors all coming from the same source plug-in this one plug-in so this one plug-in is able to represent three or four or whatever many number of of plugins that you have hopefully that makes sense um my triple install is just widescreen so i would have loved to show you that um but i'm not uh because uh yeah drupal is not installed yeah um any questions so far because if there is that would give me some time to actually any questions no questions ah this probably take way too long forget it i mean wait a minute yeah yeah uh time's pressing i better not i have to go over a lot of other stuff okay method there yeah derivatives is uh is used it can be used uh with with any plug-in type um the derivatives is actually a decorator which we'll get into when i talk about the uh the the the uh the internals of the plug-in system so um for now this concludes the demo um i should have installed drip beforehand but yeah for now this concludes the demo with actually creating a plug-in that extends an existing plug-in type so to review this part basically we're just putting in a file and a specific file structure and making sure that that plug-in is within a specific name space so what i'm defining here and the only thing you really need to pay attention to is either other plugins that are already implementing this plugin type and see what methods they are providing in which you just extend that class if it's an image if there's a base class associated with it and then provide whatever methods uh the interface is expecting so in a nut that's every plug-in that exists in drupal yeah so a derivative is you've probably done before a for each loop inside of an info hook right um yeah so by doing an in a 4-h loop inside of the info hook you could essentially have the same effect here by using derivatives a derivative class in this scope whereas it's using the same base plug-in definition here so it's going to use the same class but other attributes will spawn out of the derivative class such as the label and the colors right my derivative class is only appending a new label and a new uh plug-in definition for for colors and it's being used within uh this within this one class i'm kind of related to concepts from other languages oh this is this is a separate yeah it's not like object-oriented specific this is a triple-ism okay so driven from something implies yeah so right right here in the derivative class we are defining a method get derivative definition and in here you can define how these things are derived i'm just using a a function that has some simple values which just return an array right but this could have also came from the database right for instance uh one example that's used a lot is uh all the different menus that you have in dribble say i wanted to have a block for every different menu that existed i have a single class that implements the block that would put in some sort of a menu but i have a derivative class that says look to the database find all the different menu types get the name and pop that in as if it was its own plugin right but it's still using that one class to actually implement the plugin that it provides okay so is it basically it's kind of like a copy paste think of that it's copying this implementation or rather this implementation and it's it's it's pasting it into a different plugin type presumably but has slightly different properties to it so does that get generated into the php directory as actual code basically based on stuff no it's actually happening yeah yeah um yeah it's our runtime and the only thing that's really runtime is the collection of the plugin definitions not necessarily as it's invoking this uh so and in this so so far we haven't discussed doing any kind of hooks at all uh yeah so the info hooks at this place uh at this point have been replaced but you will still have an opportunity to do some sort of an in-house an info hook alter or a hook info alter and again that is a sort of a sort of a decorator and yeah we'll get to that in a second but there's no info hooks that are in place so far in this at this scope all right so that didn't work out um okay so here's here's the internals of the plug-in system um and there's four plug-in components um that needed to be talked about the plug-in manager a discovery class a factory class and a mapper uh those are four fundamental concepts within the entire plug-in system and understanding this would get you really far so first off we'll talk about the discovery the job of the discovery is basically to go out into the drupal code and find out where these plugins exist it doesn't care how those definitions are actually represented it just cares that it's looking for any kind of plugin then we also have a factory in factory all it's doing is instantiating uh a plug-in so its methods are create instance and that's all it does uh whereas the discovery it's looking oh it's it's implementing get definitions and get definition singular so that's the scope of what discovery is doing and then the mapper is actually something that could be skipped assuming that you already knew exactly what class you wanted to call but a mapper's job is to figure out what instance it should get so there's use cases for this and i'll talk about what those are and ultimately what a plug-in manager does is it's implementing each one of these interfaces so in a sense a plug-in manager is a mapper is a discovery and is a factory class of its own but the thing is that the plugin manager itself doesn't implement these functions directly what it does is it stores a reference of dedicated classes for discovery or dedicated classes for the factory and it kind of just delegates the task so for instance if i have a call on the manager for get definitions really what's happening internally is it's saying this function get my discovery object and then have it do the get definitions and so that lets the manager figure out what kind of discovery method do i want to use and how many do i want to use so we'll look into the discovery classes um these are the the base classes that are provided by triple these are all different mechanisms for discovering uh classes uh we have a yaml discovery which basically means it looks out for all the yaml files of a certain type that you request and it pulls in definitions from that because it's simple to just have a name a label a reference to a class coming from yaml so you can do that you could also use annotations which is pretty much the the predominant way of finding classes within drupal which you've already seen through the doc blocks you could have hook discovery so instead of just uh having these come from annotations you can just still have a hook whatever info um and the the plug-in system will still be able to retrieve those and there's also a static discovery which just means that you're you're literally telling it where the plugins are and this is used for testing but there's still some sort of clever ways of using them within your own system where it's not discovering anything you're literally just handing it which plug-ins are available to you uh so here is an uh what it looks like to i guess instantiate uh within the plugin manager that you're creating um this would be for the annotation class discovery um so i guess this is the part of the slides where i'll talk about what each one of those are but so inside of the annotations basically it's just the doc blocks that are on top of the classes um and i should say inside of the the annotation class discovery uh one of the requirements are first uh what namespace am i looking for so you would give it the namespace plugin actions if you wanted to collect all the plugins for actions uh you provided the namespaces which just say which are the available modules that are you know enabled at the time and uh also uh the reference to drupal core annotations actions is saying what marker am i looking for so i mentioned at deprecated or you've seen at base or app or at image effects each one of those is a marker uh the drupal core annotation action is a is an implementation of defining your own marker so for instance if you had your own plugin type called a thermometer uh you can have an at thermometer uh doc block marker and that would be basically just extending the annotation plug-in class itself uh so that's what's required for the annotation class discovery um hook discovery as you'd expect all need to know is what's the name of the hook and internally it's going to call the hook for that so in this case i'm passing in elements info and what i'd expected to invoke is hook element info the yaml discovery all i need to know is what's the name of the file that i'm looking for in some cases it's going to be local actions.yaml or it could be routing.yaml or whatever the case um and all it needs to know is what are all the module directories that exist so everything that's enabled at the time and static discovery you're just passing in directly what those plugins are so on top of the original discovery you have something a concept of decorator and this is something you're probably already used to the ultra hooks right you already have a set list the plugin definitions or whatever kind of definitions and you have another like function that's altering it so there's a discovery class for that um and also the derivatives is another way of decorating the existing uh plugins that you have by finding all of its derivatives and adding more stuff to that stack of plugins so i'll talk about each one of those here so derivative discovery um the only thing that these classes need to know is the existing uh plugins uh or yeah the existing plugins that have been defined so these are all definitions that i'm passing to it and within those definitions it will scan to see if there's a derivative id in there and i'll go ahead and find that class and figure out what derivatives exist there alter decorator this works very similar to the info hook it's basically going to call hook action info alter and pass in the definitions that you've already found and return whatever you get from there so you still have access to halter hooks there a process decorator would be say you've collected all the definitions but you want to do a certain level of massaging to the data and all you're defining is what callback is going to handle doing that massaging of data so there's your your opportunity to do some processing and one that is interesting is a cache decorator which basically is a proxy for implementing uh cash within your plug-in system so you don't have to go out and fetch all the definitions all over again the decorator will say hey first look at the cache and if there's nothing in the cache then go ahead and look through all the discovery definitions that can be found through annotations or hooks or whatever the case might be and then cache that so all the decorator is serving is basically a way of like a cache proxy so yeah that's the decorator there so internet those are the discovery it's just means of collecting the information next we have factory classes and there's a few of them here um it's basically just doing the create instance and the thing that's interesting about this is that when you're creating an instance there's only two possible things that you can do first is invoke the class and then handle the arguments so there's really not too much depth with extending a factory class and what we have in the default factory is it's basically just calling in the plug-in class that it finds within your plug-in definition and passing in the set number of parameters or arguments to the plugin definitions every plugin by default will be accepting a configuration its plugin id and a plugin definition so that's the default standard an extension of the default factory would be a container factory just like i've talked about service containers as an alternative method of just instantiating your classes you can leverage the same power here by using the container factory so it's going to call essentially a method inside of the the manager to create the the service container and just still pass in the same arguments so the only derivation here would be how it's instantiating the class and it leaves the arguments intact uh within the widget factory um the widget plug-in type decided that i wanted to have different arguments passed into the plug-in so all it's doing here is passing in custom arguments so it's it's figuring out uh the config field definitions is going to be the third argument and then the config settings is going to be the fourth argument for that plug-in so if you want to do something custom like that for your plug-in types you can do it by copying essentially what the widget factor is doing and finally we have the reflected reflection factory which does something very interesting it's handling a dynamic uh i guess argument setter for your class it uses a reflection class to introspect on the class that is the plugin and it determines how many different parameters is used within its constructor and it maps that with whatever configuration is being passed in to the factory class and so it's dynamically adding in the plugins and this is actually really useful for something like autocomplete because if you ever implemented some sort of autocomplete functionality you could have an n number of arguments being passed in to figure out what kind of autocomplete values you can be pulling in so this is used with um uh the uh the relation module or not the relation module the reference module the entity reference module within drupal core so yeah and uh finally the mapper classes uh this is the last part so um i mentioned that you can you can skip this part because if you already know what class you're going to call you can just go directly to the factory and say hey get me this class but the idea with the mapper class is uh for some instances where you don't necessarily know what plugin is appropriate for certain processing so here's where it starts inside of the the method signature all it's asking for is a set of options and it's not asking for the name of the class what it does internally is it takes that options class and it goes inside the logic for the mapper class and it figures out exactly what kind of method or what sorry what kind of plugin should be used for the certain context so i'll give an example say you are processing a compressed file the only context you have here is that the file name is like uh archive.tar.gz and your your mapper class is supposed to figure out what class is appropriate to untard this or unpackage this based on the file extension it's going to say okay i know this is supposed to be a tar gz so i'm going to use the plugin for tar gz and then i'm going to pass that to the factory class right so it's basically it's a wrapper to the the the factory uh the factory class itself to create the instance another example of this would be say for instance you're doing a content negotiation for a rest server right a client says give me this resource like a user five but i accept content types application json or i accept application xml right when the the server receives that request it needs to figure out what plug-in is going to implement that serialization and it uses that by finding the options oh it's asking for application json so i'm going to figure out what class is the one that implements the application json serializer and it'll pass that to the factory so that's the way of with logic figuring out what class is appropriate for whatever kind of context in many cases you don't need this but there's some instances like the use cases i provided that you know you actually do so yeah that's uh that's all the internals of the the plugin system and so with the remaining three minutes um i will show you how actually create a plugin manager of your own um in this case i'm creating a person plugin manager which is extending the default plugin manager which extends the plugin manager base and all those methods provider just the all the necessary logic to do the uh the delegation that was mentioned earlier where the plugin manager is really just calling a a dedicated class to do the discovery to do the mapping or to do the factory uh so i'm gonna reduce the size here and i'm not going to walk through all the code but basically we start off here with a plugin manager and this plugin manager can be listed anywhere within your library you define what name space you want to use my name space is going to be drooped by the plugin and right here i am extending the default plugin manager inside of the constructor i'm saying further discovery i want to use annotations and i also want to have a container derivative discovery decorator meaning that i'm going to support any kind of derivatives and then finally for the factory i'm just going to have the default container factory and that's it everything else as far as discovery in factory is now handled by those dedicated classes i don't even have to extend those um and i'm also providing an opportunity to alter whatever plug-in definitions i find by you know calling pilot or hook pilot person info alter and i'm setting the cache back end to uh well here's going to be my uh my my cid my cache identifier which is going to be the the language so english and then pilot person plugins um these helper functions itself is basically calling other discoveries internally and it's setting that cache for me and you can look at the default plugin manager to see exactly what it's doing but it's abstracted in such a way that when you wanted to create your own plugin type this is the only thing that you'd have to do so as far as the actual plugins go we have to define what kind of default properties that i would have this is where i would extend the annotations so there's something for this as well inside of my library pilot annotations person and the only thing i'm doing here is extending the plugin for annotations and so now you can find a marker just like this on all the plugins that exist and plugs that i'm looking for are the ones that say at person the defaults that i'm expecting are an id a name and an age and i can provide some defaults to here in case the plug-in doesn't provide those those those values so i'm always guaranteed to have those plug-in definitions in place so now that i have that plug-in manager and the annotation i can say okay in my plug-in here's my interface i'm expecting each plugin to implement a get name a get age and a greeting that says hello world or whatever the case might be and i apologize this is a really stupid example um but the interface is saying i require these methods and i have a plug-in base just for convenience that implements that interface and it's doing get name it's grabbing it from the definition get age it's grabbing that from the definition greeting hello my name is the name and then the age so that is the base plugin um and so yeah that's a base plugin uh so when you call something like i'll do some pseudo code manager equals uh drupal service and i one thing i forgot to mention my plugin manager is registered with the service containers so if you look right here at the pilot services yaml here is the name of my service container i'm saying what class i need to instantiate and as far as the arguments go just look at the the the the parent that i have so it's going to pull in all of its arguments and i don't even have to define that when i want to call that right here with the service container i'm instantiating that plugin type now if i wanted to create a user interface and this is where you can get creative i can do something like this manager let's see this uh plugins equal manager get definitions and just like that i can go ahead and do a forum with the options where i can create a table listing of all the different plugins that exist right so that's how you would create an interface just by grabbing all the definitions and the life cycle up here is basically just pulling in it's instantiating all the discovery classes and grabbing all the yaml files or the annotations or the alter hooks whatever the case might be but at the very end of it i have an array full of all the plugin definitions now to actually use each plugin say for instance uh through this list of plugins it had discovered and here are my plugins for person or my persons uh here i have two plugins for that person plugin i'm not really doing anything within the class but all i'm providing is uh the plugin definitions bob age 26 and then dave age 42 and he's overwriting the greeting to say something different since i now know through pulling in all the definitions i now know exactly what name of the plugin idea is i can do something like this manager get inst or create instance bob and so now i actually instantiated the plugin that i just created so now within the system whatever it is that you need to design you can pull in this plugin by just calling it out by name or if you had you know used the mapper itself you can let that logic figure out what class to call in and then at this point i can do something like uh plugin greeting and it's going to go ahead and say hello world or whatever kind of operation you had and that's it so this could be wrapped around any kind of fancy user interface um and you totally could do that but this is the bare bone minimum life cycle of what it is to have a manager type instantiate it and then call it that's it um so final thoughts no time for final thoughts um so thank it was you
Info
Channel: Helior Colorado
Views: 8,156
Rating: 4.830986 out of 5
Keywords: drupal, drupal 8, plugin system
Id: 2o5uY-iOoMo
Channel Id: undefined
Length: 64min 1sec (3841 seconds)
Published: Mon Jan 27 2014
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.