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

Uxdm Laravel Package

divineomega/uxdm

UxDM is a PHP data mapping library that moves data between sources and destinations like databases, CSV, JSON, and arrays through a consistent object-oriented API, simplifying imports, exports, and migrations.

Deep Wiki
Context7

Getting Started

UXDM (Unified Data Migration) is a small, composable pipeline library: a Source yields rows, a Destination consumes them, and Fields (optionally with Mappers) describe how each value travels. Everything else is plumbing.

composer require divineomega/uxdm

Minimal migration — copy a table from one database to another:

use DivineOmega\UXDM\Objects\Sources\PDOSource;
use DivineOmega\UXDM\Objects\Destinations\PDODestination;
use DivineOmega\UXDM\Objects\Migrator;
use DivineOmega\UXDM\Objects\Field;

$source      = new PDOSource($legacyPdo, 'users', ['id', 'name', 'email']);
$destination = new PDODestination($newPdo, 'users', ['id', 'name', 'email']);

$migrator = new Migrator();
$migrator->setSource($source)
         ->setDestination($destination)
         ->setFields([
             Field::create()->setSourceFieldName('id')->setDestinationFieldName('id'),
             Field::create()->setSourceFieldName('name')->setDestinationFieldName('name'),
             Field::create()->setSourceFieldName('email')->setDestinationFieldName('email'),
         ])
         ->migrate();

Where to look first (in vendor/divineomega/uxdm/src/Objects/):

  • Migrator.php — the only orchestrator; read this before anything else.
  • Sources/ — what you can read from. If your format isn't there, you'll write one.
  • Destinations/ — what you can write to. Same rule.
  • Mappers/ — the built-in transforms; these tell you the shape of the FieldMapper interface.
  • DataRow / DataItem — the in-flight payload objects every mapper and destination touches.

First real use case: a one-off legacy-table copy or CSV/JSON import into your app's schema, run as an artisan command. It's a scripting library, not a sync engine — treat it as the body of a php artisan migrate:legacy-users command, not as a daemon.

Implementation Patterns

Rename-and-transform is the sweet spot. Fields decouple source column names from destination column names, and mappers stack per field:

Field::create()
    ->setSourceFieldName('full_name')
    ->setDestinationFieldName('name')
    ->addMapper(new Trim())
    ->addMapper(new CollapseWhitespace());

Custom mappers for anything domain-specific. The built-ins cover trimming and simple value swaps; business rules (phone normalisation, status enum translation, hashing) belong in your own class implementing the FieldMapper interface — one small class per concern, then reuse it across migrations:

class NormalisePhoneMapper implements FieldMapper
{
    public function map(DataItem $dataItem): DataItem
    {
        $dataItem->value = preg_replace('/\D+/', '', (string) $dataItem->value);
        return $dataItem;
    }
}

Batch for memory. Large tables will blow up if everything is pulled at once. Set setFieldsPerBatch() (a few hundred to a couple of thousand) and verify peak memory against a production-sized snapshot before you run it anywhere real.

Progress feedback when it's a long job. Attach a Symfony ProgressBar via setProgressBar($output) inside a command; it makes a 20-minute migration tolerable and gives you an ETA to put in the ops ticket.

Config-driven multi-table runs. For a migration of ten related tables, don't write ten scripts. Put table names, column lists, and mapper class names in a config array and loop, constructing a fresh Migrator per table. Order the array to respect foreign keys, or drop/re-add constraints around the run.

Idempotency is your problem, not the library's. Destinations generally just write. For re-runnable migrations, either truncate the target first, wrap the run in a transaction and roll back on failure, or drive the destination through an upsert-capable custom Destination that wraps your ORM instead of a raw PDO insert.

Integration tip — wrap in a command with a dry-run flag. Point the destination at an ArrayDestination/in-memory SQLite or a NullDestination you write yourself, run the mappers, and dump the first N rows. That single feature catches 80% of mapping mistakes before they hit a database.

Testing. Use SQLite in-memory PDOSource/PDODestination pairs in a PHPUnit test with three or four representative rows — including a null, an empty string, and a Unicode name. That's enough to lock down mapper behaviour and catch regressions when you touch a shared mapper.

Gotchas and Tips

Field count and order matter. The fields array is effectively your insert column list; a mismatch with the destination's declared columns produces confusing failures rather than a clear error. Keep source columns, fields, and destination columns listed in the same order and review them side by side.

Mappers are per-field, not per-row. There is no hook for "compute slug from name" inside a mapper, because a mapper only sees one DataItem. Options: a custom source that pre-computes derived values, a custom destination that post-processes the DataRow, or two passes. Plan for this — it's the most common surprise.

Types don't survive. Everything comes through as whatever PDO/your source hands back; "0", 0, false, and null are easy to conflate. Normalise explicitly in a mapper rather than trusting the destination's coercion, especially for booleans and nullable dates.

Cross-system date/time zones. Source datetimes are almost always naive strings. Convert to UTC in a mapper and make the assumption explicit in a comment; you will not remember which server's clock it was in three months later.

Schema is not created for you. Destinations insert into existing structures. Create tables, indexes, and constraints yourself, and add indexes after the bulk load, not before.

Debugging technique. When a row fails, temporarily prepend a CallableMapper-style mapper that var_dumps the DataItem and the source row, or catch the exception in the command and log the current row's primary key. The library's stack traces point at the destination, not at which row — the row identity is what you actually need.

Extension points are Source, Destination, and FieldMapper implementations. Writing a Source for an oddball API or a Destination that pipes through your Eloquent models is the intended way to bend the library, and it's a small amount of code.

Verify names against your installed version. The package is at ~164 stars with a small surface area and occasional API churn between releases; the class names and constructor signatures above are the common shape but should be confirmed by reading src/Objects/ in vendor/ rather than trusting memory or blog posts. Pin the version in composer.json — do not float ^ on a library this small if a migration script depends on it.

Licence. LGPL-3.0 permits commercial use and dynamic linking, but modifications to the library itself carry source-disclosure obligations. If you patch a mapper in vendor/, you're in that territory — prefer writing your own mapper classes in your own namespace.

Maintenance risk. With a low star count and infrequent releases, assume you may need to fork or vendor the code. Keep migrations thin and declarative so replacing the engine later is a day's work, not a rewrite.

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