›
byrcsc/laravel-data-sync · 1.x
Describe one file feed, its model, mapping, matching, and write behavior in a class.
A sync definition is a class extending SyncDefinition. It answers five
required questions and adjusts a dozen optional ones. Behavior lives here;
config/data-sync.php holds only infrastructure.
use ByRcsc\LaravelDataSync\Contracts\Reader as ReaderContract;
use ByRcsc\LaravelDataSync\Definitions\Source;
use ByRcsc\LaravelDataSync\Definitions\SyncDefinition;
final class ProductsSync extends SyncDefinition
{
public function source(): Source; // where files come from
public function format(): ReaderContract; // how to read them
public function model(): string; // the destination Eloquent model
public function fields(): array; // the mapping, as Field objects
public function matchOn(): array; // the columns that identify a row
}Everything else has a default:
| Method | Default | Documented in |
|---|---|---|
name() | kebab-case class basename minus Sync | below |
rules() | [] | Validation and hooks |
writeMode() | WriteMode::Upsert | Matching and writing |
onDuplicate() | DuplicatePolicy::LastWins | Matching and writing |
onUnknownColumns() | UnknownColumnPolicy::Ignore | Field mapping |
captureUnmappedInto() | null | Field mapping |
chunkSize() | 1000 | Matching and writing |
maxErrors() | null | Matching and writing |
atomic() | false | Matching and writing |
captureFailedPayloads() | true | Sensitive data |
prepare() | returns the row unchanged | Validation and hooks |
withValidator() | no-op | Validation and hooks |
beforeSave() | not declared | Validation and hooks |
afterSave() | not declared | Validation and hooks |
queueConnection() | null | Queues and workers |
queue() | null | Queues and workers |
schedule() | not declared | Scheduling |
The name is what you type on the command line, what the ledger and runs record,
and what data-sync.overrides keys on. It is derived from the class basename
with a trailing Sync removed, then kebab-cased:
| Class | Name |
|---|---|
ProductsSync | products |
SupplierPricingSync | supplier-pricing |
Inventory | inventory |
Override it when the class name is wrong for operators:
public function name(): string
{
return 'supplier-catalog';
}Two definitions may not share a name. A collision throws an
InvalidConfigurationException at boot.
// config/data-sync.php
'syncs' => [
App\Syncs\ProductsSync::class,
App\Syncs\StatementsTransfer::class,
],Order does not matter. sync:run with no argument runs every enabled
definition, in registration order.
Disable one without removing it:
'overrides' => [
'products' => ['enabled' => false],
],A disabled definition is skipped by sync:run and is not scheduled. It is still
validated, and still addressable by name if you run it explicitly.
Every definition is built with app()->make(), so constructor injection works:
final class ProductsSync extends SyncDefinition
{
public function __construct(
private readonly CurrencyConverter $currency,
) {}
public function fields(): array
{
return [
Field::make('price_cents', from: 'Price')
->transform(fn (string $value): int => $this->currency->toCents($value)),
];
}
}Definitions are resolved fresh on each use, including inside queued jobs, so
they must not hold per-run state. The one piece of mutable state the base class
carries is the payload-capture flag set by withoutPayloadCapture().
Resolving a definition validates it. sync:run, sync:status, sync:doctor,
and the queued jobs all go through the same path, so a broken definition fails
loudly and early rather than half way through a file.
A SyncDefinition must satisfy all of the following:
fields() is non-empty and contains only Field instances, with no duplicate
attribute names.model() names an existing class extending Illuminate\Database\Eloquent\Model.matchOn() is non-empty, and every entry names a mapped field.chunkSize() is at least 1, and maxErrors(), when set, is not negative.onUnknownColumns() is Capture, captureUnmappedInto() names an
attribute cast to array, json, object, collection, encrypted:array,
or encrypted:collection.Failures throw an InvalidDefinitionException naming the class and the problem.
A TransferDefinition shares source(), name(), the queue methods, and
schedule(), and replaces the row-mapping surface with matchFilename() and
destination(). Both kinds go in the same syncs array and run through the
same commands. See file transfers.
Field DSL.