Функція м'якого видалення в 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 забезпечує всебічне рішення для збереження цілісності даних, підтримуючи складні бізнес-потреби. Цей підхід дозволяє створювати надійні звіти, дотримуватись політики зберігання даних і забезпечувати можливості відновлення без шкоди для продуктивності застосунку
У сучасній веб-розробці аутентифікація є ключовою для захисту додатків і даних користувачів. Дізнайтеся, як модуль nuxt-sanctum-authentication спростить інтеграцію між Nuxt 3 та Laravel Sanctum, забезпечуючи надійний і зручний спосіб реалізації аутентифікації для вашого проєкту
Ви готові відкрити нові горизонти у роботі з геопросторовими даними в Laravel? Дізнайтеся, як за допомогою PostGIS та пакету Laravel-Magellan можна легко зберігати, запитувати та маніпулювати інформацією про розташування, перетворюючи ваші проекти на вражаючі рішення у сфері картографії та геолокації!
Досліджуйте новий пакет Data Model для PHP, який спрощує процес гідратації об'єктів без зайвих складнощів! Дізнайтеся, як впровадження типобезпечних об'єктів може революціонізувати ваш підхід до розробки, читаючи нашу статтю