From 1193d4be57557ea852693cac00d9d5d5f9acb671 Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Tue, 19 May 2020 08:39:37 -0400
Subject: [PATCH] Improve flexibility of getFormattedMarcDetails(). (#1622)

---
 config/vufind/NoILS.ini                       |  10 +-
 .../VuFind/RecordDriver/MarcAdvancedTrait.php | 105 +++++++++++-------
 .../VuFindTest/RecordDriver/SolrMarcTest.php  |   4 +
 3 files changed, 78 insertions(+), 41 deletions(-)

diff --git a/config/vufind/NoILS.ini b/config/vufind/NoILS.ini
index 6947c23537f..319c7fdc680 100644
--- a/config/vufind/NoILS.ini
+++ b/config/vufind/NoILS.ini
@@ -26,7 +26,10 @@ useStatus = none
 ; Used if useHoldings is set to "marc"
 ; syntax = type|data
 ; For direct mapping use "msg|Message to display"
-; For Marc Mapping use "marc|subfield(s)" E.g. marc|khi
+; For Marc Mapping use "marc|subfield(s)|overrideField" where subfield(s) lists
+; the subfield(s) to extract, and overrideField is a MARC field (if you want to
+; use something different than the field specified in the marcField setting)
+; E.g. marc|khi or marc|a|245
 marcField = 852
 availability = "msg|false"
 use_unknown_message = "msg|true"
@@ -43,7 +46,10 @@ number = "marc|8"
 ; Used if useStatus is set to "marc"
 ; syntax = type|data
 ; For direct mapping use "msg|Message to display"
-; For Marc Mapping use "marc|subfield(s)" E.g. marc|khi
+; For Marc Mapping use "marc|subfield(s)|overrideField" where subfield(s) lists
+; the subfield(s) to extract, and overrideField is a MARC field (if you want to
+; use something different than the field specified in the marcField setting)
+; E.g. marc|khi or marc|a|245
 marcField = 852
 availability = "msg|false"
 use_unknown_message = "msg|true"
diff --git a/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php b/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php
index 0efebdddf1d..5791f389d8b 100644
--- a/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php
+++ b/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php
@@ -783,57 +783,84 @@ trait MarcAdvancedTrait
         return false;
     }
 
+    /**
+     * Support method for getFormattedMarcDetails() -- extract a single result
+     *
+     * @param object $currentField Result from File_MARC::getFields
+     * @param array  $details      Parsed instructions from getFormattedMarcDetails()
+     *
+     * @return string|bool
+     */
+    protected function extractSingleMarcDetail($currentField, $details)
+    {
+        // Simplest case -- "msg" mode (just return a configured message):
+        if ($details['mode'] === 'msg') {
+            // Map 'true' and 'false' to boolean equivalents:
+            $msgMap = ['true' => true, 'false' => false];
+            return $msgMap[$details['params']] ?? $details['params'];
+        }
+
+        // Standard case -- "marc" mode (extract subfield data):
+        $result = $this->getSubfieldArray(
+            $currentField,
+            // Default to subfield a if nothing is specified:
+            str_split($details['params'] ?? 'a'),
+            true
+        );
+        return count($result) > 0 ? (string)$result[0] : '';
+    }
+
     /**
      * Get Status/Holdings Information from the internally stored MARC Record
      * (support method used by the NoILS driver).
      *
-     * @param array $field The MARC Field to retrieve
-     * @param array $data  A keyed array of data to retrieve from subfields
+     * @param string $defaultField The MARC Field to retrieve if $data commands do
+     * not request something more specific
+     * @param array  $data         The type of data to retrieve from the MARC field;
+     * an array of pipe-delimited commands where the first part determines the data
+     * retrieval mode, the second part provides further instructions, and the
+     * optional third part provides a field to override $defaultField; supported
+     * modes: "msg" (for a hard-coded message) and "marc" (for fetching subfield
+     * data)
      *
      * @return array
      */
-    public function getFormattedMarcDetails($field, $data)
-    {
-        // Initialize return array
-        $matches = [];
-        $i = 0;
+    public function getFormattedMarcDetails($defaultField, $data)
+    {
+        // First, parse the instructions into a more useful format, so we know
+        // which fields we're going to have to look up.
+        $instructions = [];
+        foreach ($data as $key => $rawInstruction) {
+            $instructionParts = explode('|', $rawInstruction);
+            $instructions[$key] = [
+                'mode' => $instructionParts[0],
+                'params' => $instructionParts[1] ?? null,
+                'field' => $instructionParts[2] ?? $defaultField
+            ];
+        }
 
-        // Try to look up the specified field, return empty array if it doesn't
-        // exist.
-        $fields = $this->getMarcRecord()->getFields($field);
-        if (!is_array($fields)) {
-            return $matches;
+        // Now fetch all of the MARC data that we need.
+        $getTagCallback = function ($instruction) {
+            return $instruction['field'];
+        };
+        $fields = [];
+        foreach (array_unique(array_map($getTagCallback, $instructions)) as $field) {
+            $fields[$field] = $this->getMarcRecord()->getFields($field);
         }
 
-        // Extract all the requested subfields, if applicable.
-        foreach ($fields as $currentField) {
-            foreach ($data as $key => $info) {
-                $split = explode("|", $info);
-                if ($split[0] == "msg") {
-                    if ($split[1] == "true") {
-                        $result = true;
-                    } elseif ($split[1] == "false") {
-                        $result = false;
-                    } else {
-                        $result = $split[1];
-                    }
-                    $matches[$i][$key] = $result;
-                } else {
-                    // Default to subfield a if nothing is specified.
-                    if (count($split) < 2) {
-                        $subfields = ['a'];
-                    } else {
-                        $subfields = str_split($split[1]);
-                    }
-                    $result = $this->getSubfieldArray(
-                        $currentField, $subfields, true
-                    );
-                    $matches[$i][$key] = count($result) > 0
-                        ? (string)$result[0] : '';
+        // Initialize return array
+        $matches = [];
+
+        // Process the instructions on the requested data.
+        foreach ($instructions as $key => $details) {
+            foreach ($fields[$details['field']] as $i => $currentField) {
+                if (!isset($matches[$i])) {
+                    $matches[$i] = ['id' => $this->getUniqueId()];
                 }
+                $matches[$i][$key] = $this->extractSingleMarcDetail(
+                    $currentField, $details
+                );
             }
-            $matches[$i]['id'] = $this->getUniqueID();
-            $i++;
         }
         return $matches;
     }
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php
index f46520d7cf6..d2acb344e36 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php
@@ -135,9 +135,11 @@ class SolrMarcTest extends \VuFindTest\Unit\TestCase
             'foo' => 'msg|true',
             'bar' => 'msg|false',
             'baz' => 'msg|xyzzy',
+            'null' => 'msg',
             'title' => 'marc|a',
             'default' => 'marc',
             'emptySubfield' => 'marc|c',
+            'pub' => 'marc|abc|260',
         ];
         $this->assertEquals(
             [
@@ -145,10 +147,12 @@ class SolrMarcTest extends \VuFindTest\Unit\TestCase
                     'id' => '000105196',
                     'foo' => true,
                     'bar' => false,
+                    'null' => null,
                     'baz' => 'xyzzy',
                     'title' => 'Bollettino della Unione matematica italiana.',
                     'default' => 'Bollettino della Unione matematica italiana.',
                     'emptySubfield' => '',
+                    'pub' => 'Bologna : Zanichelli, 1922-1975.',
                 ]
             ],
             $record->getFormattedMarcDetails('245', $input)
-- 
GitLab