Skip to content

Commit 54e977a

Browse files
committed
[#6] Parse each 653 $a as a Subject
`Record::getSubjects()` now returns an array of `Subject` (subclass of `Field`) and `UncontrolledSubject` (subclass of `Subfield`), both implementing the `SubjectInterface` interface.
1 parent b1a0b56 commit 54e977a

File tree

9 files changed

+195
-22
lines changed

9 files changed

+195
-22
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,9 @@ British tradition, a mix of records from both traditions will look silly.
201201

202202
### subjects
203203

204-
`$record->getSubjects($vocabulary, $tag)` or `$record->subjects` returns an array of `Subject`
205-
objects from all [the 6XX fields](http://www.loc.gov/marc/bibliographic/bd6xx.html).
204+
`$record->getSubjects($vocabulary, $tag)` or `$record->subjects` returns an array of
205+
`Subject` and `UncontrolledSubject` objects from all
206+
[the 6XX fields](http://www.loc.gov/marc/bibliographic/bd6xx.html).
206207
The `getSubjects()` method have two optional arguments you can use to limit by
207208
vocabulary and/or tag.
208209

src/Fields/Field.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public function __construct(\File_MARC_Field $field)
1313
$this->field = $field;
1414
}
1515

16+
public function getField()
17+
{
18+
return $this->field;
19+
}
20+
1621
public function jsonSerialize()
1722
{
1823
return (string) $this;
@@ -31,6 +36,9 @@ public function __get($key)
3136
}
3237
}
3338

39+
/**
40+
* Return the data value of the *first* subfield with a given code.
41+
*/
3442
public function sf($code)
3543
{
3644
$x = $this->getSubfield($code);

src/Fields/Subfield.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Scriptotek\Marc\Fields;
4+
5+
use Scriptotek\Marc\Record;
6+
7+
abstract class Subfield implements \JsonSerializable
8+
{
9+
protected $subfield;
10+
protected $field;
11+
12+
public function __construct(\File_MARC_Data_Field $field, \File_MARC_Subfield $subfield)
13+
{
14+
$this->field = $field;
15+
$this->subfield = $subfield;
16+
}
17+
18+
public function __destruct()
19+
{
20+
$this->field = null;
21+
$this->subfield = null;
22+
}
23+
24+
public function delete()
25+
{
26+
$this->subfield->delete();
27+
$this->field->deleteSubfield($this->subfield);
28+
$this->__destruct();
29+
}
30+
31+
public function jsonSerialize()
32+
{
33+
return (string) $this;
34+
}
35+
36+
public function __toString()
37+
{
38+
return $this->subfield->getData();
39+
}
40+
41+
public function __call($name, $args)
42+
{
43+
return call_user_func_array([$this->subfield, $name], $args);
44+
}
45+
46+
public function __get($key)
47+
{
48+
$method = 'get' . ucfirst($key);
49+
if (method_exists($this, $method)) {
50+
return call_user_func([$this, $method]);
51+
}
52+
}
53+
}

src/Fields/Subject.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Scriptotek\Marc\Record;
66

7-
class Subject extends Field implements FieldInterface
7+
class Subject extends Field implements FieldInterface, SubjectInterface
88
{
99
public static $glue = ' : ';
1010

@@ -37,7 +37,19 @@ class Subject extends Field implements FieldInterface
3737

3838
public static function get(Record $record)
3939
{
40-
return parent::makeFieldObjects($record, '6..', true);
40+
$subjects = [];
41+
42+
foreach (parent::makeFieldObjects($record, '6..', true) as $subject) {
43+
if ($subject->getTag() == '653') {
44+
foreach ($subject->getSubfields('a') as $sfa) {
45+
$subjects[] = new UncontrolledSubject($subject->getField(), $sfa);
46+
}
47+
} else {
48+
$subjects[] = $subject;
49+
}
50+
}
51+
52+
return $subjects;
4153
}
4254

4355
public function getType()
@@ -79,11 +91,16 @@ public function getParts()
7991
return $parts;
8092
}
8193

82-
public function __toString()
94+
public function getTerm()
8395
{
8496
return implode(self::$glue, $this->getParts());
8597
}
8698

99+
public function __toString()
100+
{
101+
return $this->getTerm();
102+
}
103+
87104
public function jsonSerialize()
88105
{
89106
return [

src/Fields/SubjectInterface.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Scriptotek\Marc\Fields;
4+
5+
interface SubjectInterface
6+
{
7+
public function getType();
8+
9+
public function getVocabulary();
10+
11+
public function getControlNumber();
12+
13+
public function getParts();
14+
15+
public function getTerm();
16+
17+
public function __toString();
18+
19+
public function jsonSerialize();
20+
}

src/Fields/UncontrolledSubject.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Scriptotek\Marc\Fields;
4+
5+
use Scriptotek\Marc\Record;
6+
7+
class UncontrolledSubject extends Subfield implements SubjectInterface
8+
{
9+
public function getType()
10+
{
11+
return Subject::UNCONTROLLED_INDEX_TERM;
12+
}
13+
14+
public function getVocabulary()
15+
{
16+
return null;
17+
}
18+
19+
/**
20+
* Return the Authority record control number
21+
*/
22+
public function getControlNumber()
23+
{
24+
return null;
25+
}
26+
27+
public function getTerm()
28+
{
29+
return $this->subfield->getData();
30+
}
31+
32+
public function getParts()
33+
{
34+
$parts = [$this->getTerm()];
35+
}
36+
37+
public function __toString()
38+
{
39+
return $this->getTerm();
40+
}
41+
42+
public function jsonSerialize()
43+
{
44+
return [
45+
'type' => $this->getType(),
46+
'vocabulary' => $this->getVocabulary(),
47+
'id' => $this->getControlNumber(),
48+
'term' => (string) $this,
49+
];
50+
}
51+
}

src/Record.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Scriptotek\Marc\Fields\ControlField;
1010
use Scriptotek\Marc\Fields\Isbn;
1111
use Scriptotek\Marc\Fields\Subject;
12+
use Scriptotek\Marc\Fields\SubjectInterface;
1213
use Scriptotek\Marc\Fields\Title;
1314

1415
/**
@@ -180,18 +181,20 @@ public function getTitle()
180181
}
181182

182183
/**
183-
* Get an array of the 6XX fields as `Subject` objects, optionally filtered by
184-
* vocabulary and/or tag.
184+
* Get an array of the 6XX fields as `SubjectInterface` objects, optionally
185+
* filtered by vocabulary and/or tag.
185186
*
186187
* @param string $vocabulary
187-
* @param string $tag
188-
* @return Subject[]
188+
* @param string|string[] $tag
189+
* @return SubjectInterface[]
189190
*/
190191
public function getSubjects($vocabulary = null, $tag = null)
191192
{
192-
return array_values(array_filter(Subject::get($this), function (Subject $field) use ($vocabulary, $tag) {
193-
$a = is_null($vocabulary) || $vocabulary == $field->vocabulary;
194-
$b = is_null($tag) || $tag == $field->type;
193+
$tag = is_null($tag) ? [] : (is_array($tag) ? $tag : [$tag]);
194+
195+
return array_values(array_filter(Subject::get($this), function (SubjectInterface $subject) use ($vocabulary, $tag) {
196+
$a = is_null($vocabulary) || $vocabulary == $subject->getVocabulary();
197+
$b = empty($tag) || in_array($subject->getType(), $tag);
195198

196199
return $a && $b;
197200
}));

tests/SubjectFieldTest.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use Scriptotek\Marc\Collection;
44
use Scriptotek\Marc\Fields\Subject;
5+
use Scriptotek\Marc\Fields\UncontrolledSubject;
56

67
class SubjectFieldTest extends \PHPUnit_Framework_TestCase
78
{
@@ -32,14 +33,21 @@ public function testSubjects()
3233
$this->assertInstanceOf('Scriptotek\Marc\Fields\Subject', $subject);
3334
$this->assertEquals('noubomn', $subject->vocabulary);
3435
$this->assertEquals('Elementærpartikler', strval($subject));
35-
$this->assertEquals('650', $subject->getTag());
36+
$this->assertEquals(Subject::TOPICAL_TERM, $subject->getType());
3637
$this->assertNull($subject->getControlNumber());
38+
}
3739

38-
$subject = $record->subjects[3];
39-
$this->assertInstanceOf('Scriptotek\Marc\Fields\Subject', $subject);
40-
$this->assertNull($subject->vocabulary);
41-
$this->assertEquals('elementærpartikler', strval($subject));
42-
$this->assertEquals('653', $subject->getTag());
40+
public function testRepeated653a()
41+
{
42+
$record = $this->getNthrecord(3);
43+
44+
$subjects = $record->getSubjects(null, Subject::UNCONTROLLED_INDEX_TERM);
45+
$this->assertCount(2, $subjects);
46+
47+
$this->assertInstanceOf(UncontrolledSubject::class, $subjects[0]);
48+
$this->assertEquals('elementærpartikler', (string) $subjects[0]);
49+
$this->assertEquals(Subject::UNCONTROLLED_INDEX_TERM, $subjects[0]->getType());
50+
$this->assertEquals('symmetri', (string) $subjects[1]);
4351
}
4452

4553
public function testGetSubjectsFiltering()
@@ -50,21 +58,35 @@ public function testGetSubjectsFiltering()
5058
$noubomn = $record->getSubjects('noubomn');
5159
$noubomn_topic = $record->getSubjects('noubomn', Subject::TOPICAL_TERM);
5260
$noubomn_place = $record->getSubjects('noubomn', Subject::GEOGRAPHIC_NAME);
61+
$type_combo = $record->getSubjects(null, [Subject::TOPICAL_TERM, Subject::UNCONTROLLED_INDEX_TERM]);
5362

5463
$this->assertCount(1, $lcsh);
5564
$this->assertCount(2, $noubomn);
5665
$this->assertCount(2, $noubomn_topic);
5766
$this->assertCount(0, $noubomn_place);
67+
$this->assertCount(5, $type_combo);
5868
}
5969

6070
public function testEdit()
6171
{
62-
$record = $this->getNthrecord(2);
72+
$record = $this->getNthrecord(3);
73+
$this->assertCount(5, $record->subjects);
74+
75+
$this->assertInstanceOf(Subject::class, $record->subjects[0]);
76+
$record->subjects[0]->delete();
77+
78+
$this->assertInstanceOf(Subject::class, $record->subjects[0]);
79+
$record->subjects[0]->delete();
80+
81+
$this->assertInstanceOf(Subject::class, $record->subjects[0]);
82+
$record->subjects[0]->delete();
6383
$this->assertCount(2, $record->subjects);
6484

85+
$this->assertInstanceOf(UncontrolledSubject::class, $record->subjects[0]);
6586
$record->subjects[0]->delete();
6687
$this->assertCount(1, $record->subjects);
6788

89+
$this->assertInstanceOf(UncontrolledSubject::class, $record->subjects[0]);
6890
$record->subjects[0]->delete();
6991
$this->assertCount(0, $record->subjects);
7092
}

tests/data/sru-alma.xml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,6 @@
303303
</datafield>
304304
<datafield tag="653" ind1=" " ind2=" ">
305305
<subfield code="a">elementærpartikler</subfield>
306-
</datafield>
307-
<datafield tag="653" ind1=" " ind2=" ">
308306
<subfield code="a">symmetri</subfield>
309307
</datafield>
310308
<datafield tag="776" ind1="0" ind2=" ">
@@ -335,4 +333,4 @@
335333
<xb:exact>true</xb:exact>
336334
<xb:responseDate>2015-07-29T13:59:02+0200</xb:responseDate>
337335
</extraResponseData>
338-
</searchRetrieveResponse>
336+
</searchRetrieveResponse>

0 commit comments

Comments
 (0)