waleedhu/laravel-hl7

Hospital HL7v2 feeds as native Laravel events: MLLP listener, typed events, honest ACKs, test fakes.

Maintainers

Package info

github.com/WaleedHU/laravel-hl7

pkg:composer/waleedhu/laravel-hl7

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-08-06 05:13 UTC

This package is auto-updated.

Last update: 2026-08-06 09:12:08 UTC


README

Hospital HL7v2 feeds as native Laravel events.

Hospitals broadcast everything that happens — admissions, discharges, lab results, appointments — as HL7v2 messages over MLLP. This package gives that stream the Laravel treatment: run one artisan command, listen for typed events, and let the acknowledgement the hospital engine receives reflect what your application actually did.

composer require waleedhu/laravel-hl7

Requires PHP 8.2+ and Laravel 11, 12 or 13.

Listening

php artisan hl7:listen            # binds hl7.host:hl7.port (default 0.0.0.0:2575)
php artisan hl7:listen --port=6000

Point the hospital integration engine (Mirth, Rhapsody, Cloverleaf, ...) at that address and handle messages the way you handle any Laravel event:

use WaleedHu\Hl7\Events\PatientAdmitted;

Event::listen(function (PatientAdmitted $event) {
    Patient::updateOrCreate(
        ['mrn' => $event->patientId()],
        ['name' => $event->patientName(), 'ward' => $event->assignedLocation()],
    );
});

Events

MSH-9 Event Readers
ADT^A01 PatientAdmitted patientId(), patientName(), assignedLocation()
ADT^A02 PatientTransferred same as above
ADT^A03 PatientDischarged same as above
ADT^A08 PatientUpdated same as above
ORU^R01 LabResultReceived patient readers + observations()
SIU^S12 AppointmentScheduled patient readers
anything else Hl7MessageReceived

No message is ever dropped: unmapped types dispatch the fallback event. Every event exposes message() (the parsed Aranyasen\HL7\Message), raw() and type(), so nothing is ever out of reach. Readers return null rather than throwing when a segment or field is absent.

observations() returns one entry per OBX segment:

[
    ['id' => 'HGB', 'label' => 'Hemoglobin', 'value' => '10.1', 'units' => 'g/dL', 'range' => '13.5-17.5', 'flag' => 'L'],
]

Map your own types (or override the defaults) in config/hl7.php:

php artisan vendor:publish --tag=hl7-config
'events' => [
    'ADT^A01' => \App\Events\Admission::class,   // override
    'MDM^T02' => \App\Events\DocumentNotified::class, // extend
],

Custom events extend WaleedHu\Hl7\Events\Hl7Event and receive the parsed message.

Acknowledgements are honest

The ACK returned to the engine is tied to what your application did with the message:

Outcome MSA-1 Engine behaviour
Event dispatched, all listeners succeeded AA done
A listener threw AR engine re-sends later
Message could not be parsed AE flagged as error

The listener never dies on a bad message, and MSA-2 always echoes the inbound control ID so engines can correlate.

Testing your integration

No sockets needed — Hl7::receive() pushes a raw message through the exact pipeline the listener uses:

use WaleedHu\Hl7\Facades\Hl7;
use WaleedHu\Hl7\Testing\Samples;

Hl7::fake();
Event::fake();

Hl7::receive(Samples::adtA01(patientId: '12345', patientName: 'DOE^JANE'));

Hl7::assertReceived(PatientAdmitted::class, fn ($e) => $e->patientId() === '12345');
Hl7::assertAcked('AA');
Event::assertDispatched(PatientAdmitted::class);

Samples ships realistic, spec-shaped messages for every supported type — because getting real test messages out of a hospital is the hard part.

Out of scope (for now)

Outbound sending and message building, message persistence and retries, HL7v3 and FHIR. Parsing is delegated to the excellent aranyasen/hl7.

License

MIT.