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 getHashCompositeDependencies(): array
{
return []; // No related models to track
}
public function getHashRelationsToNotifyOnChange(): array
{
return []; // No dependent models to notify
}
}
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 els should be notified when this changes
*/
public function getHashRelationsToNotifyOnChange(): array
{
return []; // Orders typically don't notify other models
}
// 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 getHashCompositeDependencies(): array
{
return []; // Child models typically don't track relations
}
/**
* Define which parent models should be notified of changes
*/
public function getHashRelationsToNotifyOnChange(): array
{
return ['order']; // Notify parent order when this item changes
}
public function order()
{
return $this->belongsTo(Order::class);
}
}
// Example: Blog system with User -> Posts -> Comments
class User extends Model implements Hashable
{
use InteractsWithHashes;
public function getHashCompositeDependencies(): array
{
return ['posts']; // User's hash lic function getHashCompositeDependencies(): array
{
return ['comments', 'user']; // Post's hash des the commenter
}
public function getHashRelationsToNotifyOnChange(): array
{
return ['post', 'post.user']; // Notify both post and post's author
}
}
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\Contracts\DeletePublisher;
class OrderApiDeletePublisher implements DeletePublisher
{
public function publishDeletion(string $modelClass, int $modelId, array $lastKnownData): bool
{
$response = Http::delete("https://api.example.com/orders/{$modelId}");
return $response->successful();
}
public function shouldPublishDeletion(string $modelClass, int $modelId): bool
{
// Optional: Add logic to skip certain deletions
return true;
}
public function getMaxAttempts(): int
{
return 3;
}
}
use ameax\HashChangeDetector\Contracts\Publisher;
use ameax\HashChangeDetector\Contracts\DeletePublisher;
class OrderApiFullPublisher implements Publisher, DeletePublisher
{
// Create/Update methods
public function publish(Model $model, array $data): bool
{
$method = $model->wasRecentlyCreated ? 'post' : 'put';
$response = Http::$method('https://api.example.com/orders', [
'order_id' => $model->id,
'data' => $data,
]);
return $response->successful();
}
public function getData(Model $model): array
{
return $model->toArray();
}
// Delete methods
public function publishDeletion(string $modelClass, int $modelId, array $lastKnownData): bool
{
$response = Http::delete("https://api.example.com/orders/{$modelId}");
return $response->successful();
}
public function shouldPublishDeletion(string $modelClass, int $modelId): bool
{
return true;
}
// Shared methods
public function shouldPublish(Model $model): bool
{
return $model->status !== 'draft';
}
public function getMaxAttempts(): int
{
return 3;
}
}
use ameax\HashChangeDetector\Facades\HashChangeDetector;
HashChangeDetector::registerPublisher(
'order-api',
Order::class,
OrderApiPublisher::class
);
public function getHashCompositeDependencies(): array
{
return [
'orderItems', // Direct relation
'orderItems.product', // Nested relation
];
}
// Notify nested parent models
public function getHashRelationsToNotifyOnChange(): array
{
return [
'user', // Direct parent
'user.country', // Nested parent
];
}
// In a Comment model that belongs to both Post and User
public function getHashRelationsToNotifyOnChange(): array
{
return [
'post', // BelongsTo - single model
'user', // BelongsTo - single model
];
}
// In a User model with many posts
public function getHashRelationsToNotifyOnChange(): array
{
return [
'posts', // HasMany - collection of models
];
}
// This scenario is automatically handled without infinite loops
class User extends Model implements Hashable
{
use InteractsWithHashes;
public function getHashRelationsToNotifyOnChange(): array
{
return ['posts']; // When user changes, notify all posts
}
}
class Post extends Model implements Hashable
{
use InteractsWithHashes;
public function getHashRelationsToNotifyOnChange(): array
{
return ['user']; // When post changes, notify user
}
}
'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 getHashCompositeDependencies(): array
{
return ['items'];
}
public function getHashRelationsToNotifyOnChange(): array
{
return []; // Orders don't notify other models
}
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 getHashCompositeDependencies(): array
{
return [];
}
public function getHashRelationsToNotifyOnChange(): array
{
return ['order']; // Notify parent 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.