Skip to content

Commit

Permalink
Add itemAtPath and itemsAtPath methods to Pathable class (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
jessarcher committed Jul 9, 2021
1 parent 0fa2053 commit 95f9e9e
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/PathQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace BigPictureMedical\OpenEhr;

use BigPictureMedical\OpenEhr\Rm\Common\Archetyped\Pathable;
use Illuminate\Support\Collection;

class PathQuery
{
public function __construct(protected string $query) {}

public function find(Pathable $root): mixed
{
$item = $root;

foreach ($this->segments() as $segment) {
if (! isset($segment['expression'])) {
$item = $item?->{$segment['attribute_name']};
continue;
}

$item = collect($item?->{$segment['attribute_name']})->firstWhere('archetype_node_id', $segment['expression']);
}

return $item;
}

public function findList(Pathable $root): array
{
$items = collect([$root]);

foreach ($this->segments() as $segment) {
$items = $items
->map(fn ($item) => $item->{$segment['attribute_name']})
->flatten()
->when(
isset($segment['expression']),
fn ($items) => $items
->filter(fn ($item) => $item->archetype_node_id === $segment['expression'])
->values()
);
}

return $items->toArray();
}

private function segments(): Collection
{
return collect(explode('/', $this->query))->map(function ($segment) {
$attributePattern = '(?<attribute_name>\w+)';
$predicatePattern = '(?<predicate>\[(?<expression>[\w.-]+)\])?';
if (preg_match('/^' . $attributePattern . $predicatePattern . '$/', $segment, $matches) !== 1) {
throw new \Exception("Unable to parse path segment [$segment]");
}

if (! array_key_exists('attribute_name', $matches)) {
throw new \Exception("Unable to find attribute_name in path segment [$segment]");
}

return $matches;
});
}
}
11 changes: 11 additions & 0 deletions src/Rm/Common/Archetyped/Pathable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@
namespace BigPictureMedical\OpenEhr\Rm\Common\Archetyped;

use BigPictureMedical\OpenEhr\Base\FoundationTypes\Any;
use BigPictureMedical\OpenEhr\PathQuery;

abstract class Pathable extends Any
{
public string $_type = 'PATHABLE';

public function itemAtPath(string $path): mixed
{
return (new PathQuery($path))->find($this);
}

public function itemsAtPath(string $path): array
{
return (new PathQuery($path))->findList($this);
}
}
127 changes: 127 additions & 0 deletions tests/PathQueryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

namespace Tests;

use BigPictureMedical\OpenEhr\PathQuery;
use BigPictureMedical\OpenEhr\Rm\Common\Generic\PartySelf;
use BigPictureMedical\OpenEhr\Rm\Composition\Composition;
use BigPictureMedical\OpenEhr\Rm\Composition\Content\Entry\Evaluation;
use BigPictureMedical\OpenEhr\Rm\DataStructures\ItemStructure\ItemTree;
use BigPictureMedical\OpenEhr\Rm\DataStructures\Representation\Cluster;
use BigPictureMedical\OpenEhr\Rm\DataStructures\Representation\Element;
use BigPictureMedical\OpenEhr\Rm\DataTypes\Text\CodePhrase;
use BigPictureMedical\OpenEhr\Rm\DataTypes\Text\DvCodedText;
use BigPictureMedical\OpenEhr\Rm\DataTypes\Text\DvText;
use PHPUnit\Framework\TestCase;

class PathQueryTest extends TestCase
{
public function test_it_finds_a_single_item()
{
$composition = $this->makeComposition();

$query = 'content[test-EVALUATION.test.v0]/data/items[at0002]/items[at0003]/value/value';

$result = (new PathQuery($query))->find($composition);

$this->assertSame('Test value 1', $result);
}

public function test_it_handles_missing_paths_when_finding_a_single_item()
{
$composition = $this->makeComposition();

$query = 'content[missing]/data/items[at0002]/items[at0003]/value/value';

$result = (new PathQuery($query))->find($composition);

$this->assertSame(null, $result);
}

public function test_it_finds_a_list()
{
$composition = $this->makeComposition();

$query = 'content[test-EVALUATION.test.v0]/data/items[at0002]/items[at0003]/value/value';

$result = (new PathQuery($query))->findList($composition);

$this->assertSame([
'Test value 1',
'Test value 2',
'Test value 3',
'Test value 4',
], $result);
}

public function test_it_handles_missing_paths_when_finding_a_list()
{
$composition = $this->makeComposition();

$query = 'content[missing]/data/items[at0002]/items[at0003]/value/value';

$result = (new PathQuery($query))->findList($composition);

$this->assertSame([], $result);
}

private function makeComposition(): Composition
{
return new Composition(
archetype_node_id: 'test-COMPOSITION.test.v0',
name: new DvText(value: 'Test composition'),
language: CodePhrase::make('test', 'test'),
territory: CodePhrase::make('test', 'test'),
category: DvCodedText::make('test', 'test', 'test'),
composer: new PartySelf(),
content: [
new Evaluation(
archetype_node_id: 'test-EVALUATION.test.v0',
name: new DvText(value: 'Test evaluation'),
language: CodePhrase::make('test', 'test'),
encoding: CodePhrase::make('test', 'test'),
subject: new PartySelf(),
provider: new PartySelf(),
data: new ItemTree(
archetype_node_id: 'at0001',
name: new DvText(value: 'data'),
items: [
new Cluster(
name: new DvText(value: 'Test cluster'),
archetype_node_id: 'at0002',
items: [
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 1')
),
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 2')
),
]
),
new Cluster(
name: new DvText(value: 'Test cluster'),
archetype_node_id: 'at0002',
items: [
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 3')
),
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 4')
),
]
),
],
),
),
],
);
}
}
100 changes: 100 additions & 0 deletions tests/PathableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace Tests;

