Skip to content

Commit bd87675

Browse files
committed
Add initialization from SimpleXMLElement object
1 parent 6b9ba18 commit bd87675

File tree

8 files changed

+126
-7
lines changed

8 files changed

+126
-7
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ composer require scriptotek/marc
2929

3030
## Reading records
3131

32-
Use `Collection::fromFile` or `Collection::fromString` to read one or more
33-
MARC records from a file or string. The methods autodetect the data format
34-
(Binary XML or MARCXML) and whether the XML is namespaced or not.
32+
Use `Collection::fromFile`, `Collection::fromString` or `Collection::fromSimpleXMLElement`
33+
to read one or more MARC records from a file or string. The methods autodetect the data
34+
format (Binary XML or MARCXML) and whether the XML is namespaced or not.
3535

3636
```php
3737
use Scriptotek\Marc\Collection;
@@ -64,9 +64,9 @@ foreach ($records as $record) {
6464
}
6565
```
6666

67-
If you only have a single record, you can also use `Record::fromFile` or
68-
`Record::fromString`. These use the `Collection` methods under the hood,
69-
but returns a single `Record` object.
67+
If you only have a single record, you can also use `Record::fromFile`,
68+
`Record::fromString` or `Record::fromSimpleXMLElement`. These use the
69+
`Collection` methods under the hood, but returns a single `Record` object.
7070

