diff --git a/app/Actions/CreateVault.php b/app/Actions/CreateVault.php new file mode 100644 index 0000000..91b4afb --- /dev/null +++ b/app/Actions/CreateVault.php @@ -0,0 +1,48 @@ +vaults() + ->where('name', 'like', $attributes['name']) + ->exists(); + + if ($vaultExists) { + /** @var list $vaults */ + $vaults = array_column( + $user->vaults() + ->select('name') + ->where('name', 'like', $attributes['name'] . '-%') + ->get() + ->toArray(), + 'name', + ); + natcasesort($vaults); + $attributes['name'] .= count($vaults) && preg_match('/-(\d+)$/', end($vaults), $matches) === 1 ? + '-' . ((int) $matches[1] + 1) : + '-1'; + } + + // Save vault to database + $vault = $user->vaults()->create($attributes); + + // Save vault to disk + $vaultPath = new GetPathFromVault()->handle($vault); + Storage::disk('local')->makeDirectory($vaultPath); + + return $vault; + } +} diff --git a/app/Actions/CreateVaultNode.php b/app/Actions/CreateVaultNode.php new file mode 100644 index 0000000..a0f3c4c --- /dev/null +++ b/app/Actions/CreateVaultNode.php @@ -0,0 +1,75 @@ +nodes() + ->where('parent_id', $attributes['parent_id']) + ->where('is_file', $attributes['is_file']) + ->where('name', 'like', $attributes['name']) + ->where('extension', $attributes['extension']) + ->exists(); + + if ($nodeExists) { + /** @var list $nodes */ + $nodes = array_column( + $vault->nodes() + ->select('name') + ->where('parent_id', $attributes['parent_id']) + ->where('is_file', $attributes['is_file']) + ->where('name', 'like', $attributes['name'] . '-%') + ->where('extension', $attributes['extension']) + ->get() + ->toArray(), + 'name', + ); + natcasesort($nodes); + $attributes['name'] .= count($nodes) && preg_match('/-(\d+)$/', end($nodes), $matches) === 1 + ? '-' . ((int) $matches[1] + 1) + : '-1'; + } + + // Save node to database + $databaseContent = $attributes['extension'] === 'md' ? $attributes['content'] : null; + $node = $vault->nodes()->create([ + 'parent_id' => $attributes['parent_id'], + 'is_file' => $attributes['is_file'], + 'name' => $attributes['name'], + 'extension' => $attributes['extension'], + 'content' => $databaseContent, + ]); + + // Save node to disk + $nodePath = new GetPathFromVaultNode()->handle($node); + if ($node->is_file) { + Storage::disk('local')->put($nodePath, $attributes['content'] ?? ''); + } else { + Storage::disk('local')->makeDirectory($nodePath); + } + + return $node; + } +} diff --git a/app/Actions/DeleteVault.php b/app/Actions/DeleteVault.php new file mode 100644 index 0000000..31eac37 --- /dev/null +++ b/app/Actions/DeleteVault.php @@ -0,0 +1,58 @@ +deleteFromDatabase($vault); + DB::commit(); + } catch (Throwable) { + DB::rollBack(); + throw new Exception(__('Something went wrong')); + } + + $this->deleteFromDisk($vault); + } + + /** + * Deletes vault from the database. + */ + private function deleteFromDatabase(Vault $vault): void + { + $deleteVaultNode = new DeleteVaultNode(); + $rootNodes = $vault->nodes()->whereNull('parent_id')->get(); + foreach ($rootNodes as $node) { + $deleteVaultNode->handle($node, false); + } + $vault->delete(); + } + + /** + * Deletes vault from the disk. + */ + private function deleteFromDisk(Vault $vault): void + { + /** @var User $user */ + $user = $vault->user()->first(); + $vaultPath = new GetPathFromUser()->handle($user) . $vault->name; + + if (!Storage::disk('local')->exists($vaultPath)) { + return; + } + + Storage::disk('local')->deleteDirectory($vaultPath); + } +} diff --git a/app/Actions/DeleteVaultNode.php b/app/Actions/DeleteVaultNode.php new file mode 100644 index 0000000..a345339 --- /dev/null +++ b/app/Actions/DeleteVaultNode.php @@ -0,0 +1,78 @@ + + */ + public function handle(VaultNode $node, bool $deleteFromDisk = true): array + { + try { + DB::beginTransaction(); + $deletedNodes = $this->deleteFromDatabase($node); + DB::commit(); + } catch (Throwable) { + DB::rollBack(); + throw new Exception(__('Something went wrong')); + } + + if ($deleteFromDisk) { + $this->deleteFromDisk($node); + } + + return $deletedNodes; + } + + /** + * Deletes node from the database. + * + * @return array + */ + private function deleteFromDatabase(VaultNode $node): array + { + $deletedNodes = [$node]; + + if (!$node->is_file) { + foreach ($node->children()->get() as $child) { + $deletedNodes = array_merge( + $deletedNodes, + $this->deleteFromDatabase($child), + ); + } + } + + $node->delete(); + + return $deletedNodes; + } + + /** + * Deletes node from the disk. + */ + private function deleteFromDisk(VaultNode $node): void + { + $nodePath = new GetPathFromVaultNode()->handle($node); + + if (!Storage::disk('local')->exists($nodePath)) { + return; + } + + if ($node->is_file) { + Storage::disk('local')->delete($nodePath); + } else { + Storage::disk('local')->deleteDirectory($nodePath); + } + } +} diff --git a/app/Actions/UpdateVault.php b/app/Actions/UpdateVault.php new file mode 100644 index 0000000..928370d --- /dev/null +++ b/app/Actions/UpdateVault.php @@ -0,0 +1,34 @@ +toArray(); + $vault->update($attributes); + + if (!$vault->wasChanged('name')) { + return; + } + + /** @var User $user */ + $user = $vault->user()->first(); + $relativePath = new GetPathFromUser()->handle($user); + Storage::disk('local')->move( + $relativePath . $original['name'], + $relativePath . $vault->name, + ); + } +} diff --git a/app/Actions/UpdateVaultNode.php b/app/Actions/UpdateVaultNode.php new file mode 100644 index 0000000..b2b29d8 --- /dev/null +++ b/app/Actions/UpdateVaultNode.php @@ -0,0 +1,36 @@ +handle($node); + $node->update($attributes); + + if (!$node->wasChanged('name')) { + return; + } + + $relativePath = new GetPathFromVaultNode()->handle($node); + Storage::disk('local')->move( + $relativeOriginalPath, + $relativePath, + ); + } +} diff --git a/app/Livewire/Forms/VaultForm.php b/app/Livewire/Forms/VaultForm.php index b10e8b9..4aab819 100644 --- a/app/Livewire/Forms/VaultForm.php +++ b/app/Livewire/Forms/VaultForm.php @@ -4,9 +4,10 @@ declare(strict_types=1); namespace App\Livewire\Forms; +use App\Actions\CreateVault; +use App\Actions\UpdateVault; use App\Models\User; use App\Models\Vault; -use Illuminate\Support\Str; use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Unique; use Livewire\Attributes\Validate; @@ -47,11 +48,11 @@ final class VaultForm extends Form public function create(): void { + $this->name = mb_trim($this->name); $this->validate(); /** @var User $currentUser */ $currentUser = auth()->user(); - $this->name = Str::trim($this->name); - $currentUser->vaults()->create([ + new CreateVault()->handle($currentUser, [ 'name' => $this->name, ]); $this->reset(['name']); @@ -59,14 +60,14 @@ final class VaultForm extends Form public function update(): void { - $this->validate(); - if (is_null($this->vault)) { return; } - $this->name = Str::trim($this->name); - $this->vault->update([ + $this->name = mb_trim($this->name); + $this->validate(); + + new UpdateVault()->handle($this->vault, [ 'name' => $this->name, ]); } diff --git a/app/Livewire/Forms/VaultNodeForm.php b/app/Livewire/Forms/VaultNodeForm.php index 0800e7b..6f265a3 100644 --- a/app/Livewire/Forms/VaultNodeForm.php +++ b/app/Livewire/Forms/VaultNodeForm.php @@ -4,9 +4,10 @@ declare(strict_types=1); namespace App\Livewire\Forms; +use App\Actions\CreateVaultNode; +use App\Actions\UpdateVaultNode; use App\Models\Vault; use App\Models\VaultNode; -use Illuminate\Support\Str; use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Unique; use Livewire\Attributes\Validate; @@ -65,9 +66,9 @@ final class VaultNodeForm extends Form public function create(): VaultNode { + $this->name = mb_trim($this->name); $this->validate(); - $this->name = Str::trim($this->name); - $node = $this->vault->nodes()->create([ + $node = new CreateVaultNode()->handle($this->vault, [ 'parent_id' => $this->parent_id, 'is_file' => $this->is_file, 'name' => $this->name, @@ -81,16 +82,18 @@ final class VaultNodeForm extends Form public function update(): void { - $this->validate(); - if (is_null($this->node)) { return; } - $this->name = Str::trim($this->name); - $this->node->update([ + $this->name = mb_trim($this->name); + $this->validate(); + + new UpdateVaultNode()->handle($this->node, [ 'parent_id' => $this->parent_id, + 'is_file' => (bool) $this->node->is_file, 'name' => $this->name, + 'extension' => $this->node->extension, 'content' => $this->content, ]); } diff --git a/app/Models/Vault.php b/app/Models/Vault.php index 63223cf..9711a39 100644 --- a/app/Models/Vault.php +++ b/app/Models/Vault.php @@ -4,15 +4,12 @@ declare(strict_types=1); namespace App\Models; -use App\Observers\VaultObserver; -use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; -#[ObservedBy([VaultObserver::class])] final class Vault extends Model { /** @use HasFactory<\Database\Factories\VaultFactory> */ diff --git a/app/Models/VaultNode.php b/app/Models/VaultNode.php index 64adb87..3e832f8 100644 --- a/app/Models/VaultNode.php +++ b/app/Models/VaultNode.php @@ -4,15 +4,12 @@ declare(strict_types=1); namespace App\Models; -use App\Observers\VaultNodeObserver; -use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships; -#[ObservedBy([VaultNodeObserver::class])] final class VaultNode extends Model { /** @use HasFactory<\Database\Factories\VaultNodeFactory> */ diff --git a/app/Observers/VaultNodeObserver.php b/app/Observers/VaultNodeObserver.php deleted file mode 100644 index 8bd2725..0000000 --- a/app/Observers/VaultNodeObserver.php +++ /dev/null @@ -1,77 +0,0 @@ -handle($node); - - if (Storage::disk('local')->exists($relativePath)) { - abort(500); - } - - if ($node->is_file) { - Storage::disk('local')->put($relativePath, ''); - } else { - Storage::disk('local')->makeDirectory($relativePath); - } - } - - /** - * Handle the VaultNode "updating" event. - */ - public function updating(VaultNode $node): void - { - $relativePath = new GetPathFromVaultNode()->handle($node, false); - - if (Storage::disk('local')->exists($relativePath . $node->name)) { - abort(500); - } - - if ($node->isDirty('name')) { - /** @var string $originalName */ - $originalName = $node->getOriginal('name'); - $paths = [ - $relativePath . $originalName, - $relativePath . $node->name, - ]; - if ($node->is_file) { - $paths[0] .= '.' . $node->extension; - $paths[1] .= '.' . $node->extension; - } - Storage::disk('local')->move(...$paths); - } - - if ($node->is_file) { - Storage::disk('local')->put( - $relativePath . $node->name . '.' . $node->extension, - $node->content ?? '', - ); - } - } - - /** - * Handle the VaultNode "deleting" event. - */ - public function deleting(VaultNode $node): void - { - $relativePath = new GetPathFromVaultNode()->handle($node); - - if ($node->is_file) { - Storage::disk('local')->delete($relativePath); - } else { - Storage::disk('local')->deleteDirectory($relativePath); - } - } -} diff --git a/app/Observers/VaultObserver.php b/app/Observers/VaultObserver.php deleted file mode 100644 index 605ea7e..0000000 --- a/app/Observers/VaultObserver.php +++ /dev/null @@ -1,68 +0,0 @@ -user; - - $relativePath = new GetPathFromUser()->handle($user); - - if (Storage::disk('local')->exists($relativePath . $vault->name)) { - abort(500); - } - - Storage::disk('local')->makeDirectory($relativePath . $vault->name); - } - - /** - * Handle the Vault "updating" event. - */ - public function updating(Vault $vault): void - { - /** @var User $user */ - $user = $vault->user; - - if (!$vault->isDirty('name')) { - return; - } - - $relativePath = new GetPathFromUser()->handle($user); - - if (Storage::disk('local')->exists($relativePath . $vault->name)) { - abort(500); - } - - /** @var string $originalName */ - $originalName = $vault->getOriginal('name'); - Storage::disk('local')->move( - $relativePath . $originalName, - $relativePath . $vault->name, - ); - } - - /** - * Handle the Vault "deleting" event. - */ - public function deleting(Vault $vault): void - { - /** @var User $user */ - $user = $vault->user; - - $relativePath = new GetPathFromUser()->handle($user); - Storage::disk('local')->deleteDirectory($relativePath . $vault->name); - } -}