Skip to content

Commit

Permalink
Use HL7 factory as the primary method of creating Message object
Browse files Browse the repository at this point in the history
  • Loading branch information
senaranya committed Jan 11, 2025
1 parent 985f02f commit 0a96e6c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 47 deletions.
63 changes: 32 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
</p>

**Important: "new Message()" is deprecated and could be removed in a future release. Use HL7 factory class instead. See documents below <br><br>

Final releases for old PHP versions: <br>
-> PHP 7.0/7.1 => [1.5.4](https://github.com/senaranya/HL7/tree/1.5.4)<br>
-> PHP 7.2 => [2.0.2](https://github.com/senaranya/HL7/tree/2.0.2)<br>
-> PHP 7.4 => [2.1.7](https://github.com/senaranya/HL7/tree/2.1.7)**
-> PHP 8.1 => [3.1.7](https://github.com/senaranya/HL7/tree/3.1.7)**

## Introduction

Expand All @@ -35,10 +37,8 @@ use Aranyasen\HL7\Segments\MSH; // If MSH is used
### Parsing
```php
// Create a Message object from a HL7 string
$message = HL7::from("MSH|^~\\&|1|")->createMessage(); // Returns Message object

// Or, using Message class...
$message = new Message("MSH|^~\\&|1|\rPID|||abcd|\r"); // Either \n or \r can be used as segment endings
$message = HL7::from("MSH|^~\\&|1|")->create();
$message = HL7::from("MSH|^~\\&|1|\rPID|||abcd|\r")->create(); // Creates Message object with two segments with \r as segment ending (\n can also be used)

// Get string form of the message
echo $message->toString(true);
Expand All @@ -52,43 +52,41 @@ $message->getFirstSegmentInstance('ABC'); // Returns the first ABC segment. Same
$message->hasSegment('ABC'); // return true or false based on whether PID is present in the $message object

// Check if a message is empty
$message = new Message();
$message = HL7::create();
$message->isempty(); // Returns true
```

### Composing new messages
```php
// The class `HL7` can be used to build HL7 object. It is a factory class with various helper methods to help build a hl7.
$message = HL7::build()->createMessage(); // Creates an empty message
$message = HL7::build()->create(); // Creates an empty message

// The HL7 factory class provides methods that can be chained together in a fluent fashion
$message = HL7::build()
->withComponentSeparator('#')
->withFieldSeparator('-')
->createMessage();

// Or, using Message class...
$message = new Message();
->create();
```
#### Message constructor parameters
#### Configuring HL7 messages
```php
// When a message is composed using Message class, there are multiple parameters available to define the properties of the HL7.
// Note: All of these properties are available as fluent methods in HL7 factory class (shown above). So it's recommended to use that for readability

// Creating multiple message objects may have an unexpected side effect: segments start with wrong index values (Check tests/MessageTest for explanation)...
// Use 4th argument as true, or call resetSegmentIndices() on $message object to reset segment indices to 1
$message = new Message("MSH|^~\&|||||||ORM^O01||P|2.3.1|", null, true, true);
// So to reset segment indices to 1:
HL7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->resetIndices()->create(); // Use resetIndices() while creating a new message
$message->resetSegmentIndices(); // or call resetSegmentIndices() on an existing $message object
// ... any segments added here will now start index from 1, as expected.
```
```php
// Sometimes you may want to have exact index values, rather than auto-incrementing for each instance of a segment
// Use 5th argument as false...
$hl7String = "MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|1||22^BB|\n";
$message = new Message($hl7String, null, true, true, false); $// $message contains both OBXs with given indexes in the string
$message = HL7::from($hl7String)
->autoIncrementIndices(false)
->create();
// $message now contains both OBXs with given indexes in the string
```
```php
// Create a segment with empty sub-fields retained
$message = new Message("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|", null, true); // Third argument 'true' forces to keep all sub-fields
// Ensure empty sub-fields are not removed
// Note: We should perhaps _always_ use this while creating a message object, as we don't want empty subfields removed. In future versions, this will be the default
$message = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")->keepEmptySubfields()->create();
$pv1 = $message->getSegmentByIndex(1);
$fields = $pv1->getField(3); // $fields is ['', 'AAAA1', '', '', 'BB']

Expand All @@ -98,16 +96,19 @@ $message->toString(true); // Returns "MSH|^~\&|1\nABC|||xxx\n"
(new Connection($ip, $port))->send($message); // Sends the message without ending bar-characters (details on Connection below)

// Specify custom values for separators, HL7 version etc.
$message = new Message("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|", ['SEGMENT_SEPARATOR' => '\r\n', 'HL7_VERSION' => '2.3']);
$message = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")
->withSegmentSeparator('\r\n')
->withHL7Version('2.3')
->create();

// Segment with separator character (~) creates sub-arrays containing each sub-segment
$message = new Message("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1"); // Creates [[3,0], [4,1]]
$message = HL7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")->create(); // Creates [[3,0], [4,1]]

// To create a single array instead, pass 'true' as 6th argument. This may be used to retain behavior from previous releases
// Notice: Since this leads to a non-standard behavior, it may be removed in future
$message = new Message("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1", null, false, false, true, true); // Creates ['3', '0~4', '1']
// or
$message = new Message("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1", doNotSplitRepetition: true); // Creates ['3', '0~4', '1']
// To create a single array instead, use doNotSplitRepetition()
// Note: Since this leads to a non-standard behavior, it may be removed in future
$message = HL7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")
->doNotSplitRepetition()
->create(); // Creates ['3', '0~4', '1']
```

#### Handling segments and fields
Expand All @@ -124,7 +125,7 @@ $abc->setField(1, 'xyz');
$abc->setField(2, 0);
$abc->setField(4, ['']); // Set an empty field at 4th position. 2nd and 3rd positions will be automatically set to empty
$abc->clearField(2); // Clear the value from field 2
$message->setSegment($abc, 1); // Message is now: "MSH|^~\&|||||20171116140058|||2017111614005840157||2.3|\nABC|xyz|\n"
$message->insertSegment($abc, 1); // Message is now: "MSH|^~\&|||||20171116140058|||2017111614005840157||2.3|\nABC|xyz|\n"

// Create a defined segment (To know which segments are defined in this package, look into Segments/ directory)
// Advantages of defined segments over custom ones (shown above) are 1) Helpful setter methods, 2) Auto-incrementing segment index
Expand All @@ -145,7 +146,7 @@ Side note: In order to run Connection you need to install PHP ext-sockets [https
```php
$ip = '127.0.0.1'; // An IP
$port = '12001'; // And Port where a HL7 listener is listening
$message = new Message($hl7String); // Create a Message object from your HL7 string
$message = HL7::from($hl7String)->create(); // Create a Message object from your HL7 string. See above for details

// Create a Socket and get ready to send message. Optionally add timeout in seconds as 3rd argument (default: 10 sec)
$connection = new Connection($ip, $port);
Expand All @@ -172,12 +173,12 @@ else {
```
Create an ACK response from a given HL7 message:
```php
$msg = new Message("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|", null, true);
$msg = HL7::from("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|")->keepEmptySubfields()->create();
$ackResponse = new ACK($msg);
```
Options can be passed while creating ACK object:
```php
$msg = new Message("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|", null, true);
$msg = HL7::from("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|")->keepEmptySubfields()->create();
$ackResponse = new ACK($msg, null, ['SEGMENT_SEPARATOR' => '\r\n', 'HL7_VERSION' => '2.5']);
```

Expand Down
12 changes: 12 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## 4.0
### Breaking changes
- First segment in the Message object can only be MSH. E.g. this will now fail: `(new Message())->addSegment(new Segment('XXX'))`, so it has to be `(new Message("MSH|^~\\&|1|\r"))->addSegment(new Segment('XXX'))`
- In `insertSegment()` method, only MSH can be inserted now in the 0th index
- Replaced all `InvalidArgumentException` with `HL7Exception`, so update the catches accordingly
- Dropped support for PHP 8.1. Minimum version required is now 8.2. So if your project can not be upgraded to 8.2, you'll need to continue using 3.x version
### Non-breaking changes
- Using `new Message()` is deprecated, and might be removed in a future version. Use HL7 factory to create a new HL7 object instead. See readme on how to use it
- `setSegment` method is deprecated. Use `insertSegment` instead
- In `insertSegment`, added a replace parameter to replace a current segment in place
- Added `replaceSegment` method
- Added `create` method in HL7 factory that is an alias to `createMessage`
8 changes: 8 additions & 0 deletions src/HL7.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ public function createMessage(): Message
);
}

/**
* @throws HL7Exception
*/
public function create(): Message
{
return $this->createMessage();
}

/**
* Create a new MSH segment, using the global HL7 variables as defaults.
* @throws Exception
Expand Down
42 changes: 26 additions & 16 deletions tests/HL7Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@ class HL7Test extends TestCase
*/
#[Test] public function hl7_message_can_be_created_using_factory_class(): void
{
$msg = HL7::build()->createMessage();
$msg = HL7::build()->create();
self::assertIsObject($msg);
self::assertSame(HL7\Message::class, $msg::class);
}

/**
* @throws HL7Exception
*/
#[Test] public function hl7_message_can_be_created_using_create_alias_method(): void
{
$msg = HL7::build()->create();
self::assertIsObject($msg);
self::assertSame(HL7\Message::class, $msg::class);
}
Expand All @@ -26,7 +36,7 @@ class HL7Test extends TestCase
*/
#[Test] public function factory_creates_a_msh_segment_with_defaults_when_no_message_string_provided(): void
{
$msg = HL7::build()->createMessage();
$msg = HL7::build()->create();
self::assertStringContainsString('MSH|^~\&|||||', $msg->toString(true));
}

Expand All @@ -35,7 +45,7 @@ class HL7Test extends TestCase
*/
#[Test] public function a_message_can_be_built_from_a_provided_hl7_string(): void
{
$msg = HL7::from("MSH|^~\\&|1|\rABC|||xxx|\r")->createMessage();
$msg = HL7::from("MSH|^~\\&|1|\rABC|||xxx|\r")->create();
self::assertSame("MSH|^~\\&|1|\nABC|||xxx|\n", $msg->toString(true));
}

Expand All @@ -47,7 +57,7 @@ class HL7Test extends TestCase
self::markTestSkipped('property cannot be overridden yet in Message() constructor');
$msg = HL7::from("MSH|^~\\&|1|\rABC|||xxx|\r")
->withEscapeCharacter('=')
->createMessage();
->create();
self::assertSame("MSH|^~=&|1|\nABC|||xxx|\n", $msg->toString(true));
}
/**
Expand All @@ -63,13 +73,13 @@ class HL7Test extends TestCase
->withRepetitionSeparator('`')
->withEscapeCharacter('=')
->withHL7Version('555.666')
->createMessage();
->create();
self::assertStringContainsString('MSH#*`=}#', $msg->toString(true));
}

#[Test] public function empty_subfields_can_be_retained_if_needed(): void
{
$msg = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")->keepEmptySubfields()->createMessage();
$msg = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")->keepEmptySubfields()->create();
$this->assertSame(['', 'AAAA1', '', '', 'BB'], $msg->getSegmentByIndex(1)->getField(3));
}

Expand All @@ -79,7 +89,7 @@ class HL7Test extends TestCase
#[Test] public function indices_of_each_segment_can_be_reset_when_message_is_composed(): void
{
// Create a message with a PID segment
$msg1 = HL7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
$msg1 = HL7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg1->addSegment(new PID());
self::assertSame(
"MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|1|\n",
Expand All @@ -88,7 +98,7 @@ class HL7Test extends TestCase
);

// Create another message with a PID segment
$msg2 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
$msg2 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg2->addSegment(new PID());
self::assertSame(
"MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|2|\n",
Expand All @@ -99,20 +109,20 @@ class HL7Test extends TestCase
// Create another message with a PID segment, this time with resetIndices()
$msg3 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")
->resetIndices()
->createMessage();
->create();
$msg3->addSegment(new PID());
self::assertSame("MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|1|\n", $msg3->toString(true), 'PID index resets to 1');

// Create a message with a PID segment
$msg4 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
$msg4 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg4->addSegment(new PID());
self::assertSame(
"MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|2|\n",
$msg4->toString(true),
'PID index gets incremented'
);

$msg5 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
$msg5 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg5->resetSegmentIndices();
$msg5->addSegment(new PID());
self::assertSame("MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|1|\n", $msg5->toString(true), 'PID index resets to 1');
Expand All @@ -126,7 +136,7 @@ class HL7Test extends TestCase
$hl7String = "MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|1||22^BB|\n";

$msg = Hl7::from($hl7String)
->createMessage();
->create();
self::assertSame(
"MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|2||22^BB|\n",
$msg->toString(true),
Expand All @@ -135,15 +145,15 @@ class HL7Test extends TestCase

$msg = Hl7::from($hl7String)
->autoIncrementIndices(false)
->createMessage();
->create();
self::assertSame(
"MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|1||22^BB|\n",
$msg->toString(true)
);

$msg = Hl7::from("MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "PID|||3^0\n")
->autoIncrementIndices(false)
->createMessage();
->create();
self::assertSame(
"MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "PID|||3^0|\n",
$msg->toString(true),
Expand All @@ -157,15 +167,15 @@ class HL7Test extends TestCase
#[Test] public function repetition_separation_character_can_be_ignored(): void
{
$message = Hl7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")
->createMessage();
->create();
self::assertIsArray(
$message->getSegmentByIndex(1)->getField(3),
'By default repetition should be split into array'
);

$message = Hl7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")
->doNotSplitRepetition() // Ignore repetition separator character
->createMessage();
->create();
$patientIdentifierList = $message->getSegmentByIndex(1)->getField(3);
self::assertIsArray($patientIdentifierList);
self::assertSame(['3', '0~4', '1'], $patientIdentifierList);
Expand Down

0 comments on commit 0a96e6c

Please sign in to comment.