Table of Contents
Video 11: Code Generators, Part 1: Setting Up/Creating a Recommendation Module
The eleventh VuFind® instructional video explains how to set up a local code module and use VuFind®'s code generation tools to build a custom Recommendation module.
This is a raw machine-generated transcript; it will be cleaned up as time permits.
welcome to the 11th viewfinder tutorial video uh this month we are going to look start looking at code generators uh and talk about how to manage local custom code and how to build a simple recommendation module using a code generator so in past videos we've talked about two ways in which you can customize your viewfind instance one by maintaining a local configuration directory for your config files and another by creating a local theme so the third way is to create a local code module where you can extend override or add to viewfinds code so before we start creating a code module let me just talk a little bit about code modules in general so if i go to the viewfind home directory and i look at the files that are here uh there's a directory called module and if i look in there i see that there are a number of subdirectories you find you find api you find admin etc uh each of these directories is a lominos code module laminas has a module manager which allows you to organize your code into modules each of which is bundled with a bit of configuration uh and a laminas application can be built by composing together a number of these modules uh to get all of the functionality into one place and the neat thing about the code plus configuration design here is that you can add a module of your own and load it last and then the configuration of your module will override equivalent configuration in any of the earlier modules thus you can build your own module for viewfind put your code and configuration in there and this gives you the power to add to or replace any of the core functionality the modules that come with viewfind bundle different parts of its functionality the majority of the application is in the main viewfinder module some more specialized pieces have been broken out into separate chunks and it's entirely possible that over time the code will become more modular as we split it up into more logical pieces but that's an ongoing process in any case um to get started let's create a local module where we can begin to work and when i create it i'll also show you what it looks like so you can have a better understanding of how these modules are structured fortunately if you want a module viewfinder's install script gives you an option to create one so depending on how you set up your viewfind instance you might actually already have one but if you don't don't worry you can just run the installer again keep all of your options the same except add a module and that'll do the job for you so that's exactly what i'm going to do right now the tutorial viewfinder instance we've been working with was installed from a debian package which does not create a local code module by default so i'm just going to from my viewfind home directory run php phpinstall.php to re-run the installer and this will give me the option to create a module i just have to be sure that all of the other choices i make here are the same as the choices that were made the last time the installer was run so it asks me where i want my local settings of viewfind local that's fine i want to keep the default now it asks me what module name i want to use and this is where i need to make a decision your module name can be any legal php namespace which means practically anything you just want to be careful that you don't choose a name that either conflicts with an internal php command or conflicts with an existing viewfind module or any of ufind's dependencies usually if you name it for say your local institution you'll be very safe for the purposes of the tutorial though i'm just going to call my local module tutorial so now it asks me what path to use in viewfind's url i want to keep that the same slash viewfind and i'm done so all that the installer has done is it's created the empty local module for me and it has updated my apache configuration to include a setting to load that module so since we got viewfind under git control last week or rather last month i can do git status to see what the installer actually did and as i can see it has created a module tutorial directory and it's modified two files my environment.that file which i don't care about because that only is used under windows and my local httpd viewfinder.com my apache configuration so let me do a quick get diff to inspect exactly what has changed so first of all what happened in the environment that which again doesn't matter because we're not in windows but if we were in windows we would care that it has added an environment variable uh pointing to tutorial the more important changes are in the apache configuration where you can see that it has uncommented the setting to set up the viewfind local modules environment variable and set it to tutorial so this is what tells viewfind which module code to load there's also this small change up here where a comment has changed and that's simply a side effect of having upgraded viewfind to a newer version that no longer mentions zen framework a harmless side effect of re-running the installer so if i had made any mistakes when running the install.php script i would see other unwanted changes in here like changes to viewfind's directory path uh so if that had happened i could have done a get reset to back out the changes and i could try again but in this case i've gotten it right the first time so i am just going to commit these changes so that i have all of this going forward so i'm going to do a gift status again to see what i'm working with i'm going to git add the changes to my env.bat my local http viewfinder.com and i'm going to add my new tutorial module i'm also going to delete this httpd viewfinder.com.back.timestamp file that's a backup that the install script made of my apache configuration which is a really nice safety mechanism to have if you're not using git but since we're using git to manage our changes in our our versioning we really don't need to keep backup files around we can revert using git if we really have to so i'm going to commit add tutorial module there are a couple more things that i also am going to need to do to fully prepare this module first of all because i've changed my apache configuration i need to restart apache to load the new configuration so i can do that with sudo system ctl restart apache 2 and i'm just going to go over to my web browser and refresh my viewfinder to confirm that it still works i haven't broken everything by adding my new module that's good the other thing i want to do is be sure that i have my module defined as an environment variable at the command line because if i use viewfind command line utilities i want those to have access to my custom code as well so i do this by editing the etc.profile.d viewfind.sh script and i just add to that export find local modules equals tutorial uh then i'll reload my profile with source slash etc profile and now if i echo dollar viewfind underscore local underscore modules i get tutorials so my environment variables are all set up and i'm ready to begin working with this module but before i add anything to it let's take a quick look at what's actually in there and i'll actually open up vs code to do that because i think it will be easier to look at in a code editor all right so here i am in vs code and if i look under my module directory i see all of viewfind's core modules as well as the tutorial module i've created if i expand that out i see we have a few things here we have a module.php file every laminas module contains a module.php which is used for loading the module and if we look at this file there's not a whole lot in here uh it loads the configuration file for us it sets up an autoloader so that the rest of the framework knows where to find the code that lives in the module and it has a couple of empty hook methods where you could theoretically add actions that occur when the module initializes or bootstraps but you rarely need to do that so this is a file you shouldn't have to touch much it's just part of the setup of the module then under config there's a module.config.php file containing the modules configuration right now this is simply an empty array uh because we haven't put anything in here yet but we could in theory uh copy configuration from other modules and change it and it would be overridden because our tutorial module is going to load last which makes it take precedence over the things that are loaded before it and finally we have a source slash tutorial directory and this is where all of our code will live but we haven't added any code to the module yet so right now it's simply an empty directory thus uh we need to add some code and viewfind contains several different code generator tools that can be used to add code to a local module today we're going to look specifically at the plug-in generator which is what you use when you want to create a new plugin of an existing type so to run the plug-in generator uh you simply run php viewfind home slash public slash index.php space generate space plug-in and then some parameters but with any of you finds command line tools if you don't know what the parameters are you can add minus minus help and you'll get a usage screen so this shows us that we can provide the plug-in generator with the name of the class we want it to generate and optionally the name of the factory that we want to use to instantiate our class when viewfind builds it if you don't specify a factory the generator will actually create a new factory for you that will generate an instance of your class there's also an optional top level switch that you can use if you want to create a plugin in viewfind's top level service manager otherwise the generator is going to look at the class name you provided it and try to figure out what kind of plugin you're generating based on the name space and then put it in the appropriate context so as i mentioned at the beginning what we're going to generate today is a recommendation module we talked about recommendation modules quite a few videos ago when we were talking about search configuration so recommendation modules can be turned on in the ini files and they provide additional information usually above or to the side of search results today what i'm going to do is create a recommendation module that will display a message at the top of the combined search screen so this shows you how if you wanted to put a welcome message or some kind of additional local information you could easily add it to that top level screen so i'm going to call this recommendation module tutorial recommend combined notes so we'll go back to this generate command and we will say php if you find home public index.php generate plugin tutorial backslash backslash recommend backslash backslash combined notes i'm not going to specify a factory so this will allow it to generate one for us and a couple of notes about what i've done here so i want to create a class called tutorial recommend combined notes each part of this is important the tutorial the beginning of the name the first part of the namespace tells the viewfinder and the generator what module this belongs to everything in the tutorial namespace needs to live inside the tutorial module and all code in the tutorial module needs to belong to the tutorial namespace the next part is recommend and all of our recommendation modules have the second part of their namespace set to recommend so most of you finds core recommendation modules have classes that are named viewfind backslash recommend backslash something so we're following that same pattern we're just using our tutorial module and of course combine notes is the name of the actual class we want to create within this namespace the reason i have to double up my backslashes is because backslashes are how fully qualified php class names uh represent the parts of or connect together the parts of a namespace but backslashes have a special meaning on the linux command line so i have to double them up so that they get passed through correctly to the php code if i only put single backslashes here weird stuff would happen so that's a common gotcha be sure when you're providing class names to generators that you double up all the backslashes anyway now i can run my command and i see some messages it created my combined notes class it created a combined nodes factory it backed up my module configuration and then it updated the module configuration so if i do a git status now i see that some files have been added to my module tutorial source directory and my module configuration has been modified i'm going to switch back over to vs code to just show you uh what we've got so as you can see my module configuration has gotten some stuff added to it there's now a plugin managers array inside of you find array and it contains a recommend section which is where recommendation modules are defined there's a factories element which specifies that this new tutorial recommend combined notes class that i created should be built by the combined notes factory additionally there's an alias set up so that when i configure in my configuration files a recommendation module named combined notes it knows that that maps to uh the combined notes class that we've just created and going into depth on how this whole service manager configuration works would be a good topic for a future video but for now uh just take for granted that the generator configured this all correctly for you so that you don't have to worry about it you can just fill in this class and this factory and use this alias name in your configuration files so let's look at the source that was generated first of all here's our combinednotes.php as you can see it has the right tutorial recommend namespace on it it's named combined notes it implements the appropriate interface for a recommendation module which the generator figured out based on the namespace we told it to use when we specified our class name but there's nothing in here we're going to have to write this class we'll get there in a moment but first let's take a look at the factory that was created and all this does is return an instance of the requested service which based on the way the service manager works is always going to be the name of the class that we want to build which would be tutorial recommend combined notes uh based on what we're doing today there is actually no reason to build a special factory to generate this i just wanted to demonstrate that it's possible so that if you're building something with dependencies you have this class available to to do some dependency injection within but for today's demo we're just going to leave this at the default and not worry about it any further so because our combined notes class implements an interface we're not going to be able to use it until we've defined all of the methods that the interface requires so let's talk a little bit about the recommendation module interface and how it works so in vs code i can take advantage of the fact that i can right click on well normally all right that's not working so i need to look up this viewfind recommend recommend interface to see exactly what methods i have to define in my local class and as i mentioned our namespace tells us what module our code lives in so i know that this is defined in the meme you find module under the source directory so here's the view find namespace recommend and recommend interface so a php interface just defines which methods a class needs to define i'm just going to copy and paste all of these method definitions from the interface into my new combined notes class and for starters i'm going to just make them all empty methods that don't do anything there are three methods that recommendation modules have to define the first is set config which receives extra configuration settings that the user defines in the any file and makes use of them so we can leave this empty if we don't have any configuration then there's an init method which gets called before viewfind runs a search but after the user's search request has been processed so if we want a recommendation module to analyze user input or actually make changes to the request that we're going to make we can do that here this is not done very often but for example the side facets recommend module uses the init method to turn on the facet values that the user has configured for today's example we're not going to be touching init it and finally there's a process method which gets called after the search has been performed and which can be used to extract information from the search results if we want to use that to impact the message that we ultimately display to the user but it's perfectly valid to leave all of this empty uh and then you just have a recommendation module that doesn't do anything except display a template so let's uh let's turn this on and just see what happens and spoiler alert it's not going to work yet because we haven't made a template but i just want to demonstrate what this all looks like so i'm going to go into my local config directory into combined.ini because i want to display this recommendation module at the top of my combined search results and at the bottom of combine.ini there's a recommendation module section that lets me turn these things on so i'm going to put it on in the top the name uh as you recall from the alias that was generated in my configuration is called combined notes so just by adding this top equals combined notes to the recommendation modules list in combined.ini i should now have turned on my new recommendation module and it should appear at the top of my combined search results when i do a search except we get an error because i haven't created a template yet but as you can see what i'm looking at here is the generic please contact us for help message which doesn't really tell us anything about what went wrong and that's because by default a viewfinder runs in production mode which does not display detailed error messages because we don't want to accidentally display something that exposes a security vulnerability to the general public however when we're actually working on developing viewfind on a development server it's often helpful to turn on development mode which will display richer error messages when things like this go wrong let me quickly show you how to turn on development mode if i edit the apache configuration in local http viewfinder.com and scroll down a little bit there's a commented outline that says set end viewfinden development if i uncomment that and then restart apache now viewfinder is in development mode and if i refresh this page i get a much more useful message which is actually telling me that i didn't set up my recommendation module correctly i have a suspicion that i didn't hit save after i made my changes over here sure enough that's what happened so it was originally complaining because i hadn't saved my method definitions and so i wasn't conforming to the interface i've now gone back saved that and fixed it so if i refresh again i should get the next error message which is now that it's looking for a combined notes template so that it can display the recommendations but i haven't created that yet can't find it therefore it can't display recommendations and it throws an error and so this points out that by convention every recommendation module needs to have a phtml file in the recommend template directory of your theme whose name corresponds with the name of the class uh any viewfind plugin that displays something follows this convention of having a template directory containing phtml files whose names correspond with class names and if you look in the plugins page of the viewfind wiki you'll find all of the plugin types you can build as well as notes on these types of conventions on a plug-in by plug-in basis but for now the important thing is in order to proceed we need to create this template file and because i don't want to change core viewfind code we should turn on our custom theme again so that we can make our custom files in our custom theme so i'm just going to bring up my config.ini file in my local configuration directory and i'm going to comment out the bootprint 3 theme and uncomment the tutorial theme that we set up a few months ago and now inside my themes directory inside my tutorial theme inside its templates directory i'm going to create a new folder called recommend and inside that folder i'm going to create a new template file called combinednotes.phtml and inside that file i'm just going to put some html you are looking at combined search results and i will actually save that this time so now if i go back to my browser and i refresh the page again everything is working my custom theme is turned on which is why things look different and my recommendation module has loaded and rendered right above my search results this would be a really good time to go back to the command line and commit all of this stuff that i have just done so i'm going to git add module tutorial source i'm going to get add themes tutorial templates and i'm going to commit that as create combined notes recommendation module i can also get add my httpd viewfind comp where i turned on development mode let's say turned on development mode and for now i'm not going to commit my configuration changes uh we'll see if we want to keep things this way after we've done a little bit more work so at this point i've already demonstrated how to set up a local module how to use a plug-in generator to create a plug-in within that module how to turn that on and how to fill it in enough that it will work um but let's do a little bit more advanced work and talk a little bit more about recommendation modules while we have this opportunity because it's great that we can display this template in this place but it would be more fun to see how all the different pieces work together so let's start by adding a configuration setting so that if we want to change the name of our combined search results we can set that in the ini file as i mentioned earlier the set config method of a recommendation module uh allows you to receive configuration settings that come from uh the ini so if i open up my combined notes let's create a protected property here called name and then in set config let's just pass the settings we receive into that name property and let's create a new public method called get name which simply returns that name property that i just set up so now if i go to combined ini i can add parameters to a recommendation module by just putting in a colon and then adding stuff after the colon so let's say the tutorial results put this whole thing in double quotes since it has spaces in it we want to be sure the configuration parses correctly so at this point the tutorial results will get passed to the set config method then it will get stored in the this name property and it will be available for retrieval through this public get name method and of course the place where we want to retrieve it is in our template which by convention is going to have access to our combined notes class using the name this arrow recommend so what i can do is say this escape html because we want to be sure that we don't misrender special characters in our text this recommend get name and i will delete combined search results and actually just for completeness i'm going to go back and make my set config a little bit smarter say if the settings are empty we'll call it combine search results otherwise we'll use the settings so this way it has a reasonable default but it can be overwritten so again the flow i put a parameter on the recommendation module configuration in my any file it passes to set config which stores it my class exposes the stored property through a get name property and i can call that get name property in my template uh by referring to the this recommend object which is always going to be available in any recommendation module template pointing at the instance of the recommendation module class so now i come back here and i refresh the page combined search results has changed to the tutorial results and i've showed you both how to configure a recommendation module and how to use properties of the recommendation module to render a response let's do just one more clever thing and show a count of uh i'm sorry and so let's just do one more clever thing and show the user's search query uh along with the message we can do that by going to the process method which receives an instance of you finds search results object after the search has been completed well first let's create another property we'll call this query and then in the process we can say this query equals results get params get display query which is how you get access to the user's input in a human readable form from a search results object we also need to create a get query method to expose this property so that the view template can read it so we just create a get query that returns this query save all of that and now in our template here we can add four query colon single quote and then embed this escape html this recommend get query close friends and save that and let me actually do a search that has a query to make this more interesting and sure enough now it says you're looking at the tutorial results for query web so again my search query got processed the recommendation module pulled that out of the results object and we displayed it right here of course there's a lot more to learn there's a lot of depth to what viewfind is doing but the point here is that you can build small plug-ins like this which have access to other pieces if you find and you can pull things out and push them to the display so that you can do some fairly sophisticated customizations from within your local module with relatively little effort next time around i will go into some more code generators and show you how to do some other advanced customizations until then as always feel free to reach out with questions through the mailing lists or slack and have a good month