use BigPictureMedical\OpenEhr\Rm\Common\Generic\PartySelf;
use BigPictureMedical\OpenEhr\Rm\Composition\Composition;
use BigPictureMedical\OpenEhr\Rm\Composition\Content\Entry\Evaluation;
use BigPictureMedical\OpenEhr\Rm\DataStructures\ItemStructure\ItemTree;
use BigPictureMedical\OpenEhr\Rm\DataStructures\Representation\Cluster;
use BigPictureMedical\OpenEhr\Rm\DataStructures\Representation\Element;
use BigPictureMedical\OpenEhr\Rm\DataTypes\Text\CodePhrase;
use BigPictureMedical\OpenEhr\Rm\DataTypes\Text\DvCodedText;
use BigPictureMedical\OpenEhr\Rm\DataTypes\Text\DvText;
use PHPUnit\Framework\TestCase;

class PathableTest extends TestCase
{
public function test_it_finds_an_item_at_path()
{
$composition = $this->makeComposition();

$result = $composition->itemAtPath('content[test-EVALUATION.test.v0]/data/items[at0002]/items[at0003]/value/value');

$this->assertSame('Test value 1', $result);
}

public function test_it_finds_items_at_path()
{
$composition = $this->makeComposition();

$result = $composition->itemsAtPath('content[test-EVALUATION.test.v0]/data/items[at0002]/items[at0003]/value/value');

$this->assertSame([
'Test value 1',
'Test value 2',
'Test value 3',
'Test value 4',
], $result);
}

private function makeComposition(): Composition
{
return new Composition(
archetype_node_id: 'test-COMPOSITION.test.v0',
name: new DvText(value: 'Test composition'),
language: CodePhrase::make('test', 'test'),
territory: CodePhrase::make('test', 'test'),
category: DvCodedText::make('test', 'test', 'test'),
composer: new PartySelf(),
content: [
new Evaluation(
archetype_node_id: 'test-EVALUATION.test.v0',
name: new DvText(value: 'Test evaluation'),
language: CodePhrase::make('test', 'test'),
encoding: CodePhrase::make('test', 'test'),
subject: new PartySelf(),
provider: new PartySelf(),
data: new ItemTree(
archetype_node_id: 'at0001',
name: new DvText(value: 'data'),
items: [
new Cluster(
name: new DvText(value: 'Test cluster'),
archetype_node_id: 'at0002',
items: [
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 1')
),
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 2')
),
]
),
new Cluster(
name: new DvText(value: 'Test cluster'),
archetype_node_id: 'at0002',
items: [
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 3')
),
new Element(
archetype_node_id: 'at0003',
name: new DvText(value: 'Test element'),
value: new DvText(value: 'Test value 4')
),
]
),
],
),
),
],
);
}
}

0 comments on commit 95f9e9e

Please sign in to comment.