PHP code example of ameax / laravel-hash-change-detector

1. Go to this page and download the library: Download ameax/laravel-hash-change-detector library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

ameax / laravel-hash-change-detector example snippets


use ameax\HashChangeDetector\Contracts\Hashable;
use ameax\HashChangeDetector\Traits\InteractsWithHashes;

class Product extends Model implements Hashable
{
    use InteractsWithHashes;
    
    public function getHashableAttributes(): array
    {
        return ['name', 'price', 'sku'];
    }
    
    public function getHashableRelations(): array
    {
        return []; // No related models to track
    }
}

use ameax\HashChangeDetector\Contracts\Hashable;
use ameax\HashChangeDetector\Traits\InteractsWithHashes;

class Order extends Model implements Hashable
{
    use InteractsWithHashes;
    
    /**
     * Define which attributes to ine which relationships to track
     */
    public function getHashableRelations(): array
    {
        return [
            'orderItems',     // HasMany relationship
            'shipping',       // HasOne relationship
        ];
    }
    
    // Your regular model relationships
    public function orderItems()
    {
        return $this->hasMany(OrderItem::class);
    }
    
    public function shipping()
    {
        return $this->hasOne(ShippingDetail::class);
    }
}

class OrderItem extends Model implements Hashable
{
    use InteractsWithHashes;
    
    public function getHashableAttributes(): array
    {
        return ['product_name', 'quantity', 'price'];
    }
    
    public function getHashableRelations(): array
    {
        return []; // Child models typically don't track relations
    }
    
    /**
     * Define which parent models should be notified of changes
     */
    public function getParentModels(): Collection
    {
        return collect([$this->order]);
    }
    
    public function order()
    {
        return $this->belongsTo(Order::class);
    }
}

class Product extends Model implements Hashable
{
    use InteractsWithHashes;
    
    protected $fillable = ['name', 'price', 'sku', 'stock'];
    
    public function getHashableAttributes(): array
    {
        return ['name', 'price', 'sku', 'stock'];
    }
}

class SalesReport extends Model implements Hashable
{
    use TracksHashesOnly; // Note: Different trait!
    
    protected $table = 'sales_summary_view'; // Often a database view
    
    public function getHashableAttributes(): array
    {
        return ['report_date', 'total_sales', 'order_count'];
    }
    
    // Prevent accidental modifications
    public function save(array $options = [])
    {
        throw new \RuntimeException('This is a read-only model');
    }
}

protected function schedule(Schedule $schedule)
{
    // Detect all changes every 5 minutes
    $schedule->command('hash-detector:detect-changes')
        ->everyFiveMinutes();
        
    // Or detect changes for specific models
    $schedule->command('hash-detector:detect-changes', ['model' => Order::class])
        ->everyFiveMinutes();
}

use ameax\HashChangeDetector\Contracts\Publisher;

class OrderApiPublisher implements Publisher
{
    public function publish(Model $model, array $data): bool
    {
        $response = Http::post('https://api.example.com/orders', [
            'order_id' => $model->id,
            'data' => $data,
        ]);
        
        return $response->successful();
    }
    
    public function getData(Model $model): array
    {
        return [
            'order' => $model->toArray(),
            'items' => $model->orderItems->toArray(),
            'shipping' => $model->shipping?->toArray(),
        ];
    }
}

use ameax\HashChangeDetector\Facades\HashChangeDetector;

HashChangeDetector::registerPublisher(
    'order-api',
    Order::class,
    OrderApiPublisher::class
);

$schedule->command('hash-detector:retry-publishes')
    ->everyFiveMinutes();

$schedule->command('hash-detector:detect-changes', ['model' => SalesReport::class])
    ->hourly(); // Can use different frequency than regular models

// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    // Regular models - frequent checks
    $schedule->command('hash-detector:detect-changes', ['model' => Product::class])
        ->everyFiveMinutes();
        
    $schedule->command('hash-detector:detect-changes', ['model' => Order::class])
        ->everyFiveMinutes();
    
    // Read-only models - less frequent checks
    $schedule->command('hash-detector:detect-changes', ['model' => WarehouseInventory::class])
        ->everyThirtyMinutes();
        
    $schedule->command('hash-detector:detect-changes', ['model' => SalesAnalytics::class])
        ->hourly();
}

public function getHashableRelations(): array
{
    return [
        'orderItems',           // Direct relation
        'orderItems.product',   // Nested relation
    ];
}

public function getParentModels(): Collection
{
    return collect([
        $this->order,
        $this->warehouse,
        $this->invoice
    ])->filter(); // Filter removes nulls
}

