From 6993bb910df5a81e1da226a489f33f4cdd2a8cb9 Mon Sep 17 00:00:00 2001 From: brufdev Date: Sun, 2 Feb 2025 18:09:19 +0000 Subject: [PATCH] Move components logic to actions --- app/Actions/ExportVault.php | 78 ++++++++++++++++ app/Actions/ProcessImportedFile.php | 57 +++--------- app/Actions/ProcessImportedVault.php | 67 ++++---------- app/Livewire/Vault/Index.php | 78 ++++------------ app/Livewire/Vault/Show.php | 89 ++++++++----------- resources/views/livewire/vault/show.blade.php | 8 +- 6 files changed, 163 insertions(+), 214 deletions(-) create mode 100644 app/Actions/ExportVault.php diff --git a/app/Actions/ExportVault.php b/app/Actions/ExportVault.php new file mode 100644 index 0000000..ad158e7 --- /dev/null +++ b/app/Actions/ExportVault.php @@ -0,0 +1,78 @@ +path($relativePath); + $nodes = $vault->nodes()->whereNull('parent_id')->get(); + + if ($nodes->count() === 0) { + throw new Exception(__('Your vault is empty')); + } + + Storage::disk('local')->put($relativePath, ''); + if ($zip->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { + throw new Exception(__('Something went wrong')); + } + + $this->exportNodes($zip, $nodes); + $zip->close(); + + return $path; + } + + /** + * @param Collection $nodes + */ + private function exportNodes(ZipArchive &$zip, Collection $nodes, string $path = ''): void + { + foreach ($nodes as $node) { + $nodePath = mb_ltrim("$path/$node->name", '/'); + $nodePath .= $node->is_file ? ".$node->extension" : ''; + $relativePath = new GetPathFromVaultNode()->handle($node); + + if (!Storage::disk('local')->exists($relativePath)) { + throw new Exception( + sprintf( + "%s missing on disk: {$nodePath}", + $node->is_file ? 'File' : 'Folder', + ), + ); + } + + if ($node->is_file) { + if ($node->extension === 'md') { + $zip->addFromString($nodePath, (string) $node->content); + } else { + $relativePath = new GetPathFromVaultNode()->handle($node); + + $zip->addFile( + Storage::disk('local')->path($relativePath), + $nodePath, + ); + } + } else { + $zip->addEmptyDir($nodePath); + + if ($node->children()->count()) { + $this->exportNodes($zip, $node->children()->get(), $nodePath); + } + } + } + } +} diff --git a/app/Actions/ProcessImportedFile.php b/app/Actions/ProcessImportedFile.php index 91e0bbe..62580e2 100644 --- a/app/Actions/ProcessImportedFile.php +++ b/app/Actions/ProcessImportedFile.php @@ -6,7 +6,6 @@ namespace App\Actions; use App\Models\Vault; use App\Models\VaultNode; -use App\Services\VaultFile; use App\Services\VaultFiles\Note; use Illuminate\Http\File; use Illuminate\Support\Facades\Storage; @@ -15,53 +14,19 @@ final readonly class ProcessImportedFile { public function handle(Vault $vault, VaultNode $parent, string $fileName, string $filePath): void { - $pathInfo = pathinfo($fileName); - $name = $pathInfo['filename']; - $extension = $pathInfo['extension'] ?? ''; - - if (!in_array($extension, VaultFile::extensions())) { - abort(400); - } - - $content = null; - if (in_array($extension, Note::extensions())) { - $extension = 'md'; - $content = file_get_contents($filePath); - } - - // Find new filename if it already exists - $nodeExists = $vault->nodes() - ->where('parent_id', $parent->id) - ->where('is_file', true) - ->where('name', 'like', "$name") - ->where('extension', 'md') - ->exists(); - if ($nodeExists) { - /** @var list $nodes */ - $nodes = array_column( - $vault->nodes() - ->select('name') - ->where('parent_id', $parent->id) - ->where('is_file', true) - ->where('name', 'like', "$name-%") - ->where('extension', 'md') - ->get() - ->toArray(), - 'name', - ); - natcasesort($nodes); - $name .= count($nodes) && preg_match('/-(\d+)$/', end($nodes), $matches) === 1 ? - '-' . ((int) $matches[1] + 1) : - '-1'; - } - - $node = $vault->nodes()->createQuietly([ + $attributes = [ 'parent_id' => $parent->id, 'is_file' => true, - 'name' => $name, - 'extension' => $extension, - 'content' => $content, - ]); + ]; + $pathInfo = pathinfo($fileName); + $attributes['name'] = $pathInfo['filename']; + $attributes['extension'] = $pathInfo['extension'] ?? ''; + $attributes['content'] = null; + if (in_array($attributes['extension'], Note::extensions())) { + $attributes['extension'] = 'md'; + $attributes['content'] = (string) file_get_contents($filePath); + } + $node = new CreateVaultNode()->handle($vault, $attributes); $relativePath = new GetPathFromVaultNode()->handle($node); $pathInfo = pathinfo($relativePath); diff --git a/app/Actions/ProcessImportedVault.php b/app/Actions/ProcessImportedVault.php index 387da42..7f07b06 100644 --- a/app/Actions/ProcessImportedVault.php +++ b/app/Actions/ProcessImportedVault.php @@ -7,7 +7,6 @@ namespace App\Actions; use App\Models\User; use App\Services\VaultFile; use App\Services\VaultFiles\Note; -use Illuminate\Support\Facades\Storage; use ZipArchive; final readonly class ProcessImportedVault @@ -15,32 +14,10 @@ final readonly class ProcessImportedVault public function handle(string $fileName, string $filePath): void { $nodeIds = ['.' => null]; + $vaultName = pathinfo($fileName, PATHINFO_FILENAME); /** @var User $currentUser */ $currentUser = auth()->user(); - // Create vault with zip name - $vaultName = pathinfo($fileName, PATHINFO_FILENAME); - // Find new vault name if it already exists - $vaultExists = $currentUser->vaults() - ->where('name', 'like', "$vaultName") - ->exists(); - - if ($vaultExists) { - /** @var list $vaults */ - $vaults = array_column( - $currentUser->vaults() - ->select('name') - ->where('name', 'like', "$vaultName-%") - ->get() - ->toArray(), - 'name', - ); - natcasesort($vaults); - $vaultName .= count($vaults) && preg_match('/-(\d+)$/', end($vaults), $matches) === 1 ? - '-' . ((int) $matches[1] + 1) : - '-1'; - } - - $vault = $currentUser->vaults()->create([ + $vault = new CreateVault()->handle($currentUser, [ 'name' => $vaultName, ]); @@ -56,48 +33,36 @@ final readonly class ProcessImportedVault $isFile = !str_ends_with($entryName, '/'); $flags = $isFile ? PATHINFO_FILENAME : PATHINFO_BASENAME; - $name = pathinfo($entryName, $flags); - $extension = null; - $content = null; + $attributes = [ + 'is_file' => $isFile, + 'name' => pathinfo($entryName, $flags), + 'extension' => null, + 'content' => null, + ]; if (!$isFile) { // ZipArchive folder paths end with a / that should // be removed in order for pathinfo() return the correct dirname $entryDirName = mb_rtrim($entryName, '/'); $entryParentDirName = pathinfo($entryDirName, PATHINFO_DIRNAME); - $parentId = $nodeIds[$entryParentDirName]; + $attributes['parent_id'] = $nodeIds[$entryParentDirName]; } else { $pathInfo = pathinfo($entryName); $entryDirName = $pathInfo['dirname']; - $extension = $pathInfo['extension'] ?? ''; - $parentId = $nodeIds[$entryDirName]; + $attributes['extension'] = $pathInfo['extension'] ?? ''; + $attributes['parent_id'] = $nodeIds[$entryDirName]; - if (!in_array($extension, VaultFile::extensions())) { + if (!in_array($attributes['extension'], VaultFile::extensions())) { continue; } - if (in_array($extension, Note::extensions())) { - $extension = 'md'; - $content = $zip->getFromIndex($i); + if (in_array($attributes['extension'], Note::extensions())) { + $attributes['extension'] = 'md'; } - } - $node = $vault->nodes()->createQuietly([ - 'parent_id' => $parentId, - 'is_file' => $isFile, - 'name' => $name, - 'extension' => $extension, - 'content' => $content, - ]); - - $relativePath = new GetPathFromVaultNode()->handle($node); - if ($isFile) { - /** @var string $contents */ - $contents = $zip->getFromIndex($i); - Storage::disk('local')->put($relativePath, $contents); - } else { - Storage::disk('local')->makeDirectory($relativePath); + $attributes['content'] = (string) $zip->getFromIndex($i); } + $node = new CreateVaultNode()->handle($vault, $attributes); if (!array_key_exists($entryDirName, $nodeIds)) { $nodeIds[$entryDirName] = $node->id; diff --git a/app/Livewire/Vault/Index.php b/app/Livewire/Vault/Index.php index 97e7fb8..c731f17 100644 --- a/app/Livewire/Vault/Index.php +++ b/app/Livewire/Vault/Index.php @@ -4,21 +4,16 @@ declare(strict_types=1); namespace App\Livewire\Vault; -use App\Actions\GetPathFromVaultNode; +use App\Actions\DeleteVault; +use App\Actions\ExportVault; use App\Livewire\Forms\VaultForm; use App\Models\User; use App\Models\Vault; -use App\Models\VaultNode; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Str; use Livewire\Component; -use Staudenmeir\LaravelAdjacencyList\Eloquent\Collection; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Throwable; -use ZipArchive; final class Index extends Component { @@ -33,41 +28,34 @@ final class Index extends Component $this->dispatch('toast', message: __('Vault created'), type: 'success'); } - public function export(Vault $vault): ?BinaryFileResponse + public function export(Vault $vault, ExportVault $exportVault): ?BinaryFileResponse { $this->authorize('view', $vault); - $zip = new ZipArchive(); - $zipFileName = $vault->id . '.zip'; - $nodes = $vault->nodes()->whereNull('parent_id')->get(); - if ($zip->open(public_path($zipFileName), ZipArchive::CREATE) !== true) { - $this->dispatch('toast', message: __('Something went wrong'), type: 'error'); + try { + $path = $exportVault->handle($vault); + } catch (Throwable $e) { + $this->dispatch('toast', message: $e->getMessage(), type: 'error'); return null; } - $this->exportNodes($zip, $nodes); - $zip->close(); - - return response()->download(public_path($zipFileName), $vault->name . '.zip')->deleteFileAfterSend(true); + return response()->download($path, $vault->name . '.zip')->deleteFileAfterSend(true); } public function delete(Vault $vault): void { $this->authorize('delete', $vault); - DB::beginTransaction(); + try { - $rootNodes = $vault->nodes()->whereNull('parent_id')->get(); - foreach ($rootNodes as $node) { - $this->deleteNode($node); - } - $vault->delete(); - DB::commit(); - $this->dispatch('toast', message: __('Vault deleted'), type: 'success'); - } catch (Throwable) { - DB::rollBack(); - $this->dispatch('toast', message: __('Something went wrong'), type: 'error'); + new DeleteVault()->handle($vault); + } catch (Throwable $e) { + $this->dispatch('toast', message: $e->getMessage(), type: 'error'); + + return; } + + $this->dispatch('toast', message: __('Vault deleted'), type: 'success'); } public function render(): Factory|View @@ -79,38 +67,4 @@ final class Index extends Component 'vaults' => $currentUser->vaults()->orderBy('updated_at', 'DESC')->get(), ]); } - - /** - * @param Collection $nodes - */ - private function exportNodes(ZipArchive &$zip, Collection $nodes, string $path = ''): void - { - foreach ($nodes as $node) { - $nodePath = Str::ltrim("$path/$node->name", '/'); - - if ($node->is_file) { - if ($node->extension === 'md') { - $zip->addFromString("$nodePath.$node->extension", (string) $node->content); - } else { - $relativePath = new GetPathFromVaultNode()->handle($node); - $filePath = Storage::disk('local')->path($relativePath); - $zip->addFile($filePath, "$nodePath.$node->extension"); - } - } else { - $zip->addEmptyDir($nodePath); - - if ($node->children->count()) { - $this->exportNodes($zip, $node->children, $nodePath); - } - } - } - } - - private function deleteNode(VaultNode $node): void - { - foreach ($node->childs as $child) { - $this->deleteNode($child); - } - $node->delete(); - } } diff --git a/app/Livewire/Vault/Show.php b/app/Livewire/Vault/Show.php index b2d020c..02a028a 100644 --- a/app/Livewire/Vault/Show.php +++ b/app/Livewire/Vault/Show.php @@ -4,10 +4,11 @@ declare(strict_types=1); namespace App\Livewire\Vault; -use App\Actions\GetPathFromVaultNode; +use App\Actions\DeleteVaultNode; use App\Actions\GetUrlFromVaultNode; use App\Actions\GetVaultNodeFromPath; use App\Actions\ResolveTwoPaths; +use App\Actions\UpdateVault; use App\Livewire\Forms\VaultForm; use App\Livewire\Forms\VaultNodeForm; use App\Models\Vault; @@ -36,13 +37,10 @@ final class Show extends Component #[Url(as: 'file')] public ?int $selectedFile = null; - public ?string $selectedFilePath = null; + public ?string $selectedFileUrl = null; public bool $isEditMode = true; - /** @var list */ - private array $deletedNodes = []; - public function mount(Vault $vault): void { $this->authorize('view', $vault); @@ -52,7 +50,10 @@ final class Show extends Component $this->getTemplates(); if ((int) $this->selectedFile > 0) { - $selectedFile = $vault->nodes()->where('id', $this->selectedFile)->first(); + $selectedFile = $vault->nodes() + ->where('id', $this->selectedFile) + ->where('is_file', true) + ->first(); if (!$selectedFile) { $this->selectedFile = null; @@ -69,11 +70,13 @@ final class Show extends Component $this->authorize('view', $node->vault); if (!$node->vault || !$node->vault->is($this->vault) || !$node->is_file) { + $this->selectedFile = null; + return; } $this->selectedFile = $node->id; - $this->selectedFilePath = new GetUrlFromVaultNode()->handle($node); + $this->selectedFileUrl = new GetUrlFromVaultNode()->handle($node); $this->nodeForm->setNode($node); if ($node->extension === 'md') { @@ -85,12 +88,11 @@ final class Show extends Component public function openFilePath(string $path): void { - /** - * @var string $currentPath - * - * @phpstan-ignore-next-line larastan.noUnnecessaryCollectionCall - */ - $currentPath = $this->nodeForm->node->ancestorsAndSelf()->get()->last()->full_path; + /** @var string $currentPath */ + $currentPath = is_null($this->nodeForm->node) + ? '' + /** @phpstan-ignore-next-line larastan.noUnnecessaryCollectionCall */ + : $this->nodeForm->node->ancestorsAndSelf()->get()->last()->full_path; $resolvedPath = new ResolveTwoPaths()->handle($currentPath, $path); $node = new GetVaultNodeFromPath()->handle($this->vault->id, $resolvedPath); @@ -110,14 +112,13 @@ final class Show extends Component return; } - $this->selectedFile = $node->id; - $this->selectedFilePath = new GetPathFromVaultNode()->handle($node); + $this->selectedFileUrl = new GetUrlFromVaultNode()->handle($node); $this->nodeForm->setNode($node); } public function closeFile(): void { - $this->reset(['selectedFile', 'selectedFilePath']); + $this->reset(['selectedFile', 'selectedFileUrl']); $this->nodeForm->reset('node'); } @@ -157,7 +158,9 @@ final class Show extends Component return; } - $this->vault->update(['templates_node_id' => $node->id]); + new UpdateVault()->handle($this->vault, [ + 'templates_node_id' => $node->id, + ]); $this->getTemplates(); $this->dispatch('toast', message: __('Template folder updated'), type: 'success'); } @@ -212,30 +215,38 @@ final class Show extends Component { $this->authorize('delete', $node->vault); - DB::beginTransaction(); try { - if ($node->is_file) { - $this->deleteFile($node); - } else { - $this->deleteFolder($node); + DB::beginTransaction(); + $deletedNodes = new DeleteVaultNode()->handle($node); + $this->dispatch('node-updated'); + + $openFileDeleted = !is_null( + array_find( + $deletedNodes, + fn (VaultNode $node): bool => $node->id === $this->selectedFile, + ) + ); + if ($openFileDeleted) { + $this->closeFile(); } - DB::commit(); - $this->dispatch('node-updated'); $templateDeleted = !is_null( array_find( - $this->deletedNodes, + $deletedNodes, fn (VaultNode $node): bool => $node->parent_id === $this->vault->templates_node_id, ) ); if ($templateDeleted) { $this->getTemplates(); } - $this->deletedNodes = []; + + DB::commit(); $message = $node->is_file ? __('File deleted') : __('Folder deleted'); $this->dispatch('toast', message: $message, type: 'success'); - } catch (Throwable) { + } catch (Throwable $e) { DB::rollBack(); + + $this->dispatch('toast', message: $e->getMessage(), type: 'error'); } } @@ -243,28 +254,4 @@ final class Show extends Component { return view('livewire.vault.show'); } - - private function deleteFile(VaultNode $node): void - { - if ($this->selectedFile === $node->id) { - $this->closeFile(); - } - - $this->deletedNodes[] = $node; - $node->delete(); - } - - private function deleteFolder(VaultNode $node): void - { - foreach ($node->childs as $child) { - if ($child->is_file) { - $this->deleteFile($child); - } else { - $this->deleteFolder($child); - } - } - - $this->deletedNodes[] = $node; - $node->delete(); - } } diff --git a/resources/views/livewire/vault/show.blade.php b/resources/views/livewire/vault/show.blade.php index 9d4bd1a..2f0d859 100644 --- a/resources/views/livewire/vault/show.blade.php +++ b/resources/views/livewire/vault/show.blade.php @@ -163,20 +163,20 @@ @elseif (in_array($nodeForm->extension, App\Services\VaultFiles\Image::extensions()))
- +
@elseif (in_array($nodeForm->extension, App\Services\VaultFiles\Pdf::extensions())) - @elseif (in_array($nodeForm->extension, App\Services\VaultFiles\Video::extensions())) @elseif (in_array($nodeForm->extension, App\Services\VaultFiles\Audio::extensions()))