About Features Downloads Getting Started Documentation Events Support GitHub

Love VuFind®? Consider becoming a financial supporter. Your support helps build a better VuFind®!

Site Tools


Warning: This page has not been updated in over over a year and may be outdated or deprecated.
videos:customizing_record_views

This is an old revision of the document!


Video 22: Customizing Record Views

The twenty-second VuFind® instructional video provides an overview of VuFind®'s record display code, including Record Drivers and the Record Data Formatter, and includes a demonstration of adding a custom field to your VuFind display.

Video is available as an mp4 download or through YouTube.

Transcript

This is a machine-generated transcript and will be corrected as time permits.

hello and welcome to this month's VuFind tutorial video this month we're going to be talking about customizing VuFinds record views so to give a quick overview i should first say that this is a a very in-depth detailed topic and we can only scratch the surface of it in a brief video so i encourage you to look at the learning VuFind book look at the code look at the wiki and ask questions of the community if you need more help but i hope that this video will provide some useful context and at least show you some of the starting points you'll need to explore to get a deep understanding of all of these concepts so in this video we're going to talk about record drivers which are the way VuFind internally represents individual records regardless of where they come from we're going to talk about the record data formatter which is a tool that VuFind uses to display tables of data taken from records which makes it easy to customize particular parts of the user interface in sophisticated ways and i'm going to do a quick demo of how you can add a custom field to your VuFind instance if you have something you want to display that's not part of the core system so let's start by talking about record drivers what is a record driver a record driver is an abstraction it is a php object wrapped around some metadata with accompanying custom templates and this is how VuFind displays all of the data coming from all of the records that it interacts with the idea here is that VuFind only interacts with data using record driver code it does not directly interact with the data what do i mean by that for example in a mark record the title is stored in a 245 field but information about things like 245 fields only exists inside record driver code outside of the record driver VuFind only cares about getting a title it asks the record driver please give me a title and the record driver if it's wrapped around a mark record gives it the 245 field if it's wrapped around a dublin core record it gives it the dc title field etc etc so it lets us encapsulate all of the specific metadata handling in one place and write more generic more reusable more flexible code that can interact with all kinds of things at a higher level VuFind uses what is called duct typing to determine compatibility between its various features and particular records so for example you know if we want to display a title it's going to check with the record driver if it's capable of displaying a title and then only display it if appropriate now of course everything has a title but there are some more advanced features that are more metadata format specific and for these purposes the record drivers are able to conditionally provide data only when it's supported and a lot of VuFind's code is designed to either load particular logic based on the presence of capabilities or to gracefully degrade if we want to do something and we can't all of this is designed again to make it possible to write fairly generic fairly reusable code and have it interact successfully with all kinds of different data because VuFind is designed to let you index practically anything and also to interact with a number of external systems that format data in different ways of course with all of the possibilities that are out there it would be easy to end up with a lot of redundant and complicated and contradictory code but VuFind uses features of the php language particularly traits and inheritance to make code as reusable as possible and to share features across multiple drivers so in order to understand record drivers having a good understanding of php object oriented logic is helpful so let's go into a little more detail on the php side of things so every record driver is a php class and all of these classes can be found in the VuFind record driver namespace there are a few record driver classes that are particularly important one is called abstract base this is an abstract base class as the name suggests and all of the record drivers extend to this class either directly or indirectly and this is where the absolute bare minimum functionality for record drivers live all of the things that every record driver has to be able to do are implemented here and this is fairly minimal because VuFind is designed to be very flexible but at the very least every record needs to have an identifier and every record needs to be able to be displayed through some textual representation and so these bare minimum methods are there in abstract base now even though vufind is designed to be extremely flexible and to support a lot of use cases the most common use case in VuFind is displaying bibliographic records records representing books or other materials and so there is a record driver called default record which extends abstract base and adds a whole lot of methods that support bibliographic record functionality things like titles and authors and subjects and so forth so this allows us to create sort of a standard implementation of bibliographic record information which is shared by a large number of other record drivers so you know whether we're working with data from solar or from a third-party discovery system we're probably in a subclass of default record which just fills in a standard set of methods using data from appropriate places depending on the data source record drivers have their own plugin manager just like pretty much any extensible component of VuFind so well the default is to use the record drivers from the VuFind record driver namespace you can very easily create your own record drivers or extend the built-in record drivers to add methods change behavior etc all of this is extensible and overrideable it's also worth noting that the way the plugin manager is used varies from search backend to search backend and search back-ends were talked about in last month's video when we went over the search system but the important thing is that whenever we're retrieving data from a search backend VuFind has code that turns it into record drivers so that the rest of the system can interact with it in many cases this is pretty simple a lot of the web based discovery systems just have a single record driver so for something like eds or summon we're just going to pull a copy of the appropriate record driver out of the plugin manager and populate it with data for every record that we've got but for records coming from the local solar index we do things in a slightly more sophisticated way because it's possible that we have sort of heterogeneous data in our solar index we might have some mark records populated from one place some dublin core records populated from another and so forth and of course depending on what kind of data is stored in the solar index we may need to display it in different ways and so this is where solar's record format field comes into play for each record that we fetch from solar we look at the record format field and we look to see if there is a record driver registered in the plug-in manager with the name solar and the value of record format following that so for example if record format is set to mark we look for a solar mark record driver if we find a match we use that if we don't find a match we default to solar default which is another core record driver that provides all of its data based on what has been indexed into solar some of the other drivers like solar mark which i mentioned augment the data in the solar index with more specific things pulled directly from the raw source record so again that's a fair amount of detail but the point is we have a lot of flexibility in how we load these record drivers and it lets us be very specific in what we present while interacting with the rest of the VuFind system in a very standardized and generic way so as i mentioned at the beginning of the discussion of record drivers the record drivers are not just php classes they are also sets of templates the reason for this is that some parts of VuFind's displays are rendered using record driver specific templates so for example when you get a screen of search results every individual search result that you see is getting rendered using a different template pulled from your theme the reason for this is as i say you might have a mix of different kinds of records in your results and they may need to be displayed very differently so VuFind has the ability to display a totally different template for multiple records in a list based on what kind of records they are and again in practice most of what VuFind does by default is very similar but the design is such that it can be extended a great deal and you could have an extremely diverse set of displays if you need them so the templates that are used for these types of displays are found in the record driver subdirectory of the templates directory of your theme inside this record driver directory there are driver specific directories whose names match the record driver class names minus the namespaces so for example if we had templates that were specific to VuFind record driver solarmark they would be in a folder called templates record driver solar mark not using the the VuFind piece of the uh the name again most of vufine's default templates are actually defined in the default record directory because most of what we display by default is bibliographic record type stuff so we just have these core templates that we reuse across all the drivers but if you need to customize something more specifically to a particular record driver all you have to do is create a folder put an override template in there and that will be displayed only for records matching that particular type and of course VuFind uses record driver class inheritance to load the templates so it tries to find the most specific available match so for example if we're displaying a solar mark record based on the discussion earlier solar mark is a child of solar default solar default is a child of default record default record is a child of abstract base uh there's there's a fairly deep inheritance tree in record drivers because of the way we reuse functionality anyway given given the solar mark record if ufi needs to display it in search results it's going to look and see do we have a solar mark search result template if so use it if not let's check if we have a solar default result template if we if we do use it nope we don't so we fall back another level there is the default record search result template that's what's going to end up getting used so again understanding these relationships between the templates and the classes and the ability to override is useful both to find the things that you want to customize and also to override things at the right level of specificity so enough about record drivers let's dive a little bit deeper into the record data formatter the record data formatter is a view helper that was introduced in VuFind four and its purpose is to display tables of data extracted from record drivers using a configuration driven approach are the reason that this was built is that prior to VuFind four many of the custom templates record driver specific templates that i described earlier were basically really long html tables full of you know labels and values and they were difficult to maintain and also very difficult to customize because if you wanted to change the order of two fields or add something new you had to copy this entire huge template customize it in your local theme and then every time you had an upgrade to VuFind you would have to reconcile any changes in that template with your local customizations it was an error prone tedious process and just not very flexible so the idea was instead of hard coding these tables into these templates let's build a helper that takes a configuration that tells it what fields to display how to display them what order to display them in and then if you want to customize something instead of copying and pasting potentially hundreds of lines of html and php you can just adjust a configuration slightly and thus you express your customization in a more specific and concise way it's easier to reconcile during updates etc so of course that's the advantage here using the record data formatter replaces these difficult to maintain gigantic templates with configuration of course with every advantage comes a disadvantage and the obvious disadvantage here is the record data formatter has a much steeper learning curve customizing a giant html template is a matter of cut and paste the the act of doing it is easy it's the maintenance that's hard and the record data formatter kind of reverses that making it harder to set things up in the first place because you have to learn more but making the long-term maintenance uh considerably simpler so it's not perfect but it's worth the investment in the long term i think it saves many people some trouble so let's talk about how the record data formatter configuration works since understanding this is very important to actually making use of it so the record data formatter is a view helper it has a factory that builds it with default settings for VuFind so if you look at the record data format or factory you will find all of the default configurations for the various places where we display tables of data these are things like the sort of core part of the record page where we display primary data about a record areas in some of the tabs like the description tab each of these areas has its own configuration established in the factory and there's a method that builds each of the default configurations which all makes it easy to extend and override and customize the configuration of each area is just a big php array with various settings explaining to the helper how it needs to display all the fields but because this is a pretty large and unwieldy data structure we've built a record data formatter spec builder class which provides some convenience methods for building this array and reorganizing it the most common ways of describing fields in this configuration are either just specifying there's a method on the record driver call that and display all the values you get back from it or in a slightly more sophisticated way rendering a custom template so call record driver method and then render this template using that data as input so the direct method approach is useful if you just want to display a value and you don't need to do anything special with it the template approach is more useful if you need to do special formatting or make it have outgoing links or things like that i should also note that if you use the custom template approach it follows the same mechanism as other record driver related templates so you can potentially tell the record data formatter to use a particular template to display a particular kind of value and then you can implement that template differently for each of your record drivers if you need to again there's a great deal of flexibility here but it can get a little complicated so as i say given the amount of time we have for this video i can't do too deep of a dive into all of this but i encourage you take a look at the record data formatter factory code this will show you how the record data formatter is set up by default and also take a look at the record data formatter wiki page which explains all of the settings that can go into the php array to configure the record data formatter and also lists all of the spec builder methods that you can use to build the configuration so for most things you can get away with fairly simple calls to just add a method based field or add a custom template but if you need to get deeper into it there are some really sophisticated things you can do like building custom functions that return a map of labels to values if for example you need to call a single method of the record driver and have that lead to multiple labeled sections in your final display it can get quite complex but in the interest of demonstrating this while keeping things simple i'm now going to dive into a demo of how to use the record data formatter and record drivers to add a custom field to VuFind so the use case for this demo is that we have a local note field in this example 597 a which contains a note about a donor and we just want to create a donor note field in VuFind that shows the contents of this mark field and here is a sample record which we're going to be working with just to show what this looks like as you can see it's just a bare minimum mark record with an author a title a publisher and a donor note so with that i am going to switch over to my tutorial test machine so this is the same test machine that we've been building on through all of the tutorial videos and for the purposes of this example the key details of note are that we are using a local code module called tutorial and also a local custom theme called tutorial both of these were built in earlier videos so if you need to learn more about custom code or custom themes just go back through the list you'll find them but i am just going to dive right in here and start building i'll explain as i go so i'm going to go to the command line and i'm going to switch to the view find home directory i am actually going to also bring up my code editor since we'll be editing some files and running some commands so the first step in adding a custom field to VuFind is to index the data that we want to display in the field now as i mentioned we do store the entire raw records in the index so in theory since this is a mark file and the data is in a 597 i could just skip the indexing step write some custom code that pulls the 597 out of the stored mark record and be done with it but in general i think it's a little more useful to pull the data into solar because this allows you to search it and it's also more generic if you potentially have the same type of data coming from multiple sources if you can just index it into solar from various formats then you only need to write one set of code to pull the data from the solar index you don't have to worry about all the metadata specifics in the code you're writing so for this example first thing we're going to do is index the 597a so i'm just going to go to my local import mark local properties file i'm going to go down to the bottom and i'm just going to create a custom field called donor str mv which is a multi-valued string field taking advantage of the dynamic field suffixes defined in VuFind solar schema i'm going to say donor string mv equals 597 a i'm going to save that file then i'm going to go to the command line and i'm going to run import mark.sh on my example mark record to index it this will take just a moment and there we go our record with an id of donor example has been indexed into our VuFind so now if i go to the catalog and i just look that up by id here is our record and if i look at the staff view i can even see there's my 597a but of course we're not displaying our donor note up here in the core metadata like we want to because we haven't done that customization yet so let me show you how to add that and this is going to require us to customize both record driver and record data formatter so the first step is we need to create a custom record driver that has a method in it that can retrieve the donor information and i'm going to use the extend class code generator which we demonstrated in an earlier video to build myself a new record driver so i'm going to say php public index.php generate extend class and i'm going to extend the VuFind record driver solar mark driver into the tutorial module so in this instance we know that we're only going to have this donor note in our mark records and we just need to extend that class so we can add a method to it and we'll do that in the tutorial module if in a hypothetical scenario we had multiple types of records that all needed to have this implemented we might have to extend multiple classes and we could perhaps create a custom trait to share this functionality between them but for the purposes of this demo we're doing things in the simplest possible way i'm just extending the one class so my generator has now set up for me in my tutorial module this solar mark record driver which has nothing in it so i just need to add a public function here to provide the donor information so that we can get access to it from the record data formatter so i'm just going to paste some code in here we create public function get donors and this just returns this field donor strmv and if there's nothing there it defaults to an empty array so in all of the record drivers extending solar default there's a property called this fields and it's just an array indexed by solar field name so this is how you can gain direct access to anything you put in the solar index so that is all i need to do there now because i've created a new class which has edited my module configuration i do also need to remember to clear my configuration cache so that VuFind knows to load the updated configuration so i'm just going to quickly before i forget delete the contents of the local cache config directory so that will not confuse me later now the next step is i want to create a custom record data formatter factory so i can adjust the configuration of the record data formatter to display this new donor field so first thing i'm going to do is just create an empty directory in my tutorial module for the factory since we haven't created anything uh custom related to view helpers yet this directory doesn't exist and i'm just going to create it at the command line here so that i don't have to create a whole bunch of nested folders in vs code which is less efficient so as i mentioned the core default record data formatter settings are found in if you find view helper root record data formatter factory so i'm just creating an equivalent directory structure in my tutorial module where i can extend that now we don't currently have a code generator that is particularly well suited to overriding this kind of factory so i'm just going to create this whole custom factory by hand so i'm creating record data formatter factory dot php in that directory that i just built and i'm going to paste some code straight in here and then i'll walk you through it so here is our custom factory for namespace of course we have to match our directory structure so it's tutorial view helper root uh we're going to make use of that spec builder helper class for setting up the configuration so i just have a use statement to make that code accessible here and then my class definition my local record data formatter factory extends the core record data formatter factory and as i explained earlier the core record data formatter factory has a method for each of the uh default configurations that it manages so there's one called get default core specs which is how we set up the core metadata and what i want to do is simply add a line to that so what i do is i create a new spec builder which is that helper class for manipulating these configurations and i initialize it using the output of the parent version of this method so i load the existing default core specs into the spec builder so this takes the php array of settings turns it into an object with convenience methods for manipulating things next i call spec set line which is one of the helper methods for adding something and set line is the simplest option this just says i want to label this donor note and i want it to display any or all values coming from the record drivers get donors method which we just defined earlier and then my method returns a call to spec get array which turns my spec builder object back into a php configuration array and that's it so we just take what we've already got by default we add something to it and we return it you can do other kinds of manipulations here you can change the order of fields you can remove fields you don't want you can add fields in a whole variety of ways but this is the simplest possible example so now there's just one last thing we need to do we've created this custom factory but we need to actually register it in our theme so because this factory is building a view helper and view helpers are specific to themes i need to go into my custom theme and find the theme.config.php file there and i just need to create a helpers section inside that file so again i'm just going to paste some code in here add a comma the end of our css there so that it parses correctly and then paste in this helpers so the helpers section of your theme configuration is where all the view helpers are defined inside the helper section there's a factories section this is how we tell VuFind which factory to use to build which helper so here we just want to say for the existing core record data formatter class we want to use our local custom factory which has added a little bit of extra special logic so all i have to do is save that and we're done we have a configuration telling VuFind to build the record data formatter using a custom factory that custom factory just adds a line to one of the default configurations for record display which makes use of a custom record driver that we just built so we've essentially edited three files and we're now ready to test this out so if i refresh this page and there it is donor note number 41 donated by damian katz and that's it so thank you for sitting through all of this background and this demo as i say there's a lot more that you can learn i certainly encourage you if you're interested to examine the code read the learning VuFind book and reach out with questions if there's anything else the community can help you with thanks for your time and bye for now

videos/customizing_record_views.1651749921.txt.gz · Last modified: 2022/05/05 11:25 by demiankatz