'hash_algorithm' => 'sha256', // Default is 'md5'

use ameax\HashChangeDetector\Traits\SyncsFromExternalSources;

class Product extends Model implements Hashable
{
    use InteractsWithHashes, SyncsFromExternalSources;
    
    // ... your model configuration
}

// Sync single model from API without triggering publishers
$product->syncFromExternal([
    'name' => 'Updated from API',
    'price' => 99.99,
    'stock' => 50
], 'external-api'); // Mark 'external-api' publisher as synced

// Create or update from API
$product = Product::syncOrCreateFromExternal(
    ['sku' => 'WIDGET-001'], // Find by SKU
    [
        'name' => 'Widget',
        'price' => 49.99,
        'stock' => 100
    ],
    'external-api' // Optional: specific publisher to mark as synced
);

// Bulk sync from API
$products = Product::bulkSyncFromExternal($apiData, 'sku', 'external-api');

// Manual approach without trait
$product->fill($apiData);
$product->saveQuietly(); // Laravel's quiet save
$product->updateHashWithoutPublishing(['external-api', 'another-api']);

use ameax\HashChangeDetector\Events\HashableModelDeleted;

class HandleDeletedModel
{
    public function handle(HashableModelDeleted $event)
    {
        Log::info("Model deleted: {$event->modelClass} ID: {$event->modelId}");
        
        // Notify external systems
        // Clean up related data
        // etc.
    }
}

// app/Models/Order.php
class Order extends Model implements Hashable
{
    use InteractsWithHashes;
    
    protected $fillable = ['order_number', 'customer_email', 'total_amount', 'status'];
    
    public function getHashableAttributes(): array
    {
        return ['order_number', 'customer_email', 'total_amount', 'status'];
    }
    
    public function getHashableRelations(): array
    {
        return ['items'];
    }
    
    public function items()
    {
        return $this->hasMany(OrderItem::class);
    }
}

// app/Models/OrderItem.php
class OrderItem extends Model implements Hashable
{
    use InteractsWithHashes;
    
    protected $fillable = ['order_id', 'product_name', 'quantity', 'price'];
    
    public function getHashableAttributes(): array
    {
        return ['product_name', 'quantity', 'price'];
    }
    
    public function getHashableRelations(): array
    {
        return [];
    }
    
    public function getParentModels(): Collection
    {
        return collect([$this->order]);
    }
    
    public function order()
    {
        return $this->belongsTo(Order::class);
    }
}

// Usage
$order = Order::create([
    'order_number' => 'ORD-001',
    'customer_email' => '[email protected]',
    'total_amount' => 100.00,
    'status' => 'pending'
]);

$item = $order->items()->create([
    'product_name' => 'Widget',
    'quantity' => 2,
    'price' => 50.00
]);

// When item changes, order's composite hash updates automatically
$item->update(['quantity' => 3]);

// Direct database changes are detected by the scheduled job
DB::table('order_items')->where('id', $item->id)->update(['price' => 60.00]);

// routes/api.php
Route::prefix('api/hash-change-detector')
    ->middleware(['api', 'auth:api']) // Add your preferred middleware
    ->group(base_path('vendor/ameax/laravel-hash-change-detector/routes/api.php'));

// Force publish a model to all active publishers
$response = Http::post('/api/hash-change-detector/models/Product/123/publish');

// Force publish to specific publishers only
$response = Http::post('/api/hash-change-detector/models/Product/123/publish', [
    'publisher_names' => ['webhook', 'external-api']
]);

// Check if model needs publishing
$hash = Http::get('/api/hash-change-detector/models/Product/123/hash');
$history = Http::get('/api/hash-change-detector/models/Product/123/publishes?status=published&limit=1');

if ($hash->json('composite_hash') !== $history->json('data.0.published_hash')) {
    // Model has unpublished changes
}

// Bulk retry failed publishes
$response = Http::post('/api/hash-change-detector/retry-publishes');

// Monitor system health
$stats = Http::get('/api/hash-change-detector/stats');
if ($stats->json('publishes_by_status.failed') > 100) {
    // Alert: Too many failed publishes
}
bash
php artisan vendor:publish --tag="hash-change-detector-migrations"
php artisan migrate
bash
php artisan vendor:publish --tag="hash-change-detector-config"
bash
composer 
bash
php artisan hash-detector:publisher:create "Order API" Order OrderApiPublisher
bash
# Initialize hashes for existing records
php artisan hash-detector:initialize-hashes "App\Models\SalesReport"

# Process in chunks for large tables
php artisan hash-detector:initialize-hashes "App\Models\SalesReport" --chunk=1000