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
);
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]);
// 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 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
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.