diff --git a/app/Filament/Admin/Resources/ComputerScienceResource.php b/app/Filament/Admin/Resources/ComputerScienceResource.php index 4d305e3c..a58068ca 100644 --- a/app/Filament/Admin/Resources/ComputerScienceResource.php +++ b/app/Filament/Admin/Resources/ComputerScienceResource.php @@ -44,6 +44,9 @@ public static function form(Form $form): Form Forms\Components\TextInput::make('slug') ->maxLength(255), + Forms\Components\TextInput::make('page_url') + ->maxLength(255), + Forms\Components\Select::make('user_id') ->relationship('user', 'name') ->required(), @@ -84,6 +87,7 @@ public static function table(Table $table): Table ->description(fn (ModelsComputerScienceResource $resource): string => $resource->user->id)->wrap(), TextColumn::make('name')->searchable() ->description(fn (ModelsComputerScienceResource $resource): string => $resource->description)->wrap(), + TextColumn::make('page_url')->searchable()->wrap()->copyable(), TextColumn::make('topic_tags') ->label('topics_tags') ->badge() diff --git a/app/Http/Controllers/ResourceEditsController.php b/app/Http/Controllers/ResourceEditsController.php index ef23c0ed..2b6ff2bf 100644 --- a/app/Http/Controllers/ResourceEditsController.php +++ b/app/Http/Controllers/ResourceEditsController.php @@ -5,7 +5,6 @@ use App\Http\Requests\StoreResourceEdit; use App\Models\ComputerScienceResource; use App\Models\ResourceEdits; -use App\Services\DataNormalizationService; use App\Services\ResourceEditsService; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -18,7 +17,7 @@ class ResourceEditsController extends Controller { public function __construct( - private DataNormalizationService $dataNormalizationService + protected ResourceEditsService $resourceEditsService ) {} /** @@ -42,7 +41,7 @@ public function store(ComputerScienceResource $computerScienceResource, StoreRes $validatedData = $request->validated(); $proposedChanges = $validatedData['proposed_changes'] ?? []; - $actualChanges = $this->calculateChanges($computerScienceResource, $proposedChanges); + $actualChanges = $this->resourceEditsService->calculateChanges($computerScienceResource, $proposedChanges); // Add image path to the actual changes if (array_key_exists('image_file', $proposedChanges)) { @@ -72,25 +71,6 @@ public function store(ComputerScienceResource $computerScienceResource, StoreRes ->with('success', 'Edits Created!'); } - /** - * Calculate the actual differences between the proposed changes and the original resource. - */ - private function calculateChanges(ComputerScienceResource $resource, array $proposedChanges): array - { - $actualChanges = []; - $normalizedProposed = $this->dataNormalizationService->normalize($proposedChanges); - $normalizedOriginal = $this->dataNormalizationService->normalize($resource->toArray()); - - foreach ($normalizedProposed as $key => $value) { - if (! array_key_exists($key, $normalizedOriginal) || $normalizedOriginal[$key] !== $value) { - // Use the original value from the request, not the normalized one, for file uploads. - $actualChanges[$key] = $proposedChanges[$key]; - } - } - - return $actualChanges; - } - public function show(string $slug) { $resourceEdits = ResourceEdits::where('slug', $slug)->firstOrFail(); diff --git a/app/Models/ComputerScienceResource.php b/app/Models/ComputerScienceResource.php index 045e9e67..5433f2c1 100644 --- a/app/Models/ComputerScienceResource.php +++ b/app/Models/ComputerScienceResource.php @@ -6,6 +6,7 @@ use App\Observers\ComputerScienceResourceObserver; use App\Traits\HasComments; use App\Traits\HasVotes; +use App\Utilities\UrlUtilities; use Cviebrock\EloquentSluggable\Sluggable; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -89,6 +90,13 @@ protected function imageUrl(): Attribute ); } + protected function pageUrl(): Attribute + { + return Attribute::make( + set: fn ($value) => UrlUtilities::normalize($value) + ); + } + /** * Get the review summary relationship. */ diff --git a/app/Models/ResourceEdits.php b/app/Models/ResourceEdits.php index b3dc4736..7e502a0d 100644 --- a/app/Models/ResourceEdits.php +++ b/app/Models/ResourceEdits.php @@ -6,6 +6,7 @@ use App\Services\ResourceEditsService; use App\Traits\HasComments; use App\Traits\HasVotes; +use App\Utilities\UrlUtilities; use Cviebrock\EloquentSluggable\Sluggable; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -89,6 +90,13 @@ protected function proposedChanges(): Attribute return $changes; }, + set: function ($value) { + if (array_key_exists('page_url', $value) && is_string($value['page_url'])) { + $value['page_url'] = UrlUtilities::normalize($value['page_url']); + } + + return json_encode($value); + } ); } diff --git a/app/Services/ComputerScienceResourceService.php b/app/Services/ComputerScienceResourceService.php index d6ea772b..af3b2250 100644 --- a/app/Services/ComputerScienceResourceService.php +++ b/app/Services/ComputerScienceResourceService.php @@ -8,6 +8,7 @@ use App\Models\ResourceEdits; use App\Models\ResourceReview; use App\Services\SortingManagers\ResourceSortingManager; +use App\Utilities\UrlUtilities; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -157,6 +158,9 @@ function () use ($computerScienceResource, $sortBy, $request) { */ private function existingConflictingResource(array $data): ?ComputerScienceResource { - return ComputerScienceResource::where('page_url', $data['page_url'])->first(); + return ComputerScienceResource::where( + 'page_url', + UrlUtilities::normalize($data['page_url']), + )->first(); } } diff --git a/app/Services/DataNormalizationService.php b/app/Services/DataNormalizationService.php deleted file mode 100644 index a4f6530d..00000000 --- a/app/Services/DataNormalizationService.php +++ /dev/null @@ -1,30 +0,0 @@ -normalize($array1) === $this->normalize($array2); - } -} diff --git a/app/Services/ResourceEditsService.php b/app/Services/ResourceEditsService.php index bb101044..ade2d6f1 100644 --- a/app/Services/ResourceEditsService.php +++ b/app/Services/ResourceEditsService.php @@ -2,7 +2,9 @@ namespace App\Services; +use App\Models\ComputerScienceResource; use App\Models\ResourceEdits; +use App\Utilities\UrlUtilities; class ResourceEditsService { @@ -42,4 +44,44 @@ public function canMergeEdits(ResourceEdits $edits): bool return $approvals >= $neededApprovals; } + + /** + * Calculate the actual differences between the proposed changes and the original resource. + */ + public function calculateChanges(ComputerScienceResource $resource, array $proposedChanges): array + { + $actualChanges = []; + $normalizedProposed = $this->normalize($proposedChanges); + $normalizedOriginal = $this->normalize($resource->toArray()); + + foreach ($normalizedProposed as $key => $value) { + if (! array_key_exists($key, $normalizedOriginal) || $normalizedOriginal[$key] !== $value) { + // Use the original value from the request, not the normalized one, for file uploads. + $actualChanges[$key] = $proposedChanges[$key]; + } + } + + return $actualChanges; + } + + /** + * Normalize an array by sorting keys and values for consistent comparison, including page_url normalization. + */ + public function normalize(array $array): array + { + ksort($array); + + // Normalize page_url if present + if (array_key_exists('page_url', $array) && is_string($array['page_url'])) { + $array['page_url'] = UrlUtilities::normalize($array['page_url']); + } + + foreach ($array as &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $array; + } } diff --git a/app/Utilities/UrlUtilities.php b/app/Utilities/UrlUtilities.php new file mode 100644 index 00000000..7ac19cbc --- /dev/null +++ b/app/Utilities/UrlUtilities.php @@ -0,0 +1,14 @@ +create(); $this->postJson(route('resources.store'), $formData); - // Try to create the same resource again + // Try to create the same resource again, with a slightly different URL + $formData['page_url'] = $formData['page_url'].' '; $response = $this->postJson(route('resources.store'), $formData); $response->assertStatus(200);