Функція м'якого видалення в Laravel пропонує витончений підхід до управління даними, зберігаючи повну інформацію, водночас дозволяючи логічне видалення. Ця функція є важливою для відповідності вимогам, відновлення даних і підтримання зв'язності у складних додатках.
Додайте необхідний стовпець до бази даних за допомогою міграцій Laravel:
Schema::table('documents', function (Blueprint $table) {
$table->softDeletes();
});
Увімкніть м'яке видалення у вашій Eloquent моделі:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Document extends Model
{
use SoftDeletes;
protected $fillable = ['title', 'content', 'category', 'author_id'];
}
Основні операції з м'яким видаленням працюють безперешкодно:
// М'яке видалення документа
$document->delete();
// Відновлення м'яко видаленого документа
$document->restore();
// Перевірка, чи документ м'яко видалений
if ($document->trashed()) {
// Обробка стану м'якого видалення
}
Розгляньмо систему управління юридичними документами, де важливо зберігати повні звіти та забезпечувати дотримання нормативних вимог. Організаціям потрібно відстежувати зміни у життєвому циклі документів, зберігаючи історичні дані для судових справ і дотримання норм:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class LegalDocument extends Model
{
use SoftDeletes;
protected $fillable = [
'title', 'document_type', 'classification', 'retention_period',
'author_id', 'department_id', 'version', 'content_hash'
];
protected $casts = [
'retention_expires_at' => 'datetime',
'archived_at' => 'datetime',
];
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'author_id');
}
public function department(): BelongsTo
{
return $this->belongsTo(Department::class);
}
public function revisions(): HasMany
{
return $this->hasMany(DocumentRevision::class);
}
public function annotations(): HasMany
{
return $this->hasMany(DocumentAnnotation::class);
}
protected static function booted()
{
static::deleting(function ($document) {
// М'яке видалення пов'язаних анотацій
$document->annotations()->delete();
// Логування видалення для звіту
ActivityLog::create([
'action' => 'document_deleted',
'subject_type' => LegalDocument::class,
'subject_id' => $document->id,
'user_id' => auth()->id(),
'properties' => [
'document_title' => $document->title,
'classification' => $document->classification,
'deletion_reason' => request()->input('deletion_reason'),
],
]);
});
static::restoring(function ($document) {
// Відновлення пов'язаних анотацій
$document->annotations()->restore();
// Логування відновлення
ActivityLog::create([
'action' => 'document_restored',
'subject_type' => LegalDocument::class,
'subject_id' => $document->id,
'user_id' => auth()->id(),
'properties' => [
'document_title' => $document->title,
'restored_from' => $document->deleted_at,
],
]);
});
}
public function scopeAwaitingDestruction($query)
{
return $query->onlyTrashed()
->where('retention_expires_at', '<', now())
->where('deleted_at', '<', now()->subMonths(6));
}
public function isPermanentlyDeletable(): bool
{
return $this->trashed() &&
$this->retention_expires_at < now() &&
$this->deleted_at < now()->subMonths(6);
}
}
class DocumentRevision extends Model
{
use SoftDeletes;
protected $fillable = [
'document_id', 'version_number', 'change_summary',
'content_diff', 'author_id'
];
public function document(): BelongsTo
{
return $this->belongsTo(LegalDocument::class, 'document_id');
}
}
class DocumentAnnotation extends Model
{
use SoftDeletes;
protected $fillable = [
'document_id', 'author_id', 'annotation_text',
'page_number', 'position_data'
];
public function document(): BelongsTo
{
return $this->belongsTo(LegalDocument::class, 'document_id');
}
}
Розширені можливості запитів дозволяють управляти складними сценаріями відповідності:
<?php
namespace App\Services;
use App\Models\LegalDocument;
use Illuminate\Support\Collection;
class DocumentComplianceService
{
public function getActiveDocuments(): Collection
{
return LegalDocument::where('classification', '!=', 'archived')
->orderBy('updated_at', 'desc')
->get();
}
public function getDeletedDocumentsForAudit(): Collection
{
return LegalDocument::onlyTrashed()
->with(['author', 'department'])
->orderBy('deleted_at', 'desc')
->get();
}
public function getAllDocumentsIncludingDeleted(): Collection
{
return LegalDocument::withTrashed()
->with(['author', 'department', 'annotations'])
->orderBy('created_at', 'desc')
->get();
}
public function findDocumentForLitigation(int $documentId): ?LegalDocument
{
// Включити м'яко видалені документи для юридичного розслідування
return LegalDocument::withTrashed()
->with(['revisions.author', 'annotations'])
->find($documentId);
}
public function getExpiredDocumentsAwaitingDestruction(): Collection
{
return LegalDocument::awaitingDestruction()
->with(['author', 'department'])
->get();
}
public function performRetentionCleanup(): array
{
$expiredDocuments = $this->getExpiredDocumentsAwaitingDestruction();
$deletedCount = 0;
$errors = [];
foreach ($expiredDocuments as $document) {
try {
if ($document->isPermanentlyDeletable()) {
// Примусово видалити після закінчення терміну зберігання
$document->forceDelete();
$deletedCount++;
ActivityLog::create([
'action' => 'document_permanently_deleted',
'subject_type' => LegalDocument::class,
'subject_id' => $document->id,
'properties' => [
'document_title' => $document->title,
'retention_expired' => $document->retention_expires_at,
'soft_deleted_at' => $document->deleted_at,
],
]);
}
} catch (\Exception $e) {
$errors[] = [
'document_id' => $document->id,
'error' => $e->getMessage(),
];
}
}
return [
'deleted_count' => $deletedCount,
'errors' => $errors,
];
}
public function restoreDocumentWithRelated(int $documentId): bool
{
$document = LegalDocument::withTrashed()->find($documentId);
if (!$document || !$document->trashed()) {
return false;
}
// Відновлення документа та всіх пов'язаних анотацій
$document->restore();
return true;
}
}
class DocumentController extends Controller
{
public function __construct(
private DocumentComplianceService $complianceService
) {}
public function destroy(LegalDocument $document, Request $request)
{
$request->validate([
'deletion_reason' => 'required|string|max:500',
]);
$document->delete();
return response()->json([
'message' => 'Документ м'яко видалено успішно',
'can_restore' => true,
'deleted_at' => $document->fresh()->deleted_at,
]);
}
public function restore(int $documentId)
{
$restored = $this->complianceService->restoreDocumentWithRelated($documentId);
if (!$restored) {
return response()->json(['message' => 'Документ не знайдено або не видалено'], 404);
}
return response()->json(['message' => 'Документ успішно відновлено']);
}
public function audit()
{
return response()->json([
'active_documents' => $this->complianceService->getActiveDocuments()->count(),
'deleted_documents' => $this->complianceService->getDeletedDocumentsForAudit()->count(),
'pending_destruction' => $this->complianceService->getExpiredDocumentsAwaitingDestruction()->count(),
]);
}
}
М'яке видалення у Laravel забезпечує всебічне рішення для збереження цілісності даних, підтримуючи складні бізнес-потреби. Цей підхід дозволяє створювати надійні звіти, дотримуватись політики зберігання даних і забезпечувати можливості відновлення без шкоди для продуктивності застосунку