Files
law-api/app/Http/Controllers/api/FolderController.php
2026-07-16 17:56:59 +03:30

365 lines
15 KiB
PHP

<?php
namespace App\Http\Controllers\api;
use App\Http\Controllers\Controller;
use App\Models\Folder;
use App\Models\FolderArt;
use App\Models\FolderBylaw;
use App\Models\FolderJudicialPrecedent;
use App\Models\FolderLegalOpinion;
use App\Traits\BaseApiResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class FolderController extends Controller
{
use BaseApiResponse;
public function index()
{
// Eager load everything to prevent N+1 Database issues
$folder = Folder::with(['arts', 'bylaws', 'judicialPrecedents', 'legalOpinions'])
->where('user_id', auth()->id())
->get()
->map(function ($q) {
return [
'id' => $q->id,
'name' => $q->name,
'count_of_arts' => $q->arts->count(),
'count_of_bylaws' => $q->bylaws->count(),
'count_of_judicials' => $q->judicialPrecedents->count(),
'count_of_opinions' => $q->legalOpinions->count(),
'created_at' => $q->created_at,
];
});
return $this->success($folder);
}
public function create(Request $request)
{
$validted = $request->validate([
'name' => 'required',
]);
Folder::create([
'user_id' => auth()->id(),
'name' => $validted['name'],
]);
return $this->success([], 'ایجاد شد', 'پوشه با موفقیت ایجاد شد.');
}
public function assign(Request $request)
{
$validted = $request->validate([
'folder_id' => 'required',
'art_id' => 'nullable',
'bylaw_document_id' => 'nullable',
'judicial_precedent_id' => 'nullable',
'legal_opinion_id' => 'nullable',
]);
// 🚨 SECURITY FIX: Check if the folder exists AND belongs to the logged-in user
if (Folder::where('id', $validted['folder_id'])->where('user_id', auth()->id())->count() == 0) {
return $this->failed([], 'پوشه مورد نظر یافت نشد یا شما دسترسی ندارید', 404);
}
if ($request->filled('art_id')) {
if (FolderArt::where('folder_id', $validted['folder_id'])->where('art_id', $validted['art_id'])->exists()) {
return $this->failed([], 'ارت مورد نظر قبلا اضافه شده است', 400);
}
FolderArt::create(['folder_id' => $validted['folder_id'], 'art_id' => $validted['art_id']]);
}
elseif ($request->filled('bylaw_document_id')) {
if (FolderBylaw::where('folder_id', $validted['folder_id'])->where('bylaw_document_id', $validted['bylaw_document_id'])->exists()) {
return $this->failed([], 'آیین‌نامه قبلا اضافه شده است', 400);
}
FolderBylaw::create(['folder_id' => $validted['folder_id'], 'bylaw_document_id' => $validted['bylaw_document_id']]);
}
elseif ($request->filled('judicial_precedent_id')) {
if (FolderJudicialPrecedent::where('folder_id', $validted['folder_id'])->where('judicial_precedent_id', $validted['judicial_precedent_id'])->exists()) {
return $this->failed([], 'رأی قبلا اضافه شده است', 400);
}
FolderJudicialPrecedent::create(['folder_id' => $validted['folder_id'], 'judicial_precedent_id' => $validted['judicial_precedent_id']]);
}
elseif ($request->filled('legal_opinion_id')) {
if (FolderLegalOpinion::where('folder_id', $validted['folder_id'])->where('legal_opinion_id', $validted['legal_opinion_id'])->exists()) {
return $this->failed([], 'نظریه قبلا اضافه شده است', 400);
}
FolderLegalOpinion::create(['folder_id' => $validted['folder_id'], 'legal_opinion_id' => $validted['legal_opinion_id']]);
}
else {
return $this->failed([], 'موردی برای افزودن یافت نشد', 400);
}
return $this->success([], 'اضافه شد', 'با موفیت اضافه شد');
}
public function folder($id)
{
// Added 'arts.law.category' so we can fetch category_name without slowing down the DB
$folder = Folder::with([
'arts.chapter', 'arts.part', 'arts.volum', 'arts.law.category', 'arts.book', 'arts.section', 'arts.gate',
'bylaws.category', 'bylaws.approvalAuthority', 'bylaws.promulgationAuthority',
'judicialPrecedents.category',
'legalOpinions.category'
])->where('user_id', auth()->id())->find($id);
if (!$folder) {
return $this->failed([], 'پوشه یافت نشد یا شما دسترسی ندارید', 404);
}
$items = collect();
// 1. Process Arts
$arts = $folder->arts->map(function ($art) {
$text = $art->text ?? '';
$shortText = '';
if (strlen($text) > 50) {
$shortText = mb_substr($text, 0, 50);
$shortText = mb_substr($shortText, 0, mb_strrpos($shortText, ' ')).'...';
} else {
$shortText = $text;
}
$isLocked = ! auth()->user()->isSubscriber() && (bool) $art->law?->is_locked;
return [
'id' => $art->id,
'title' => $art->title,
'text' => $isLocked ? null : $shortText,
'number' => $art->number,
'is_locked' => $isLocked,
'type' => 'art',
'assigned_at' => $art->pivot->created_at,
'law_title' => $art->law?->title,
'law' => $art->law != null ? [
'id' => $art->law->id,
'title' => $art->law->title,
'number' => $art->law->number,
] : null,
'chapter' => $art->chapter != null ? [
'id' => $art->chapter->id,
'title' => $art->chapter->title,
'number' => $art->chapter->number,
] : null,
'part' => $art->part != null ? [
'id' => $art->part->id,
'title' => $art->part->title,
'number' => $art->part->number,
] : null,
'volum' => $art->volum != null ? [
'id' => $art->volum->id,
'title' => $art->volum->title,
'number' => $art->volum->number,
] : null,
'book' => $art->book != null ? [
'id' => $art->book->id,
'title' => $art->book->title,
'number' => $art->book->number,
] : null,
'section' => $art->section != null ? [
'id' => $art->section->id,
'title' => $art->section->title,
'number' => $art->section->number,
] : null,
'gate' => $art->gate != null ? [
'id' => $art->gate->id,
'title' => $art->gate->title,
'number' => $art->gate->number,
] : null,
];
});
$items = $items->concat($arts);
// 2. Process Bylaws
$bylawIds = $folder->bylaws->pluck('id')->toArray();
$allBylawNodes = collect();
if (!empty($bylawIds)) {
$allBylawNodes = DB::table('bylaw_nodes')
->whereIn('document_id', $bylawIds)
->orderBy('sort_order', 'asc')
->get();
}
$bylaws = $folder->bylaws->map(function ($bylaw) use ($allBylawNodes) {
$itemNodes = $allBylawNodes->where('document_id', $bylaw->id);
$formatted = $this->formatBylaw($bylaw, $itemNodes);
$formatted['type'] = 'bylaw';
$formatted['assigned_at'] = $bylaw->pivot->created_at;
return $formatted;
});
$items = $items->concat($bylaws);
// 3. Process Judicial Precedents
$judicials = $folder->judicialPrecedents->map(function ($judicial) {
$formatted = $this->formatJudicial($judicial);
$formatted['type'] = 'judicial_precedent';
$formatted['assigned_at'] = $judicial->pivot->created_at;
return $formatted;
});
$items = $items->concat($judicials);
// 4. Process Legal Opinions
$opinions = $folder->legalOpinions->map(function ($opinion) {
$formatted = $this->formatOpinion($opinion);
$formatted['type'] = 'legal_opinion';
$formatted['assigned_at'] = $opinion->pivot->created_at;
return $formatted;
});
$items = $items->concat($opinions);
// Sort everything together based on when it was added to the folder!
$sortedItems = $items->sortByDesc('assigned_at')->values()->toArray();
return $this->success($sortedItems);
}
public function delete_folder($id)
{
$folder = Folder::where('id', $id)->where('user_id', auth()->id())->first();
if (!$folder) {
return $this->failed('پوشه یافت نشد', 'پوشه مورد نظر یافت نشد یا شما دسترسی ندارید', 404);
}
$folder->delete();
return $this->success([], 'حذف شد', 'پوشه با موفقیت حذف شد');
}
private function checkFolderOwnership($folder_id)
{
return Folder::where('id', $folder_id)->where('user_id', auth()->id())->exists();
}
public function delete_art($id, $art_id)
{
if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403);
if (FolderArt::where('folder_id', $id)->where('art_id', $art_id)->count() == 0) {
return $this->failed([], 'مورد نظر یافت نشد', 404);
}
FolderArt::where('folder_id', $id)->where('art_id', $art_id)->delete();
return $this->success([], 'حذف شد', 'با موفقیت حذف شد');
}
public function delete_bylaw($id, $bylaw_id)
{
if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403);
if (FolderBylaw::where('folder_id', $id)->where('bylaw_document_id', $bylaw_id)->count() == 0) {
return $this->failed([], 'مورد نظر یافت نشد', 404);
}
FolderBylaw::where('folder_id', $id)->where('bylaw_document_id', $bylaw_id)->delete();
return $this->success([], 'حذف شد', 'با موفقیت حذف شد');
}
public function delete_judicial($id, $judicial_id)
{
if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403);
if (FolderJudicialPrecedent::where('folder_id', $id)->where('judicial_precedent_id', $judicial_id)->count() == 0) {
return $this->failed([], 'مورد نظر یافت نشد', 404);
}
FolderJudicialPrecedent::where('folder_id', $id)->where('judicial_precedent_id', $judicial_id)->delete();
return $this->success([], 'حذف شد', 'با موفقیت حذف شد');
}
public function delete_opinion($id, $opinion_id)
{
if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403);
if (FolderLegalOpinion::where('folder_id', $id)->where('legal_opinion_id', $opinion_id)->count() == 0) {
return $this->failed([], 'مورد نظر یافت نشد', 404);
}
FolderLegalOpinion::where('folder_id', $id)->where('legal_opinion_id', $opinion_id)->delete();
return $this->success([], 'حذف شد', 'با موفقیت حذف شد');
}
/* -----------------------------------------------------------------
* FORMATTING HELPERS
* ----------------------------------------------------------------- */
private function isLockedForCurrentUserGeneric($item): bool
{
if (auth()->check() && auth()->user() && method_exists(auth()->user(), 'isSubscriber')) {
return !auth()->user()->isSubscriber() && (bool) ($item->is_locked ?? false);
}
return (bool) ($item->is_locked ?? false);
}
private function formatBylaw($item, $itemNodes = null)
{
$isLocked = $this->isLockedForCurrentUserGeneric($item);
$description = $item->description;
$cleanDesc = trim(str_replace(['&nbsp;', '&zwnj;', "\r", "\n", "\t", ' '], '', strip_tags($description ?? '')));
if (strlen($cleanDesc) < 5) {
if ($itemNodes === null || (is_object($itemNodes) && $itemNodes->isEmpty())) {
$dbNodes = DB::table('bylaw_nodes')->where('document_id', $item->id)->count();
if ($dbNodes > 0) {
$itemNodes = DB::table('bylaw_nodes')->where('document_id', $item->id)->orderBy('sort_order', 'asc')->get();
}
}
$nodeContents = [];
if ($itemNodes) {
foreach ($itemNodes as $node) {
$content = trim(strip_tags($node->node_content ?? ''));
if (!empty($content)) {
$nodeContents[] = $content;
}
}
}
$description = implode("\n\n", $nodeContents);
}
return [
'id' => $item->id,
'title' => $item->title,
'category_name' => optional($item->category)->name,
'approval_authority_name' => optional($item->approvalAuthority)->name,
'approval_date' => $item->approval_date,
'promulgation_number' => $item->promulgation_number,
'description' => $isLocked ? null : mb_substr($description, 0, 100) . '...',
'is_locked' => $isLocked,
];
}
private function formatJudicial($item)
{
$isLocked = $this->isLockedForCurrentUserGeneric($item);
return [
'id' => $item->id,
'title' => $item->subject,
'ruling_number' => $item->ruling_number,
'ruling_date' => $item->ruling_date,
'text' => $isLocked ? null : mb_substr($item->full_text, 0, 100) . '...',
'issuing_authority' => $item->issuing_authority,
'category_name' => optional($item->category)->name,
'is_locked' => $isLocked,
];
}
private function formatOpinion($item)
{
$isLocked = $this->isLockedForCurrentUserGeneric($item);
return [
'id' => $item->id,
'title' => $item->subject,
'opinion_number' => $item->opinion_number,
'opinion_date' => $item->opinion_date,
'text' => $isLocked ? null : mb_substr($item->full_text, 0, 100) . '...',
'issuing_authority' => $item->issuing_authority,
'category_name' => optional($item->category)->name,
'is_locked' => $isLocked,
];
}
}