PHPackages                             andreagroferreira/laravel-sync-tracker - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. andreagroferreira/laravel-sync-tracker

ActiveLibrary[Database &amp; ORM](/categories/database)

andreagroferreira/laravel-sync-tracker
======================================

A Laravel package for tracking entity synchronization status between systems

1.0.0(1y ago)113.0k↓42.3%1MITPHPPHP ^8.1|^8.2|^8.3|^8.4

Since Mar 12Pushed 1y ago3 watchersCompare

[ Source](https://github.com/andreagroferreira/laravel-sync-tracker)[ Packagist](https://packagist.org/packages/andreagroferreira/laravel-sync-tracker)[ RSS](/packages/andreagroferreira-laravel-sync-tracker/feed)WikiDiscussions v1.0.0 Synced 1mo ago

READMEChangelog (1)Dependencies (6)Versions (3)Used By (0)

Laravel Sync Tracker
====================

[](#laravel-sync-tracker)

 [![Laravel Sync Tracker Banner](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/banner.png)](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/banner.png)

[![Latest Version on Packagist](https://camo.githubusercontent.com/931cdade40dbb6065468bbf29a1f5e60d7e2d3d3d26bda679427b75f45aa3368/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616e6472656167726f66657272656972612f72656469732d73747265616d2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andreagroferreira/laravel-sync-tracker)[![Total Downloads](https://camo.githubusercontent.com/37c4c7c7275ca9b361ad039abba793f59bf8e0ae9567aa6a5a79caeec15c87fd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616e6472656167726f66657272656972612f72656469732d73747265616d2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andreagroferreira/laravel-sync-tracker)[![MIT Licensed](https://camo.githubusercontent.com/55c0218c8f8009f06ad4ddae837ddd05301481fcf0dff8e0ed9dadda8780713e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![PHP Version Support](https://camo.githubusercontent.com/3df15037bdd4b5d09ab4724c04ff4d67adb2c6e9cc1dfedf3e40710d18394780/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f616e6472656167726f66657272656972612f72656469732d73747265616d2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andreagroferreira/laravel-sync-tracker)[![Laravel Version Support](https://camo.githubusercontent.com/a654fdf1fbc7e288fc61ec9595f9dc36a153cb3bc4d4750a36b42665b0b2ac8b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d392e7825323025374325323031302e7825323025374325323031322e782d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andreagroferreira/laravel-sync-tracker)[![GitHub forks](https://camo.githubusercontent.com/7377e59dba05ce908780286f74040ea2b33e6e894b7456a3ead44b153dd54640/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f616e6472656167726f66657272656972612f6c61726176656c2d72656469732d73747265616d2e7376673f7374796c653d736f6369616c266c6162656c3d466f726b266d61784167653d32353932303030)](https://github.com/andreagroferreira/laravel-sync-tracker)[![GitHub stars](https://camo.githubusercontent.com/5bd86fa74a171cbb91e50a38c95059e6e314e05b3602f87f3522073ef4fb2d90/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f616e6472656167726f66657272656972612f6c61726176656c2d72656469732d73747265616d2e7376673f7374796c653d736f6369616c266c6162656c3d53746172266d61784167653d32353932303030)](https://github.com/andreagroferreira/laravel-sync-tracker)

A powerful Laravel package for tracking entity synchronization status between systems. Easily manage data synchronization between your Laravel application and external services like CRMs, ERPs, or any third-party API.

Features
--------

[](#features)

- 🔄 **Track sync status** of any Eloquent model with external systems
- 🔍 **Find models by external ID** from various sources
- 🕒 **Track timestamps** for creation, update, and deletion events
- 🧩 **Support for multiple sync sources** within the same application
- 📊 **Store metadata** about sync operations for audit trails
- 🛠️ **Highly configurable** to suit your specific needs
- 🔌 **Easy integration** with existing Laravel applications

 [![Sync Flow Diagram](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/flow-diagram.png)](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/flow-diagram.png)

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Configuration](#configuration)
- [Basic Usage](#basic-usage)
- [Advanced Usage](#advanced-usage)
- [Real-world Examples](#real-world-examples)
- [Events](#events)
- [Custom Implementations](#custom-implementations)
- [Testing](#testing)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [Security](#security)
- [Credits](#credits)
- [License](#license)

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require andreagroferreira/laravel-sync-tracker
```

Configuration
-------------

[](#configuration)

Publish the configuration and migrations:

```
php artisan vendor:publish --provider="WizardingCode\FlowNetwork\SyncTracker\SyncTrackerServiceProvider" --tag="config"
php artisan vendor:publish --provider="WizardingCode\FlowNetwork\SyncTracker\SyncTrackerServiceProvider" --tag="migrations"
```

Then run the migrations:

```
php artisan migrate
```

The published configuration file (`config/sync-tracker.php`) allows you to customize how sync tracking works:

```
return [
    // The table name used to store sync tracking information
    'table_name' => 'sync_tracked_entities',

    // Default tracking options
    'default_tracking' => [
        // Whether to track creation timestamps by default
        'track_created' => true,

        // Whether to track update timestamps by default
        'track_updated' => true,

        // Whether to track deletion timestamps by default
        'track_deleted' => true,
    ],

    // Custom tracking models configuration
    'models' => [
        // Example of model-specific configuration
        App\Models\User::class => [
            'track_created' => true,
            'track_updated' => false,
            'track_deleted' => true,
        ],
    ],
];
```

Basic Usage
-----------

[](#basic-usage)

### Using the Trait

[](#using-the-trait)

Add the `HasSyncTracking` trait to your model:

```
use WizardingCode\FlowNetwork\SyncTracker\Traits\HasSyncTracking;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasSyncTracking;

    // ...
}
```

Then you can use the methods provided by the trait:

```
$user = User::find(1);

// Mark the model as synced
$user->markAsSynced('external-123', 'salesforce', ['meta' => 'data']);

// Check if model is synced
if ($user->isSynced()) {
    // Do something
}

// Get sync information
$externalId = $user->getExternalId();
$source = $user->getSyncSource();
$metadata = $user->getSyncMetadata();
```

### Using the Facade

[](#using-the-facade)

```
use WizardingCode\FlowNetwork\SyncTracker\Facades\SyncTracker;

// Mark a model as synced
SyncTracker::markAsSynced($model, 'external-123', 'salesforce', ['meta' => 'data']);

// Check if model is synced
if (SyncTracker::isSynced($model)) {
    // Do something
}

// Find a model by external ID and source
$user = SyncTracker::findByExternalId('external-123', 'salesforce', User::class);
```

Advanced Usage
--------------

[](#advanced-usage)

### Sync Multiple Source Systems

[](#sync-multiple-source-systems)

Track entities that exist in multiple external systems:

```
// Track the same user in different systems
$user = User::find(1);

// Mark as synced with Salesforce
$user->markAsSynced('SF-123456', 'salesforce', [
    'last_sync' => now(),
    'account_type' => 'customer'
]);

// In another part of your app, sync with HubSpot
SyncTracker::markAsSynced($user, 'HS-789012', 'hubspot', [
    'contact_owner' => 'jane.doe@example.com',
    'lead_score' => 85
]);

// Get all sync trackers for this user
$syncTrackers = $user->syncTrackers()->get();

// Check if synced with specific system
$salesforceId = $user->getExternalIdFromSource('salesforce');
$hubspotId = $user->getExternalIdFromSource('hubspot');
```

### Batch Synchronization with Progress Tracking

[](#batch-synchronization-with-progress-tracking)

When syncing multiple entities in a batch job:

```
// In a command or job
public function handle()
{
    $users = User::where('needs_sync', true)->get();
    $totalUsers = $users->count();
    $processed = 0;

    foreach ($users as $user) {
        // Sync with external API (pseudo code)
        $externalData = $this->apiClient->syncUser($user);

        // Mark as synced with metadata for tracking
        SyncTracker::markAsSynced($user, $externalData['id'], 'api', [
            'batch_id' => $this->batchId,
            'sync_attempt' => now(),
            'sync_status' => 'success',
            'progress' => ++$processed / $totalUsers
        ]);

        // Update user status
        $user->update(['needs_sync' => false]);
    }
}
```

### Handling Failed Syncs

[](#handling-failed-syncs)

Track failed synchronization attempts:

```
try {
    // Attempt to sync with external system
    $response = $this->apiClient->createOrUpdate($product);

    // If successful, mark as synced
    SyncTracker::markAsSynced($product, $response['id'], 'erp', [
        'sync_status' => 'success',
        'last_successful_sync' => now()
    ]);

} catch (ApiException $e) {
    // If failed, track the failure but don't update synced_at
    $product->syncTracking()->update([
        'metadata->sync_status' => 'failed',
        'metadata->error_message' => $e->getMessage(),
        'metadata->error_code' => $e->getCode(),
        'metadata->retry_count' => DB::raw('COALESCE(metadata->\'retry_count\', 0) + 1'),
        'metadata->last_attempt' => now()->toIso8601String()
    ]);

    // Maybe schedule a retry
    if (($product->getSyncMetadata()['retry_count'] ?? 0) < 5) {
        SyncRetryJob::dispatch($product)->delay(now()->addMinutes(30));
    }
}
```

### Tracking Bi-directional Syncs

[](#tracking-bi-directional-syncs)

Track changes from both your system and external systems:

```
// When a local change is made
$product = Product::find(1);
$product->update(['price' => 29.99]);

// Mark that this entity needs to be synced
$product->syncTracking()->update([
    'metadata->needs_upstream_sync' => true,
    'metadata->local_changes' => ['price' => 29.99],
    'updated_at' => now() // This triggers the trait's auto-tracking
]);

// When receiving webhooks from an external system
public function handleExternalUpdate(Request $request)
{
    $externalId = $request->input('id');
    $source = 'erp';

    $product = SyncTracker::findByExternalId($externalId, $source, Product::class);

    if ($product) {
        // Update local record with data from external system
        $product->update([
            'name' => $request->input('name'),
            'sku' => $request->input('sku')
        ]);

        // Mark as synced from downstream with metadata
        SyncTracker::markAsSynced($product, $externalId, $source, [
            'sync_type' => 'downstream',
            'webhook_id' => $request->input('webhook_id'),
            'external_updated_at' => $request->input('updated_at')
        ]);
    }
}
```

 [![Bidirectional Sync](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/bidirectional-sync.png)](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/bidirectional-sync.png)

### Query Scopes for Sync Status

[](#query-scopes-for-sync-status)

Define query scopes on your models for easy filtering:

```
use WizardingCode\FlowNetwork\SyncTracker\Traits\HasSyncTracking;

class Product extends Model
{
    use HasSyncTracking;

    // Scope for products that need syncing to the ERP
    public function scopeNeedsErpSync($query)
    {
        return $query->whereHas('syncTracking', function ($q) {
            $q->where('source', 'erp')
              ->where(function ($q) {
                  $q->whereNull('synced_at')
                    ->orWhere('updated_at', '>', 'synced_at');
              });
        });
    }

    // Scope for products that have been synced with Shopify
    public function scopeSyncedWithShopify($query)
    {
        return $query->whereHas('syncTracking', function ($q) {
            $q->where('source', 'shopify')
              ->whereNotNull('synced_at');
        });
    }

    // Scope for products that failed to sync
    public function scopeFailedSync($query, $source = null)
    {
        return $query->whereHas('syncTracking', function ($q) use ($source) {
            $q->when($source, function ($q) use ($source) {
                $q->where('source', $source);
              })
              ->whereJsonContains('metadata->sync_status', 'failed');
        });
    }
}

// Then use in your application
$needsSyncProducts = Product::needsErpSync()->get();
$shopifyProducts = Product::syncedWithShopify()->get();
$failedProducts = Product::failedSync('erp')->get();
```

Real-world Examples
-------------------

[](#real-world-examples)

### E-commerce Platform Integration

[](#e-commerce-platform-integration)

```
class ProductSyncService
{
    public function syncToShopify(Product $product)
    {
        // If product exists in Shopify, update it, otherwise create it
        if ($product->getExternalIdFromSource('shopify')) {
            $shopifyId = $product->getExternalIdFromSource('shopify');
            $response = $this->shopifyClient->updateProduct($shopifyId, [
                'title' => $product->name,
                'price' => $product->price,
                'inventory_quantity' => $product->stock
            ]);
        } else {
            $response = $this->shopifyClient->createProduct([
                'title' => $product->name,
                'price' => $product->price,
                'inventory_quantity' => $product->stock
            ]);

            $shopifyId = $response['id'];
        }

        // Track the sync with detailed metadata
        $product->markAsSynced($shopifyId, 'shopify', [
            'shopify_handle' => $response['handle'],
            'variants_synced' => count($response['variants']),
            'images_synced' => count($response['images']),
            'shopify_updated_at' => $response['updated_at'],
            'inventory_tracked' => true
        ]);

        return $response;
    }

    public function syncFromShopify(array $shopifyData)
    {
        $shopifyId = $shopifyData['id'];

        // Try to find existing product
        $product = SyncTracker::findByExternalId($shopifyId, 'shopify', Product::class);

        if (!$product) {
            // Create new local product from Shopify data
            $product = Product::create([
                'name' => $shopifyData['title'],
                'price' => $shopifyData['price'],
                'stock' => $shopifyData['inventory_quantity'],
                'description' => $shopifyData['body_html']
            ]);
        } else {
            // Update existing product
            $product->update([
                'name' => $shopifyData['title'],
                'price' => $shopifyData['price'],
                'stock' => $shopifyData['inventory_quantity'],
                'description' => $shopifyData['body_html']
            ]);
        }

        // Track the sync
        SyncTracker::markAsSynced($product, $shopifyId, 'shopify', [
            'shopify_handle' => $shopifyData['handle'],
            'shopify_updated_at' => $shopifyData['updated_at'],
            'sync_direction' => 'from_shopify',
            'webhook_id' => request()->header('X-Shopify-Webhook-Id')
        ]);

        return $product;
    }
}
```

### CRM Integration with Conflict Resolution

[](#crm-integration-with-conflict-resolution)

```
class ContactSyncService
{
    public function syncWithCrm(User $user)
    {
        $userData = [
            'name' => $user->name,
            'email' => $user->email,
            'phone' => $user->phone,
            'address' => $user->address
        ];

        $syncInfo = $user->syncTracking;
        $externalId = $user->getExternalId();

        // Check if this is an update or a new record
        if ($externalId) {
            // Get the latest data from CRM first
            $crmData = $this->crmClient->getContact($externalId);

            // Compare timestamps to detect conflicts
            $crmUpdatedAt = Carbon::parse($crmData['updated_at']);
            $localUpdatedAt = $syncInfo->updated_at;

            if ($crmUpdatedAt->gt($localUpdatedAt)) {
                // Remote has newer data, handle conflict
                if (config('sync.conflict_strategy') === 'remote_wins') {
                    // Update local with remote data
                    $user->update([
                        'name' => $crmData['name'],
                        'email' => $crmData['email'],
                        'phone' => $crmData['phone'],
                        'address' => $crmData['address']
                    ]);

                    $result = $crmData;
                    $conflictResolution = 'remote_won';
                } else {
                    // Push local changes to CRM anyway
                    $result = $this->crmClient->updateContact($externalId, $userData);
                    $conflictResolution = 'local_force_push';
                }
            } else {
                // Local has newer data, update CRM
                $result = $this->crmClient->updateContact($externalId, $userData);
                $conflictResolution = 'local_newer';
            }
        } else {
            // Create new CRM contact
            $result = $this->crmClient->createContact($userData);
            $externalId = $result['id'];
            $conflictResolution = 'new_record';
        }

        // Track the sync with detailed metadata
        $user->markAsSynced($externalId, 'crm', [
            'sync_result' => 'success',
            'conflict_detected' => $conflictResolution !== 'new_record',
            'conflict_resolution' => $conflictResolution,
            'fields_synced' => array_keys($userData),
            'crm_updated_at' => $result['updated_at']
        ]);

        return $result;
    }
}
```

 [![Conflict Resolution Workflow](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/conflict-resolution.png)](https://raw.githubusercontent.com/andreagroferreira/laravel-sync-tracker/main/art/conflict-resolution.png)

### Syncing Data with Legacy Systems through ETL Processes

[](#syncing-data-with-legacy-systems-through-etl-processes)

```
class LegacySystemSyncJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $batchId;
    protected $models;
    protected $source = 'legacy_erp';

    public function __construct(array $modelIds, string $batchId)
    {
        $this->models = $modelIds;
        $this->batchId = $batchId;
    }

    public function handle()
    {
        // Connect to legacy system via ODBC or similar
        $connection = $this->getLegacyConnection();

        foreach ($this->models as $modelType => $ids) {
            $modelClass = $this->getModelClass($modelType);

            foreach ($ids as $id) {
                $model = $modelClass::find($id);

                if (!$model) {
                    continue;
                }

                try {
                    // Prepare data for legacy system format
                    $legacyData = $this->transformToLegacyFormat($model);

                    // Check if record exists in legacy system
                    $externalId = $model->getExternalIdFromSource($this->source);

                    if ($externalId) {
                        // Update existing record
                        $result = $connection->update(
                            $this->getLegacyTableName($modelType),
                            $legacyData,
                            "ID = '$externalId'"
                        );
                    } else {
                        // Insert new record
                        $externalId = $this->generateLegacyId($model);
                        $legacyData['ID'] = $externalId;

                        $result = $connection->insert(
                            $this->getLegacyTableName($modelType),
                            $legacyData
                        );
                    }

                    // Track successful sync
                    SyncTracker::markAsSynced($model, $externalId, $this->source, [
                        'batch_id' => $this->batchId,
                        'sync_timestamp' => now()->timestamp,
                        'tables_affected' => [$this->getLegacyTableName($modelType)],
                        'sync_mode' => $externalId ? 'update' : 'insert',
                        'legacy_fields' => array_keys($legacyData)
                    ]);

                } catch (\Exception $e) {
                    // Track failed sync but don't update synced_at
                    if ($model->syncTracking) {
                        $model->syncTracking->update([
                            'metadata->sync_status' => 'failed',
                            'metadata->error' => $e->getMessage(),
                            'metadata->batch_id' => $this->batchId,
                            'metadata->attempt_timestamp' => now()->timestamp
                        ]);
                    }

                    // Log error for admin review
                    Log::error("Legacy sync failed for {$modelType} #{$id}: " . $e->getMessage());
                }
            }
        }
    }
}
```

Events
------

[](#events)

This package dispatches Laravel events that you can listen for in your application:

```
// In your EventServiceProvider
protected $listen = [
    'WizardingCode\FlowNetwork\SyncTracker\Events\EntitySynced' => [
        'App\Listeners\HandleEntitySynced',
    ],
    'WizardingCode\FlowNetwork\SyncTracker\Events\SyncFailed' => [
        'App\Listeners\HandleSyncFailed',
    ],
];
```

Then create listeners to handle these events:

```
namespace App\Listeners;

use WizardingCode\FlowNetwork\SyncTracker\Events\EntitySynced;

class HandleEntitySynced
{
    public function handle(EntitySynced $event)
    {
        $model = $event->model;
        $syncInfo = $event->syncInfo;

        // Notify admins of successful sync
        if ($model instanceof \App\Models\CriticalEntity) {
            \Notification::route('slack', config('services.slack.webhook_url'))
                ->notify(new \App\Notifications\EntitySynced($model, $syncInfo));
        }

        // Invalidate any cache related to this model
        \Cache::tags([$model->getTable()])->flush();
    }
}
```

Custom Implementations
----------------------

[](#custom-implementations)

### Creating a Custom Synchronization Manager

[](#creating-a-custom-synchronization-manager)

```
namespace App\Services;

use WizardingCode\FlowNetwork\SyncTracker\Facades\SyncTracker;
use Illuminate\Database\Eloquent\Model;

class SalesforceSync
{
    protected $client;

    public function __construct(SalesforceClient $client)
    {
        $this->client = $client;
    }

    public function syncAccount(Model $company)
    {
        // Check if already synced
        $sfAccountId = $company->getExternalIdFromSource('salesforce');

        $companyData = [
            'Name' => $company->name,
            'BillingStreet' => $company->address,
            'BillingCity' => $company->city,
            'BillingState' => $company->state,
            'BillingPostalCode' => $company->zip,
            'BillingCountry' => $company->country,
            'Phone' => $company->phone,
            'Website' => $company->website
        ];

        try {
            if ($sfAccountId) {
                // Update existing
                $result = $this->client->update('Account', $sfAccountId, $companyData);
            } else {
                // Create new
                $result = $this->client->create('Account', $companyData);
                $sfAccountId = $result['id'];
            }

            // Now sync all contacts for this company
            $this->syncContacts($company, $sfAccountId);

            // Track successful sync with metadata
            SyncTracker::markAsSynced($company, $sfAccountId, 'salesforce', [
                'object_type' => 'Account',
                'sf_last_modified' => $result['LastModifiedDate'] ?? now(),
                'child_objects_synced' => [
                    'contacts' => $company->users()->count()
                ]
            ]);

            return $result;

        } catch (\Exception $e) {
            // Handle failure - track but don't update synced_at
            if ($company->syncTracking) {
                $company->syncTracking->update([
                    'metadata->sync_status' => 'failed',
                    'metadata->error' => $e->getMessage(),
                    'metadata->last_attempt' => now()
                ]);
            }

            throw $e;
        }
    }

    protected function syncContacts(Model $company, string $sfAccountId)
    {
        // Implementation for syncing associated contacts...
    }
}
```

### Implement a REST API for External Systems to Check Sync Status

[](#implement-a-rest-api-for-external-systems-to-check-sync-status)

```
// In a controller
public function getSyncStatus(Request $request)
{
    $request->validate([
        'model_type' => 'required|string',
        'model_id' => 'required',
        'source' => 'required|string'
    ]);

    $modelClass = $this->getModelClassFromType($request->model_type);
    $model = $modelClass::find($request->model_id);

    if (!$model) {
        return response()->json([
            'error' => 'Model not found'
        ], 404);
    }

    $syncInfo = $model->syncTracking()->where('source', $request->source)->first();

    if (!$syncInfo) {
        return response()->json([
            'sync_status' => 'not_synced',
            'model_type' => $request->model_type,
            'model_id' => $request->model_id,
            'source' => $request->source
        ]);
    }

    return response()->json([
        'sync_status' => $syncInfo->synced_at ? 'synced' : 'pending',
        'external_id' => $syncInfo->external_id,
        'last_synced_at' => $syncInfo->synced_at,
        'last_updated_at' => $syncInfo->updated_at,
        'metadata' => $syncInfo->metadata
    ]);
}
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Andre Agro Ferreira](https://github.com/andreagroferreira)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance45

Moderate activity, may be stable

Popularity29

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

432d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/56b076ca26a29dde8c2946bea9a1eeea0ea87e5019037a67766a39642aa71862?d=identicon)[andreagroferreira.af](/maintainers/andreagroferreira.af)

---

Top Contributors

[![andreagroferreira](https://avatars.githubusercontent.com/u/35269365?v=4)](https://github.com/andreagroferreira "andreagroferreira (4 commits)")

---

Tags

laraveldatabaseormeloquentsynctracker

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/andreagroferreira-laravel-sync-tracker/health.svg)

```
[![Health](https://phpackages.com/badges/andreagroferreira-laravel-sync-tracker/health.svg)](https://phpackages.com/packages/andreagroferreira-laravel-sync-tracker)
```

###  Alternatives

[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k7.2M71](/packages/mongodb-laravel-mongodb)[spiritix/lada-cache

A Redis based, automated and scalable database caching layer for Laravel

591444.8k2](/packages/spiritix-lada-cache)[pdphilip/elasticsearch

An Elasticsearch implementation of Laravel's Eloquent ORM

145360.2k4](/packages/pdphilip-elasticsearch)[toponepercent/baum

Baum is an implementation of the Nested Set pattern for Eloquent models.

3154.7k](/packages/toponepercent-baum)[czim/laravel-filter

Filter for Laravel Eloquent queries, with support for modular filter building

8973.0k3](/packages/czim-laravel-filter)[eusonlito/laravel-database-cache

Cache Database Query results on Laravel Query Builder or Eloquent

194.2k](/packages/eusonlito-laravel-database-cache)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
