| @@ -6,14 +6,15 @@ | ||
| 6 | 6 | use FluentBoards\App\Services\Helper; |
| 7 | 7 | use FluentBoards\App\Services\TaskService; |
| 8 | 8 | use FluentBoards\Framework\Database\Orm\Builder; |
| 9 | 9 | use FluentBoardsPro\App\Models\TaskAttachment; |
| 10 | -use FluentBoardsPro\App\Models\CustomField; | |
| 10 | +use FluentBoards\App\Models\CustomField; | |
| 11 | 11 | use FluentBoardsPro\App\Services\Constant as ProConstant; |
| 12 | 12 | use FluentCrm\App\Models\Subscriber; |
| 13 | 13 | |
| 14 | 14 | class Task extends Model |
| 15 | 15 | { |
| 16 | + | |
| 16 | 17 | protected $table = 'fbs_tasks'; |
| 17 | 18 | |
| 18 | 19 | protected $guarded = ['id']; |
| 19 | 20 | |
| @@ -99,8 +100,18 @@ | ||
| 99 | 100 | self::adjustSubtaskCount($model->parent_id); |
| 100 | 101 | } |
| 101 | 102 | }); |
| 102 | 103 | |
| 104 | + static::updated(function ($model) { | |
| 105 | + if ($model->isDirty('status') && $model->getOriginal('status') === 'open' && $model->status === 'closed') { | |
| 106 | + // Use persisted attributes without stale stage/board relations from the move. | |
| 107 | + $closedTask = $model->fresh(); | |
| 108 | + if ($closedTask && $closedTask->status === 'closed') { | |
| 109 | + do_action('fluent_boards/task_closed', $closedTask); | |
| 110 | + } | |
| 111 | + } | |
| 112 | + }); | |
| 113 | + | |
| 103 | 114 | /* global scope for task type which means only type = task will be fetched from everywhere in */ |
| 104 | 115 | static::addGlobalScope('type', function (Builder $builder) { |
| 105 | 116 | $builder->where('type', '=', 'task') |
| 106 | 117 | ->orWhere('type', '=', 'roadmap'); |
| @@ -356,10 +367,15 @@ | ||
| 356 | 367 | return 0; |
| 357 | 368 | } |
| 358 | 369 | } |
| 359 | 370 | |
| 371 | + /** Use batch-loaded metadata when preparing task-list responses. */ | |
| 360 | 372 | public function getMetaAttribute() |
| 361 | 373 | { |
| 374 | + if ($this->relationLoaded('taskMeta')) { | |
| 375 | + return $this->getRelation('taskMeta')->pluck('value', 'key'); | |
| 376 | + } | |
| 377 | + | |
| 362 | 378 | return $this->taskMeta()->get()->pluck('value', 'key'); |
| 363 | 379 | } |
| 364 | 380 | |
| 365 | 381 | public function stage() |
| @@ -523,8 +539,9 @@ | ||
| 523 | 539 | |
| 524 | 540 | return $default; |
| 525 | 541 | } |
| 526 | 542 | |
| 543 | + /** Persist metadata and invalidate any snapshot loaded for serialization. */ | |
| 527 | 544 | public function updateMeta($key, $value) |
| 528 | 545 | { |
| 529 | 546 | $exist = TaskMeta::where('task_id', $this->id)->where('key', $key) |
| 530 | 547 | ->first(); |
| @@ -539,8 +556,10 @@ | ||
| 539 | 556 | 'value' => $value, |
| 540 | 557 | ]); |
| 541 | 558 | } |
| 542 | 559 | |
| 560 | + $this->unsetRelation('taskMeta'); | |
| 561 | + | |
| 543 | 562 | return $exist; |
| 544 | 563 | } |
| 545 | 564 | |
| 546 | 565 | /** |
| @@ -545,9 +564,9 @@ | ||
| 545 | 564 | |
| 546 | 565 | /** |
| 547 | 566 | * Virtual attribute: pinned state from task meta (for API/frontend). |
| 548 | 567 | * Tasks without is_pinned meta are treated as unpinned (0). |
| 549 | - * Reads from the already-loaded 'meta' attribute to avoid an extra DB query per task. | |
| 568 | + * Uses the batch-loaded metadata relation when preparing task lists. | |
| 550 | 569 | * |
| 551 | 570 | * @return int 1 if pinned, 0 otherwise |
| 552 | 571 | */ |
| 553 | 572 | public function getIsPinnedAttribute() |
| @@ -877,9 +896,9 @@ | ||
| 877 | 896 | 'priority' => [ |
| 878 | 897 | 'field' => __('Priority', 'fluent-boards'), |
| 879 | 898 | 'type' => 'text', |
| 880 | 899 | 'rules' => 'optional', |
| 881 | - 'description' => __('Priority of the task (urgent | high | medium | low). Leave empty for no priority. Example: "medium" ', 'fluent-boards'), | |
| 900 | + 'description' => __('Priority of the task (urgent | high | medium | low). Leave empty for no priority. Example: "medium"', 'fluent-boards'), | |
| 882 | 901 | ], |
| 883 | 902 | 'due_at' => [ |
| 884 | 903 | 'field' => __('Due Date', 'fluent-boards'), |
| 885 | 904 | 'type' => 'date', |
| @@ -961,10 +980,16 @@ | ||
| 961 | 980 | public function repeatTaskMeta() |
| 962 | 981 | { |
| 963 | 982 | return $this->hasOne(Meta::class, 'object_id', 'id')->where('object_type', Constant::REPEAT_TASK_META); |
| 964 | 983 | } |
| 984 | + | |
| 985 | + /** Reuse batch-loaded repeat settings, including a known missing relation. */ | |
| 965 | 986 | public function getRepeatTaskMetaAttribute() |
| 966 | 987 | { |
| 988 | + if ($this->relationLoaded('repeatTaskMeta')) { | |
| 989 | + return $this->getRelation('repeatTaskMeta'); | |
| 990 | + } | |
| 991 | + | |
| 967 | 992 | return $this->repeatTaskMeta()->first(); |
| 968 | 993 | } |
| 969 | 994 | public function taskCustomFields() |
| 970 | 995 | { |