mirror of
https://github.com/brufdev/many-notes.git
synced 2026-01-23 19:38:46 -06:00
Move observer logic to actions
This commit is contained in:
48
app/Actions/CreateVault.php
Normal file
48
app/Actions/CreateVault.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Vault;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
final readonly class CreateVault
|
||||
{
|
||||
/**
|
||||
* @param array{name: string} $attributes
|
||||
*/
|
||||
public function handle(User $user, array $attributes): Vault
|
||||
{
|
||||
// Generate a new vault name if the current one already exists
|
||||
$vaultExists = $user->vaults()
|
||||
->where('name', 'like', $attributes['name'])
|
||||
->exists();
|
||||
|
||||
if ($vaultExists) {
|
||||
/** @var list<string> $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;
|
||||
}
|
||||
}
|
||||
75
app/Actions/CreateVaultNode.php
Normal file
75
app/Actions/CreateVaultNode.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Models\Vault;
|
||||
use App\Models\VaultNode;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
final readonly class CreateVaultNode
|
||||
{
|
||||
/**
|
||||
* @param array{
|
||||
* parent_id?: int|null,
|
||||
* is_file: bool,
|
||||
* name: string,
|
||||
* extension?: string|null,
|
||||
* content?: string|null
|
||||
* } $attributes
|
||||
*/
|
||||
public function handle(Vault $vault, array $attributes): VaultNode
|
||||
{
|
||||
$attributes['parent_id'] ??= null;
|
||||
$attributes['extension'] ??= null;
|
||||
$attributes['content'] ??= null;
|
||||
|
||||
// Generate a new filename if the current one already exists
|
||||
$nodeExists = $vault->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<string> $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;
|
||||
}
|
||||
}
|
||||
58
app/Actions/DeleteVault.php
Normal file
58
app/Actions/DeleteVault.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Vault;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Throwable;
|
||||
|
||||
final readonly class DeleteVault
|
||||
{
|
||||
public function handle(Vault $vault): void
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
78
app/Actions/DeleteVaultNode.php
Normal file
78
app/Actions/DeleteVaultNode.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Models\VaultNode;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Throwable;
|
||||
|
||||
final readonly class DeleteVaultNode
|
||||
{
|
||||
/**
|
||||
* Handles the action.
|
||||
*
|
||||
* @return array<int, VaultNode>
|
||||
*/
|
||||
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<int, VaultNode>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
app/Actions/UpdateVault.php
Normal file
34
app/Actions/UpdateVault.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Vault;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
final readonly class UpdateVault
|
||||
{
|
||||
/**
|
||||
* @param array{name?: string, templates_node_id?: int|null} $attributes
|
||||
*/
|
||||
public function handle(Vault $vault, array $attributes): void
|
||||
{
|
||||
/** @var array{name: string} $original */
|
||||
$original = $vault->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,
|
||||
);
|
||||
}
|
||||
}
|
||||
36
app/Actions/UpdateVaultNode.php
Normal file
36
app/Actions/UpdateVaultNode.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Models\VaultNode;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
final readonly class UpdateVaultNode
|
||||
{
|
||||
/**
|
||||
* @param array{
|
||||
* parent_id?: int|null,
|
||||
* is_file: bool,
|
||||
* name: string,
|
||||
* extension?: string|null,
|
||||
* content?: string|null
|
||||
* } $attributes
|
||||
*/
|
||||
public function handle(VaultNode $node, array $attributes): void
|
||||
{
|
||||
$relativeOriginalPath = new GetPathFromVaultNode()->handle($node);
|
||||
$node->update($attributes);
|
||||
|
||||
if (!$node->wasChanged('name')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relativePath = new GetPathFromVaultNode()->handle($node);
|
||||
Storage::disk('local')->move(
|
||||
$relativeOriginalPath,
|
||||
$relativePath,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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> */
|
||||
|
||||
@@ -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> */
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Actions\GetPathFromVaultNode;
|
||||
use App\Models\VaultNode;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
final readonly class VaultNodeObserver
|
||||
{
|
||||
/**
|
||||
* Handle the VaultNode "creating" event.
|
||||
*/
|
||||
public function creating(VaultNode $node): void
|
||||
{
|
||||
$relativePath = new GetPathFromVaultNode()->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Actions\GetPathFromUser;
|
||||
use App\Models\User;
|
||||
use App\Models\Vault;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
final readonly class VaultObserver
|
||||
{
|
||||
/**
|
||||
* Handle the Vault "creating" event.
|
||||
*/
|
||||
public function creating(Vault $vault): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $vault->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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user