Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Gedcom Laravel Package

laravel-liberu/php-gedcom

A PHP library for reading and writing GEDCOM genealogy files. Parse individuals, families, events, and sources into structured objects, or generate valid GEDCOM for export, with a clean API that fits neatly into Laravel and other PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

liberu-genealogy/php-gedcom is a plain PHP GEDCOM parser/writer (a maintained fork of the classic php-gedcom), not a Laravel package with a service provider — so the first step is just:

composer require liberu-genealogy/php-gedcom

Where to look first

  1. The tests/ directory. In this fork the tests are the most reliable documentation of the current API surface (the README lags behind the code).
  2. src/PhpGedcom/Parser.php — the entry point for reading.
  3. src/PhpGedcom/Record/ — the object graph you get back (Gedcom, Indi, Fam, Sour, Note, Obje, and per-record sub-records like Name, Birth, Resn).
  4. The write/export layer (Write, and as of v4.2.0 a GEDCOM-X export path) if you need to emit files or JSON.

First use case — import an uploaded file

use PhpGedcom\Parser;

$gedcom = (new Parser())->parse($request->file('gedcom')->getRealPath());

$count = 0;
foreach ($gedcom->getIndi() as $indi) {
    $name = $indi->getName()[0] ?? null;   // sub-record, not a string
    $count++;
}

Wrap this in your own service class (App\Services\Gedcom\GedcomReader) and bind it as a singleton if you want it injectable — there is no package config to publish, and none of the library classes are container-aware.

Implementation Patterns

Never parse in the request cycle. Importing a real family file (tens of thousands of individuals) belongs in a queued job. Pattern:

// app/Jobs/ImportGedcom.php
$path = Storage::disk('local')->path($this->storedPath);
$gedcom = (new Parser())->parse($path);

Store the uploaded file first, pass the path to the job, and record progress so the UI can poll.

Two-pass persistence. GEDCOM is a pointer graph (@I123@, @F45@), so do not try to create Eloquent rows and relations in one sweep:

  1. Pass 1: create people / families rows keyed by external_id = the xref (@I123@), no foreign keys yet.
  2. Pass 2: resolve FAMC/FAMS on individuals and HUSB/WIFE/CHIL on families into your real FKs.

This is also the simplest way to survive records referencing individuals defined later in the file.

Keep a raw column. Add raw_gedcom (text) on your person/family tables, or a gedcom_records table holding the original lines per xref. The object graph is lossy for vendor tags; the raw text lets you re-export or re-import without data loss when the package's mapping improves.

Export/download. Build (or mutate) record objects and hand them to the writer, then stream the result:

return response()->streamDownload(function () use ($gedcom) {
    echo (new Write())->gedcom($gedcom);
}, 'family.ged', ['Content-Type' => 'text/plain']);

Model → GEDCOM. For "export my database" you generally construct records programmatically rather than parsing an existing file. Put this behind an Exporter class implementing a small interface, because the write API is where this fork changes most between minors.

GEDCOM-X (new in v4.2.0). Per the release notes, GEDCOM→GEDCOM-X mapping landed (PR #95, still marked WIP) alongside UID/_UID support across all record types with GedcomX export (PR #96). Practical use: expose a /api/people/{id}/gedcomx JSON endpoint for interop with FamilySearch-style tooling instead of hand-writing a serializer. Treat it as experimental — feature-flag it, don't make it the only export path, and assert on it in your test suite before relying on it.

Commands and testing. A php artisan gedcom:import {path} --dry-run command pays for itself immediately for debugging. For tests, keep small real-world fixtures covering both GEDCOM 5.5.1 and 7.0, plus one file with vendor _-prefixed tags and _UID lines, and assert record counts and a couple of spot-checked names/dates.

Gotchas and Tips

Character encoding will bite you first. GEDCOM 5.5.1 declares CHAR ANSI|UTF-8|UNICODE|ASCII. UNICODE means UTF-16, and legacy files are often Windows-1252 despite the header. Normalise to UTF-8 (strip the BOM, transcode if needed) before handing the file to the parser, or you'll get mojibake in names and places that is painful to clean up later.

Memory. The parser builds the whole tree in memory. A 200k-individual file will exceed a default memory_limit; raise it for the worker, or split/stream at the file level for very large imports. Also unset($gedcom) after the import to release the object graph before the job finishes.

Pointers are strings, not objects. HUSB, WIFE, CHIL, FAMC, FAMS, SOUR, NOTE, OBJE come back as @X@ references. The library does not resolve them into linked objects for you — build your own ['@I1@' => $person] map. This is the single most common source of "why is this null" bugs.

Dates are a grammar, not a date. Values like ABT 1900, BEF 12 MAR 1850, BET 1880 AND 1890, (?), and Julian/double-dated forms (1699/1700) are all legal. Store the raw string and a parsed/normalised form with a qualifier column; do not cast directly to Carbon on import or you'll throw on real files.

CONC and CONT continuation. Long notes and addresses are split across lines. If you read NOTE/TEXT sub-records directly you must join CONC (no separator) and CONT (newline) in order. If you skip this, every long biography in your import will be truncated at ~248 chars.

Names. Don't explode(' ', $name). NAME carries GIVN, SURN, NICK, NPFX, NSFX sub-tags, and married names live in the non-standard _MARNM. Read the sub-records when present and fall back to splitting on the /.../ surname delimiters.

Round-trip loss. Tags the library doesn't model — especially vendor _-prefixed ones — are typically dropped when you parse and re-write. If round-trip fidelity matters, that's what the raw_gedcom column is for; alternatively subclass/extend the record classes to capture them, which is the main extension point this library offers.

UID / _UID in v4.2.0. These are now parsed on all record types and included in GedcomX output — useful for stable identity across re-imports (map UID → your external_uid and use it for upserts instead of names). Note the underscore variant is non-standard, so treat _UID as advisory and deduplicate defensively.

Pin the version. This is a fast-moving fork maintained largely by automated contributions; the 4.x minors have shifted the API. Use a caret constraint on 4.2 with a committed lockfile, read the diff before bumping, and keep your own thin adapter layer (GedcomReader/GedcomWriter) so an upstream rename touches one file rather than fifty.

Debugging. When something returns unexpectedly, parse a three-line hand-written GEDCOM string and var_dump the record object — far faster than reading source. Wrap parser calls in try/catch and log the offending line number from the exception; malformed files fail deep in the line loop.

No framework integration to lean on. No service provider, no facade, no config file, no published migrations. That's fine — but it means validation, encoding normalisation, chunking, and error reporting are all yours to write, and they're the parts that actually determine whether the import works on a user's real 30-year-old file.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin