Payload Mapping
Payload mapping lets you transform Laravel offline sync data at the boundary between client and server. Use it when client-local fields, server database fields, or public sync payloads need to differ without changing the core sync protocol. There are three distinct mappers:
| Mapper | Runs on | Direction | Purpose |
|---|---|---|---|
mutationMapper | Client | Outbound push | Transform a mutation's payload before it is sent to the server |
pushMutationMapper | Server | Inbound push | Transform a mutation's payload before it is applied to the database |
pullSnapshotMapper | Server | Outbound pull | Transform model attributes before they are sent to the client |
Client-side mutation mapper
Registered via ClientSyncRegistry. Transforms the payload of an outbound mutation before it leaves the client.
Use when:
- Adding client-side metadata (e.g. app version, device fingerprint)
- Converting client-local field names to server-expected names
- Filtering fields that should never be sent to the server
use Tether\Client\ClientSyncRegistry;
use Tether\Core\Mutation\Mutation;
public function boot(): void
{
app(ClientSyncRegistry::class)->register(
modelClass: Task::class,
mutationMapper: function (Mutation $mutation): Mutation {
return $mutation->withPayload(array_merge($mutation->getPayload(), [
'client_app_version' => config('app.version'),
]));
},
);
}
Callback signature:
function (Mutation $mutation): Mutation
Server-side push mutation mapper
Registered via SyncRegistry or as tetherPushMutationMapper on the model. Transforms a mutation's payload after it arrives on the server but before it is applied to the database or checked for conflicts.
Use when:
- Stamping the authenticated user ID onto every mutation (prevents clients writing records for other users)
- Renaming or converting fields between client and server schema
- Adding server-computed values the client doesn't know
Via SyncRegistry
use Tether\Server\SyncRegistry;
use Tether\Core\Mutation\Mutation;
public function boot(): void
{
app(SyncRegistry::class)->register(
modelClass: Task::class,
pushMutationMapper: fn (Mutation $mutation, Request $request) => $mutation->withPayload(array_merge(
$mutation->getPayload(),
['user_id' => $request->user()->id],
)),
);
}
Via model method (Syncable)
use Tether\Server\Traits\Syncable;
use Illuminate\Http\Request;
class Task extends Model
{
use Syncable;
public static function tetherPushMutationMapper(
Mutation $mutation,
Request $request,
): Mutation {
return $mutation->withPayload(array_merge($mutation->getPayload(), ['user_id' => $request->user()->id]));
}
}
Callback signature:
function (Mutation $mutation, Request $request): Mutation
Server-side pull snapshot mapper
Registered via SyncRegistry or as tetherPullSnapshotMapper on the model. Transforms a model's attributes before they are serialised into a pull snapshot and sent to the client.
Use when:
- Stripping sensitive server-only fields before clients receive them
- Adding computed or derived values the client needs
- Renaming server field names to client-expected names
Via SyncRegistry
use Tether\Server\SyncRegistry;
use Tether\Core\Sync\Snapshot;
use Illuminate\Support\Arr;
public function boot(): void
{
app(SyncRegistry::class)->register(
modelClass: Task::class,
pullSnapshotMapper: fn (Snapshot $snapshot, $row) => $snapshot->withPayload(Arr::except($snapshot->payload, ['internal_notes', 'admin_flag'])),
);
}
Via model method (Syncable)
use Tether\Server\Traits\Syncable;
use Illuminate\Support\Arr;
class Task extends Model
{
use Syncable;
public static function tetherPullSnapshotMapper(Snapshot $snapshot, self $row): Snapshot
{
return $snapshot->withPayload(Arr::except($snapshot->payload, ['internal_notes', 'admin_flag']));
}
}
Callback signature:
function (Snapshot $snapshot, Model $row): Snapshot
Conflict Resolution
Learn how Laravel Tether detects offline sync conflicts, applies server-wins defaults, returns server state, and supports custom conflict resolvers.
Task Manager
Follow a practical Laravel Tether example that builds a task manager with local offline writes, server sync endpoints, scoped pull, and conflicts.