status = $status; } /** * Execute the job. * * @return void */ public function handle() { $status = $this->status; // Verify status exists if (! $status) { Log::info('DeleteRemoteStatusPipeline: Status no longer exists, skipping job'); return null; } // Verify status has a profile if (! $status->profile_id) { Log::info("DeleteRemoteStatusPipeline: Status {$status->id} has no profile_id, skipping job"); return null; } try { AccountStatService::decrementPostCount($status->profile_id); NetworkTimelineService::del($status->id); StatusService::del($status->id, true); Bookmark::whereStatusId($status->id)->delete(); Notification::whereItemType(Status::class) ->whereItemId($status->id) ->forceDelete(); DirectMessage::whereStatusId($status->id)->delete(); Like::whereStatusId($status->id)->forceDelete(); MediaTag::whereStatusId($status->id)->delete(); $media = Media::whereStatusId($status->id)->get(); // Detach media from the status before dispatching deletion. // status_id has no FK/cascade, so it is not cleared when the status // is deleted; detaching here ensures the row is genuinely orphaned // by the time the MediaDeletePipeline guard checks it, so the // delete is not skipped. Media::whereStatusId($status->id)->update(['status_id' => null]); $media->each(function ($m) { $m->status_id = null; MediaDeletePipeline::dispatch($m)->onQueue('mmo'); }); Mention::whereStatusId($status->id)->forceDelete(); Report::whereObjectType(Status::class)->whereObjectId($status->id)->delete(); // Model-based delete so StatusHashtagObserver::deleted() runs and // decrements hashtags.cached_count (a query-builder delete bypasses it). StatusHashtag::whereStatusId($status->id)->get()->each->delete(); StatusView::whereStatusId($status->id)->delete(); Status::whereReblogOfId($status->id)->forceDelete(); $status->forceDelete(); } catch (\Exception $e) { Log::warning("DeleteRemoteStatusPipeline: Failed to delete status {$status->id}: ".$e->getMessage()); throw $e; } return 1; } }