Consortialize

These changes/additions were made to accommodate a consortial environment, specifically MARC records containing 035$a fields for each of its consortium members' original source IDs.

All of these changes are well-isolated from the mainline code. At first, I considered incorporating these changes into the mainline code, but later thought it might be best to simply submit a pull request with the code I have now, just to get it "out there" (initial exposure). In the future, when/if there is enough demand (and discussion), hopefully a pattern will emerge out of the different needs from other VuFind users with consorital environments.

Here are just a few, quick brainstorming thoughts from Demian: "If we wanted to make this a mainline feature, it might actually be worth considering adding code to a few more places -- e.g. adding a getConsortialIds() method to the record driver so that retrieving the alternate IDs could be better abstracted (to allow alternate implementations, like storing them in the Solr index, retrieving based on a configuration, etc.). It might also make sense to have a separate ILS driver method for retrieving consortial holdings rather than using the existing getHolding, so the feature could be specifically implemented on a driver by driver basis and VuFind could throw an exception if consortial IDs are passed to a driver that can't handle them."

Lastly, I have put together a simple outline below, which describes the data model our consortium needs to use within VuFind (and how to setup VuFind to use the code in this pull request):

-----------

Goal: Extend VuFind to handle Consortial MARC Bibliographic Records




Steps required in order to setup VuFind for use with consortial data:

1. Load SolrMarcConsortium record driver (instead of default, SolrMarc) via module/VuFind/config/module.config.php

2. Choose an ILS Driver (via local/config/vufind/config.ini) that can handle consortial data, e.g., XCNCIP2

3. Configure ILS Driver for consortium (local/config/vufind/XCNCIP2.ini)





1. Data model differences between Single Institution (default) and Consortium

   A. Single institution

   A single institution (typical VuFind setup) has one set of bibliographic records, where each record contains a unique ID (Control Number and Control Number Identifier). 

   001 - Control Number (i.e., BIB ID)
   003 - Control Number Identifier (i.e., Institution code)

   E.g., Three bibliographic records

      001 - 123
      003 - ConsortiumMember1

      001 - 123
      001 - ConsortiumMember2

      001 - 456
      003 - ConsortiumMember3

   

   B. Consortium

    Similarly, a consortium has one set of bibliographic records, where each record contains a unique ID (Control Number and Control Number Identifier). However, each record also contains a collection of unique IDs representing the system control numbers of the consortium members' (originating) local records.

   001 - Control Number (i.e., BIB ID)
   003 - Control Number Identifier (i.e., Institution code)
   035 $a - System control number (i.e., BIB ID *and* Institution code combined into one value) for a consortium member, i.e., a "source record"
   035 $a - System control number for another consortium member, etc.

   E.g., Let's assume that the three bibliographic records above (in 1a) "match" (i.e., are effectively the same), thus a single bibliographic record would represent them as follows:

   001 - 567
   003 - ConsortiumOrgCode
   035 $a - (ConsortiumMember1)123
   035 $a - (ConsortiumMember2)123
   035 $a - (ConsortiumMember3)456



2. Introduce Consortium data model into VuFind

   I somehow need to pass along an array of 035$a data to the ILS Driver (instead of the default, non-consortium model, which passes along a single BIB ID).

   I decided to do this in the RecordDriver class. I created a SolrMarc subclass called SolrMarcConsortium, which has a single, overridden function:

    public function getRealTimeHoldings()
    {
        // We pass along all of the 035$a records to our ILS Driver.
        // The first one in the list is the ID of the aggregate (deduped) record.
        $the_035s = array();
        $the_035s[] = $this->getUniqueID();
        foreach ($this->getFieldArray('035', 'a', true) as $_035) {
            $the_035s[] = $_035;
        }
        return $this->hasILS()
            ? $this->holdLogic->getHoldings($the_035s)
            : array();
    }

    Of course, the underlying ILS driver needs to be able to handle this array of data. The XCNCIP2 driver is now capable of handling such data (along with default, non-consortium data). One needs to simply configure the XCNCIP2.ini file to expect consortium data. Here is the default .ini file (notice how it also gives instructions on how to set up the SolrMarcConsortium RecordDriver):

------------ snip -------------
[Catalog]
; Base URL for the XC NCIP Toolkit's NCIP responder:
url         = http://myuniversity.edu:8080/ncipv2/NCIPResponder

; Your library's Agency ID (ILSDefaultAgency setting in driver_config.properties):
agency      = "My University"

; Pickup location definitions: Tab-delimited file 
;
;     Format: [agency][TAB][locationID][TAB][locationDisplay]
;
;     e.g., 
;         (for consortium=false)
;         My University 1       Main Circulation Desk
;         My University 2       Stacks
;
;     e.g., 
;         (for consortium=true)
;         Agency1       1       Agency1 - Main Circulation Desk
;         Agency1       2       Agency1 - Stacks
;         Agency2       11      Agency2 - Main Circulation Desk
;         Agency2       12      Agency2 - Stacks
pickupLocationsFile = "config/vufind/locations.txt"

;-----------------------------------------------------------------
; Consortium settings below:
;-----------------------------------------------------------------

; Is this a consortium?
consortium  = false

;
; If consortium is true, you will need to also configure a consortium-based Record Driver
; capable of passing in a collection of matching deduplicated records, e.g., SolrMarcConsortium
;
; If consortium is false, then a standard (default) Record Driver, such as SolrMarc (which
; simply passes in a single record ID) will suffice
;
; To configure a consortium-based Record Driver, e.g, SolrMarcConsortum,
;       edit module/VuFind/config/module.config.php:
;
;'recorddriver' => array(
;               'abstract_factories' => array('VuFind\RecordDriver\PluginFactory'),
;               'factories' => array(
;               ...
;                   'solrmarc' => 'VuFind\RecordDriver\Factory::getSolrMarcConsortium',
;               ...
;               ),
;           ),

; If consortium is true, list all valid agencies
;agency[]  = "Agency1"
;agency[]  = "Agency2"


------------ snip -------------