7171
```php
7272
use Scriptotek\Marc\Record;

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"ext-xml": "*",
1616
"ext-json": "*",
1717
"ck/file_marc_reference": "^1.2",
18-
"pear/file_marc": "^1.1"
18+
"pear/file_marc": "^1.4.1"
1919
},
2020
"require-dev": {
2121
"phpunit/phpunit": "^5.7 | ^6.0 | ^7.0",

src/Collection.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
use Scriptotek\Marc\Exceptions\RecordNotFound;
77
use Scriptotek\Marc\Exceptions\UnknownRecordType;
88
use Scriptotek\Marc\Importers\Importer;
9+
use Scriptotek\Marc\Importers\XmlImporter;
10+
use SimpleXMLElement;
911

1012
class Collection implements \Iterator
1113
{
@@ -51,6 +53,19 @@ public static function fromString($data)
5153
return $importer->fromString($data);
5254
}
5355

56+
/**
57+
* Load records from a SimpleXMLElement object.
58+
*
59+
* @param SimpleXMLElement $element
60+
* @return Collection
61+
*/
62+
public static function fromSimpleXMLElement(SimpleXMLElement $element)
63+
{
64+
$importer = new XmlImporter($element);
65+
66+
return $importer->getCollection();
67+
}
68+
5469
/**
5570
* Determines if a record is a bibliographic, holdings or authority record.
5671
*

src/Importers/Importer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use File_MARC;
66
use Scriptotek\Marc\Collection;
77
use Scriptotek\Marc\Factory;
8+
use SimpleXMLElement;
89

910
class Importer
1011
{

src/Importers/XmlImporter.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use File_MARCXML;
66
use Scriptotek\Marc\Collection;
7+
use Scriptotek\Marc\Exceptions\RecordNotFound;
78
use Scriptotek\Marc\Exceptions\XmlException;
89
use Scriptotek\Marc\Factory;
910
use SimpleXMLElement;
@@ -15,15 +16,30 @@ class XmlImporter
1516
/* var SimpleXMLElement */
1617
protected $source;
1718

19+
/**
20+
* XmlImporter constructor.
21+
*
22+
* @param string|SimpleXMLElement $data Filename, XML string or SimpleXMLElement object
23+
* @param string $ns URI or prefix of the namespace
24+
* @param bool $isPrefix TRUE if $ns is a prefix, FALSE if it's a URI; defaults to FALSE
25+
* @param string $factory (optional) Object factory, probably no need to set this outside testing.
26+
*/
1827
public function __construct($data, $ns = '', $isPrefix = false, $factory = null)
1928
{
2029
$this->factory = isset($factory) ? $factory : new Factory();
2130

31+
if (is_a($data, SimpleXMLElement::class)) {
32+
$this->source = $data;
33+
return;
34+
}
35+
2236
if (strlen($data) < 256 && file_exists($data)) {
2337
$data = file_get_contents($data);
2438
}
2539

40+
// Store errors internally so that we can fetch them with libxml_get_errors() later
2641
libxml_use_internal_errors(true);
42+
2743
$this->source = simplexml_load_string($data, 'SimpleXMLElement', 0, $ns, $isPrefix);
2844
if (false === $this->source) {
2945
throw new XmlException(libxml_get_errors());
@@ -69,6 +85,22 @@ public function getRecords()
6985
return [];
7086
}
7187

88+
public function getFirstRecord()
89+
{
90+
$records = $this->getRecords();
91+
if (!count($records)) {
92+
throw new RecordNotFound();
93+
}
94+
95+
$record = $records[0];
96+
97+
list($prefix, $ns) = $this->getMarcNamespace($record->getNamespaces(true));
98+
99+
$parser = $this->factory->make('File_MARCXML', $record, File_MARCXML::SOURCE_SIMPLEXMLELEMENT, $ns);
100+
101+
return (new Collection($parser))->$this->getFirstRecord();
102+
}
103+
72104
public function getCollection()
73105
{
74106
$records = $this->getRecords();
@@ -77,6 +109,7 @@ public function getCollection()
77109
}
78110

79111
list($prefix, $ns) = $this->getMarcNamespace($records[0]->getNamespaces(true));
112+
80113
$pprefix = empty($prefix) ? '' : "$prefix:";
81114

82115
$records = array_map(function (SimpleXMLElement $record) {

src/Record.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Scriptotek\Marc\Exceptions\UnknownRecordType;
1111
use Scriptotek\Marc\Fields\ControlField;
1212
use Scriptotek\Marc\Fields\Field;
13+
use SimpleXMLElement;
1314

1415
/**
1516
* The MARC record wrapper.
@@ -148,6 +149,21 @@ public static function fromString($data)
148149
return Collection::fromString($data)->first();
149150
}
150151

152+
/**
153+
* Returns the first record found in the SimpleXMLElement object
154+
*
155+
* @param SimpleXMLElement $element
156+
* The SimpleXMLElement object in which to look for MARC records.
157+
* @return BibliographicRecord|HoldingsRecord|AuthorityRecord
158+
* A wrapped MARC record.
159+
* @throws RecordNotFound
160+
* When the object does not contain a MARC record.
161+
*/
162+
public static function fromSimpleXMLElement(SimpleXMLElement $element)
163+
{
164+
return Collection::fromSimpleXMLElement($element)->first();
165+
}
166+
151167
/*************************************************************************
152168
* Query
153169
*************************************************************************/

tests/CollectionTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,21 @@ public function testCollectionFromFile($filename, $expected)
7575
$this->assertCount($expected, $records);
7676
$this->assertInstanceOf(BibliographicRecord::class, $records[0]);
7777
}
78+
79+
80+
/**
81+
* Test that the sample files can be loaded using Collection::fromSimpleXMLElement.
82+
*
83+
* @dataProvider xmlFiles
84+
* @param string $filename
85+
* @param int $expected
86+
*/
87+
public function testInitializeFromSimpleXmlElement($filename, $expected)
88+
{
89+
$el = simplexml_load_file($this->pathTo($filename));
90+
91+
$collection = Collection::fromSimpleXMLElement($el);
92+
93+
$this->assertCount($expected, $collection->toArray());
94+
}
7895
}

tests/RecordTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use File_MARC_Record;
77
use Scriptotek\Marc\AuthorityRecord;
88
use Scriptotek\Marc\BibliographicRecord;
9+
use Scriptotek\Marc\Exceptions\RecordNotFound;
910
use Scriptotek\Marc\Fields\Field;
1011
use Scriptotek\Marc\Fields\Subject;
1112
use Scriptotek\Marc\HoldingsRecord;
@@ -122,4 +123,40 @@ public function testGetRecord()
122123
// by the getter.
123124
$this->assertSame($wrapped_record, $wrapper->getRecord());
124125
}
126+
127+
/**
128+
* Test that a Record wrapper object will not be initialized from
129+
* an SimpleXMLElement object that doesn't contain a MARC record.
130+
*/
131+
public function testInitializeFromInvalidSimpleXMLElement()
132+
{
133+
$source = simplexml_load_string(
134+
'<?xml version="1.0" encoding="UTF-8" ?><book></book>'
135+
);
136+
137+
$this->expectException(RecordNotFound::class);
138+
$record = Record::fromSimpleXMLElement($source);
139+
}
140+
141+
/**
142+
* Test that a Record wrapper object can be initialized from
143+
* a SimpleXMLElement object.
144+
*/
145+
public function testInitializeFromSimpleXmlElement()
146+
{
147+
$source = simplexml_load_string('<?xml version="1.0" encoding="UTF-8" ?>
148+
<record xmlns="http://www.loc.gov/MARC21/slim">
149+
<leader>99999cam a2299999 u 4500</leader>
150+
<controlfield tag="001">98218834x</controlfield>
151+
<datafield tag="020" ind1=" " ind2=" ">
152+
<subfield code="a">8200424421</subfield>
153+
<subfield code="q">h.</subfield>
154+
<subfield code="c">Nkr 98.00</subfield>
155+
</datafield>
156+
</record>');
157+
158+
$record = Record::fromSimpleXMLElement($source);
159+
$this->assertInstanceOf(Record::class, $record);
160+
$this->assertInstanceOf(BibliographicRecord::class, $record);
161+
}
125162
}

0 commit comments

Comments
 (0)