سیستم ComputedValues - راهنمای استفاده
این راهنما به شما نشان میدهد چگونه از سیستم ComputedValues برای محاسبه و کش خودکار دادههای مشتق شده در مدلهای لاراول خود استفاده کنید.
پیشنیازها
- مدل لاراول با جدول پایگاه داده
- درک اساسی از رویدادهای لاراول
- آشنایی با اینترفیسها و ویژگیهای PHP
پیادهسازی گام به گام
گام ۱: اضافه کردن ستون پایگاه داده
ستون JSON computed_values را به جدول خود اضافه کنید:
Schema::table('posts', function (Blueprint $table) {
$table->json('computed_values')->nullable();
});
گام ۲: پیادهسازی اینترفیس و ویژگی
مدل خود را بهروزرسانی کنید:
use App\Services\ComputedValues\Contracts\HasComputedValues;
use App\Services\ComputedValues\Traits\HasComputedValuesField;
class Post extends Model implements HasComputedValues
{
use HasComputedValuesField;
// ... بقیه مدل شما
}
گام ۳: تعریف پیکربندی مقدار محاسبه شده
متد getComputedValueConfig() را به مدل خود اضافه کنید:
use App\Services\ComputedValues\Collections\ComputedValueConfigCollection;
use App\Services\ComputedValues\DTOs\ComputedValueConfig;
use App\Services\ComputedValues\DTOs\EloquentEventConfig;
use App\Services\ComputedValues\Enums\ComputedValueCastEnum;
use App\Services\ComputedValues\Enums\ComputedValueModeEnum;
use App\Services\ComputedValues\Enums\EloquentEventEnum;
public function getComputedValueConfig(): ComputedValueConfigCollection
{
return new ComputedValueConfigCollection([
new ComputedValueConfig(
key: 'media_count',
relatedEvents: [
new EloquentEventConfig(
modelClass: MediaLibrary::class,
events: [EloquentEventEnum::CREATED, EloquentEventEnum::DELETED]
)
],
mode: ComputedValueModeEnum::SYNC,
cast: ComputedValueCastEnum::Integer
),
]);
}
گام ۴: ایجاد کنترلکننده
یک کلاس کنترلکننده در دایرکتوری Services/ComputedValueHandlers/ ماژول خود ایجاد کنید:
<?php
declare(strict_types=1);
namespace App\Modules\CMS\Services\ComputedValueHandlers;
use App\Core\Media\Entities\MediaLibrary;
use App\Services\ComputedValues\Collections\ComputedValueResultCollection;
use App\Services\ComputedValues\Contracts\ComputedValueHandler;
use App\Services\ComputedValues\DTOs\ComputedValueEvent;
use App\Services\ComputedValues\DTOs\ComputedValueResult;
final class MediaCountComputedValueHandler implements ComputedValueHandler
{
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
// استخراج شناسه پست از payload رویداد
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');
if ($postId === null) {
return new ComputedValueResultCollection([]);
}
// شمارش رسانه برای این پست
$mediaCount = MediaLibrary::where('post_id', $postId)->count();
// بازگرداندن نتیجه
return new ComputedValueResultCollection([
new ComputedValueResult(
entityId: $postId,
value: $mediaCount
),
]);
}
}
گام ۵: ثبت کشف
دستور کشف را برای ثبت پیکربندی خود اجرا کنید:
php artisan computed-values:discover
دستور کشف به طور خودکار هر کش موجود را قبل از کشف پاک میکند تا نتایج تازه تضمین شود.
گام ۶: دسترسی به مقادیر محاسبه شده
به مقادیر محاسبه شده در کد خود دسترسی داشته باشید:
$post = Post::find(1);
// دسترسی به مقدار محاسبه شده (دسترسی خودکار)
$mediaCount = $post->media_count;
// بررسی وجود مقدار
if ($post->hasComputedValue('media_count')) {
echo "تعداد رسانه: " . $post->media_count;
}
// دریافت برچسب زمانی بهروزرسانی
$updatedAt = $post->getComputedValueUpdatedAt('media_count');
echo "آخرین بهروزرسانی: " . $updatedAt;
گزینههای پیکربندی
انواع رویداد
- رویدادهای کسبوکار
- رویدادهای Eloquent
- رویدادهای مدل خاص
از رویدادهای سفارشی برنامه استفاده کنید:
relatedEvents: [
MediaUploadedEvent::class,
MediaDeletedEvent::class,
]
از رویدادهای چرخه حیات مدل استفاده کنید:
relatedEvents: [
EloquentEventEnum::CREATED,
EloquentEventEnum::UPDATED,
EloquentEventEnum::DELETED,
]
مدلهای خاص را هدف قرار دهید:
relatedEvents: [
new EloquentEventConfig(
modelClass: MediaLibrary::class,
events: [EloquentEventEnum::CREATED, EloquentEventEnum::DELETED]
),
]
حالتهای پردازش
- همزمان (SYNC)
- ناهمزمان (ASYNC)
فوری در همان درخواست پردازش میشود. برای محاسبات سریع استفاده کنید:
mode: ComputedValueModeEnum::SYNC
بهترین برای:
- شمارشهای ساده
- محاسبات سریع (<100ms)
- عملیات بدون وابستگیهای خارجی
در صف پسزمینه پردازش میشود. برای محاسبات سنگین استفاده کنید:
mode: ComputedValueModeEnum::ASYNC
بهترین برای:
- تجمیعهای پیچیده
- فراخوانیهای API خارجی
- کوئریهای سنگین پایگاه داده
- عملیاتی که ممکن است >100ms طول بکشد
انواع داده (تبدیل)
نوع داده را برای مقدار محاسبه شده خود مشخص کنید:
- عدد صحیح
- بولین
- آرایه
- رشته
- اعشاری
- DateTime
- مجموعه
- JSON
cast: ComputedValueCastEnum::Integer
برای اعداد کامل مانند شمارشها، شناسهها، کمیتها.
cast: ComputedValueCastEnum::Boolean
برای پرچمهای true/false، بررسی وضعیت.
cast: ComputedValueCastEnum::Array
برای ساختارهای داده پیچیده (پیشفرض).
cast: ComputedValueCastEnum::String
برای مقادیر متنی، برچسبها، توضیحات.
cast: ComputedValueCastEnum::Float
برای اعداد اعشاری، درصدها، میانگینها.
cast: ComputedValueCastEnum::DateTime
برای اشیاء datetime کربن.
cast: ComputedValueCastEnum::Collection
برای نمونههای مجموعه Laravel.
cast: ComputedValueCastEnum::Json
برای نمایش رشته JSON.
موارد استفاده رایج
مورد استفاده ۱: شمارش مدلهای مرتبط
تعداد مدلهای مرتبط موجود را بشمارید:
new ComputedValueConfig(
key: 'comment_count',
relatedEvents: [
new EloquentEventConfig(
modelClass: Comment::class,
events: [EloquentEventEnum::CREATED, EloquentEventEnum::DELETED]
)
],
mode: ComputedValueModeEnum::SYNC,
cast: ComputedValueCastEnum::Integer
)
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');
$count = Comment::where('post_id', $postId)->count();
return new ComputedValueResultCollection([
new ComputedValueResult($postId, $count),
]);
}
مورد استفاده ۲: محاسبه آمار
آمار تجمیعی را محاسبه کنید:
new ComputedValueConfig(
key: 'view_statistics',
relatedEvents: [PostViewedEvent::class],
mode: ComputedValueModeEnum::ASYNC,
cast: ComputedValueCastEnum::Array
)
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');
$stats = [
'total_views' => View::where('post_id', $postId)->count(),
'unique_views' => View::where('post_id', $postId)->distinct('user_id')->count(),
'today_views' => View::where('post_id', $postId)
->whereDate('created_at', today())
->count(),
];
return new ComputedValueResultCollection([
new ComputedValueResult($postId, $stats),
]);
}
مورد استفاده ۳: بررسی وضعیت/پرچمها
پرچمهای بولین را محاسبه کنید:
new ComputedValueConfig(
key: 'has_active_comments',
relatedEvents: [
new EloquentEventConfig(
modelClass: Comment::class,
events: [
EloquentEventEnum::CREATED,
EloquentEventEnum::UPDATED,
EloquentEventEnum::DELETED
]
)
],
mode: ComputedValueModeEnum::SYNC,
cast: ComputedValueCastEnum::Boolean
)
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');
$hasActive = Comment::where('post_id', $postId)
->where('status', 'active')
->exists();
return new ComputedValueResultCollection([
new ComputedValueResult($postId, $hasActive),
]);
}
مورد استفاده ۴: برچسب زمانی آخرین فعالیت
پیگیری زمان وقوع آخرین رویداد:
new ComputedValueConfig(
key: 'last_activity_at',
relatedEvents: [
CommentAddedEvent::class,
PostLikedEvent::class,
PostSharedEvent::class,
],
mode: ComputedValueModeEnum::SYNC,
cast: ComputedValueCastEnum::DateTime
)
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');
$lastActivity = Activity::where('post_id', $postId)
->latest()
->first()
?->created_at;
return new ComputedValueResultCollection([
new ComputedValueResult($postId, $lastActivity ?? now()),
]);
}
مورد استفاده ۵: تجمیعهای پیچیده
دادههای مشتق شده پیچیده را محاسبه کنید:
new ComputedValueConfig(
key: 'engagement_metrics',
relatedEvents: [
PostViewedEvent::class,
PostLikedEvent::class,
CommentAddedEvent::class,
],
mode: ComputedValueModeEnum::ASYNC,
cast: ComputedValueCastEnum::Array
)
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');
$metrics = [
'engagement_score' => $this->calculateEngagementScore($postId),
'trending_rank' => $this->calculateTrendingRank($postId),
'virality_index' => $this->calculateViralityIndex($postId),
'calculated_at' => now()->toIso8601String(),
];
return new ComputedValueResultCollection([
new ComputedValueResult($postId, $metrics),
]);
}
دسترسی به مقادیر محاسبه شده
دسترسی پایه
$post = Post::find(1);
// دسترسی مستقیم (دسترسی خودکار)
$mediaCount = $post->media_count;
// بررسی وجود
if ($post->hasComputedValue('media_count')) {
echo $post->media_count;
}
دریافت متادیتا
// دریافت برچسب زمانی آخرین بهروزرسانی
$updatedAt = $post->getComputedValueUpdatedAt('media_count');
// برمیگرداند: "2024-01-15T10:30:00+00:00" یا null
// دریافت تمام آمار
$stats = $post->getCacheStatistics();
// برمیگرداند:
// [
// 'total_keys' => 3,
// 'cached_keys' => 2,
// 'empty_keys' => 1,
// 'keys_with_data' => ['media_count', 'view_stats'],
// 'keys_without_data' => ['last_activity'],
// 'last_updated' => '2024-01-15T10:30:00+00:00'
// ]
دریافت مقادیر چندگانه
// دریافت مقادیر خاص
$data = $post->getComputedValueForKeys(['media_count', 'view_stats']);
// برمیگرداند:
// [
// 'media_count' => [
// 'data' => 42,
// 'has_data' => true,
// 'updated_at' => '2024-01-15T10:30:00+00:00'
// ],
// 'view_stats' => [...]
// ]
// دریافت تمام مقادیر محاسبه شده
$allData = $post->getAllCacheData();
بررسی کامل بودن
// بررسی اینکه آیا تمام کلیدهای پیکربندی شده داده دارند
if ($post->hasCompleteCacheData()) {
echo "تمام مقادیر محاسبه شده در دسترس هستند";
}
در پاسخهای API
مقادیر محاسبه شده به طور خودکار در پاسخهای JSON ظاهر میشوند:
return response()->json($post);
// خروجی شامل:
// {
// "id": 1,
// "title": "پست من",
// "media_count": 42,
// "view_stats": {...},
// ...
// }
مقادیر محاسبه شده چندگانه
شما میتوانید چندین مقدار محاسبه شده را برای یک مدل تعریف کنید:
public function getComputedValueConfig(): ComputedValueConfigCollection
{
return new ComputedValueConfigCollection([
// تعداد رسانه
new ComputedValueConfig(
key: 'media_count',
relatedEvents: [
new EloquentEventConfig(
modelClass: MediaLibrary::class,
events: [EloquentEventEnum::CREATED, EloquentEventEnum::DELETED]
)
],
mode: ComputedValueModeEnum::SYNC,
cast: ComputedValueCastEnum::Integer
),
// تعداد کامنت
new ComputedValueConfig(
key: 'comment_count',
relatedEvents: [
new EloquentEventConfig(
modelClass: Comment::class,
events: [EloquentEventEnum::CREATED, EloquentEventEnum::DELETED]
)
],
mode: ComputedValueModeEnum::SYNC,
cast: ComputedValueCastEnum::Integer
),
// آمار بازدید
new ComputedValueConfig(
key: 'view_statistics',
relatedEvents: [PostViewedEvent::class],
mode: ComputedValueModeEnum::ASYNC,
cast: ComputedValueCastEnum::Array
),
]);
}
هر مقدار محاسبه شده به کنترلکننده خود نیاز دارد که از قرارداد نامگذاری پیروی کند.
راهنمای کنترلکننده
قرارداد نامگذاری
نام کلاس کنترلکننده باید با کلید کش مطابقت داشته باشد:
| کلید کش | نام کلاس کنترلکننده |
|---|---|
media_count | MediaCountComputedValueHandler |
view_statistics | ViewStatisticsComputedValueHandler |
last_activity_at | LastActivityAtComputedValueHandler |
مکان کنترلکننده
کنترلکنندهها را در دایرکتوری Services/ComputedValueHandlers/ ماژول خود قرار دهید:
app/Modules/CMS/Services/ComputedValueHandlers/
MediaCountComputedValueHandler.php
CommentCountComputedValueHandler.php
ViewStatisticsComputedValueHandler.php
ساختار کنترلکننده
<?php
declare(strict_types=1);
namespace App\Modules\{Module}\Services\ComputedValueHandlers;
use App\Services\ComputedValues\Collections\ComputedValueResultCollection;
use App\Services\ComputedValues\Contracts\ComputedValueHandler;
use App\Services\ComputedValues\DTOs\ComputedValueEvent;
use App\Services\ComputedValues\DTOs\ComputedValueResult;
final class {CacheKey}ComputedValueHandler implements ComputedValueHandler
{
public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
// 1. استخراج داده از رویداد
$payload = $eventDto->getPayload();
$entityId = $payload->get('entity_id');
// 2. اعتبارسنجی
if ($entityId === null) {
return new ComputedValueResultCollection([]);
}
// 3. محاسبه مقدار
$value = $this->computeValue($entityId);
// 4. بازگرداندن نتیجه
return new ComputedValueResultCollection([
new ComputedValueResult($entityId, $value),
]);
}
private function computeValue(int $entityId): mixed
{
// منطق محاسبه شما
return 0;
}
}
عیبیابی
مقادیر بهروز نمیشوند
بررسی کنید:
- مدل اینترفیس
HasComputedValuesرا پیادهسازی کرده است - مدل از ویژگی
HasComputedValuesFieldاستفاده میکند - ستون
computed_valuesدر پایگاه داده وجود دارد - کلاس کنترلکننده وجود دارد و از قرارداد نامگذاری پیروی میکند
- کشف اجرا شده است:
php artisan computed-values:discover
مشاهده لاگها:
tail -f storage/logs/laravel.log | grep "Computed value"
خطای کنترلکننده یافت نشد
بررسی کنید:
- نام کلاس کنترلکننده با کلید کش مطابقت دارد (StudlyCase)
- کنترلکننده در دایرکتوری صحیح برای ماژول شما قرار دارد
- کنترلکننده اینترفیس
ComputedValueHandlerرا پیادهسازی میکند - کنترلکننده متد
handle()را دارد
کارهای ناهمزمان پردازش نمیشوند
بررسی کنید:
- کارگران صف در حال اجرا هستند:
php artisan queue:work - پیکربندی صف صحیح است
- کارهای ناموفق:
php artisan queue:failed
تلاش مجدد کارهای ناموفق:
php artisan queue:retry all
دسترسی مستقیم به فیلد مسدود شد
این رفتار مورد انتظار است. سیستم از دستکاری مستقیم جلوگیری میکند:
// ❌ این موارد ComputedValueFieldAccessException را پرتاب میکنند
$post->computed_values = ['data' => 'value'];
$post->update(['computed_values' => ['data' => 'value']]);
// ✅ به جای آن از متدهای دسترسی خودکار استفاده کنید
$mediaCount = $post->media_count;
دسترسی مستقیم به فیلد computed_values عمداً مسدود شده است. این از یکپارچگی داده محافظت کرده و از شرایط رقابتی جلوگیری میکند. همیشه از متدهای دسترسی ارائه شده استفاده کنید.
بهترین شیوهها
- از کلیدهای کش توصیفی استفاده کنید:
media_countنهmc - حالت پردازش مناسب را انتخاب کنید: همزمان برای سریع، ناهمزمان برای کند
- نوع تبدیل صحیح را انتخاب کنید: با نوع داده خود مطابقت دهید
- مقادیر null را مدیریت کنید: همیشه در کنترلکنندهها null را بررسی کنید
- از کوئریهای N+1 اجتناب کنید: از بارگذاری دستهای در کنترلکنندهها استفاده کنید
- عملیات مهم را لاگ کنید: از لاگر در کنترلکنندهها استفاده کنید
- کنترلکنندههای خود را تست کنید: تستهای واحد و یکپارچه بنویسید
- پس از تغییرات کش را پاک کنید: پس از بهروزرسانی پیکربندی کشف را اجرا کنید
- عمق صف را پایش کنید: برای انباشته کارهای ناهمزمان مراقب باشید
- مقادیر محاسبه شده خود را مستند کنید: دلیل وجودشان را توضیح دهید
دستورات
# کشف و ثبت مدلها (به طور خودکار ابتدا کش را پاک میکند)
php artisan computed-values:discover
# فقط پاک کردن کش کشف (بدون کشف مجدد)
php artisan computed-values:discover --clear
# نمایش آمار کشف (از نتایج کش شده در صورت وجود استفاده میکند)
php artisan computed-values:discover --stats
# بررسی وضعیت صف
php artisan queue:work
# مشاهده کارهای ناموفق
php artisan queue:failed
# تلاش مجدد کارهای ناموفق
php artisan queue:retry all
پشتیبانی
اگر با مشکلی مواجه شدید:
- این راهنما را بررسی کنید
- لاگها را در
storage/logs/laravel.logمرور کنید - نامگذاری و مکان کنترلکننده را بررسی کنید
- دستور کشف را اجرا کنید
- پایگاه داده را برای ستون
computed_valuesبررسی کنید - با تیم توسعه تماس بگیرید
نویسنده: بهنام مرادی
نسخه: 1.0
آخرین بهروزرسانی: دسامبر 2024