Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/Http/Controllers/CommentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use App\Http\Resources\CommentResource;
use App\Http\Resources\UserResource;
use App\Services\CommentService;
use App\Services\ModelResolverService;
use App\Services\UpvoteService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
Expand All @@ -17,8 +17,9 @@

class CommentController extends Controller
{
public function __construct(protected ModelResolverService $modelResolver,
public function __construct(
protected CommentService $commentService,
protected UpvoteService $upvoteService,
) {}

/**
Expand All @@ -42,6 +43,8 @@ public function store(StoreCommentRequest $request)
'depth' => $comment->depth,
]);

$this->upvoteService->upvote('comment', $comment->id);

DB::commit();

return response()->json([
Expand Down
24 changes: 22 additions & 2 deletions app/Http/Controllers/ComputerScienceResourceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
use App\Models\ComputerScienceResource;
use App\Services\ComputerScienceResourceFilter;
use App\Services\ComputerScienceResourceService;
use App\Services\UpvoteService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Throwable;
Expand All @@ -18,6 +20,7 @@ class ComputerScienceResourceController extends Controller
{
public function __construct(
protected ComputerScienceResourceService $resourceService,
protected UpvoteService $upvoteService,
protected ComputerScienceResourceFilter $filterService
) {}

Expand Down Expand Up @@ -63,7 +66,21 @@ public function store(StoreResourceRequest $request)
{
$validatedData = $request->validated();
try {
DB::beginTransaction();
$resource = $this->resourceService->createResource($validatedData);

Log::info('Resource created', [
'resource_id' => $resource->id,
'user_id' => Auth::id(),
'name' => $resource->name,
'slug' => $resource->slug,
'platforms' => $resource->platforms,
]);

$this->upvoteService->upvote('resource', $resource->id);
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upvote call is made outside of the database transaction. If the upvote operation fails, the resource will still be created but without its automatic upvote. Consider moving the upvote call inside the transaction before the commit, or handle the failure scenario appropriately.

Copilot uses AI. Check for mistakes.

DB::commit();

session()->flash('success', 'Created Resource!');

return response()->json($resource);
Expand All @@ -77,10 +94,13 @@ public function store(StoreResourceRequest $request)

return response()->json($e->resource);
} catch (Throwable $e) {
Log::error('Error creating resource', [
'user_id' => Auth::id(),
DB::rollBack();

Log::critical('Failed to create resource', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id(),
'data' => $validatedData,
]);

return response()->json([], 500);
Expand Down
138 changes: 61 additions & 77 deletions app/Http/Controllers/ResourceEditsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
use App\Models\ComputerScienceResource;
use App\Models\ResourceEdits;
use App\Services\ResourceEditsService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;
use Str;
use Throwable;

class ResourceEditsController extends Controller
Expand Down Expand Up @@ -39,36 +37,61 @@ public function create(string $slug)
public function store(ComputerScienceResource $computerScienceResource, StoreResourceEditRequest $request)
{
$validatedData = $request->validated();
$proposedChanges = $validatedData['proposed_changes'] ?? [];

$actualChanges = $this->resourceEditsService->calculateChanges($computerScienceResource, $proposedChanges);

// Add image path to the actual changes
if (array_key_exists('image_file', $proposedChanges)) {
$actualChanges['image_path'] = null;
if (isset($proposedChanges['image_file'])) {
$path = $proposedChanges['image_file']->store('resource-edits', 'public');
$actualChanges['image_path'] = $path;
}
unset($actualChanges['image_file']);
}

if (empty($actualChanges)) {
Log::warning("Resource edit was submitted without any changes for resource ID: {$computerScienceResource->id}");
try {
$resourceEdit = $this->resourceEditsService->createResourceEdit($computerScienceResource, $validatedData);

Log::info('Resource edit created', [
'resource_edit_id' => $resourceEdit->id,
'resource_id' => $computerScienceResource->id,
'user_id' => Auth::id(),
'edit_title' => $resourceEdit->edit_title,
]);

return redirect()->route('resource_edits.show', ['slug' => $resourceEdit->slug])
->with('success', 'Edits Created!');
} catch (\InvalidArgumentException $e) {
Log::warning('Resource edit submitted with no changes', [
'resource_id' => $computerScienceResource->id,
'user_id' => Auth::id(),
'error' => $e->getMessage(),
]);

return redirect()->back()->with('warning', 'Cannot submit an edit with no changes made.');
} catch (Throwable $e) {
Log::critical('Failed to create resource edit', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'resource_id' => $computerScienceResource->id,
'user_id' => Auth::id(),
'data' => $validatedData,
]);

return redirect()->back()->withErrors(['error' => 'Failed to create resource edit. Please try again.']);
}
}

$resourceEdit = ResourceEdits::create([
'user_id' => Auth::id(),
'computer_science_resource_id' => $computerScienceResource->id,
'edit_title' => $validatedData['edit_title'],
'edit_description' => $validatedData['edit_description'],
'proposed_changes' => $actualChanges,
]);
public function index(Request $request)
{
try {
$data = $this->resourceEditsService->getIndexData($request);

return redirect()->route('resource_edits.show', ['slug' => $resourceEdit->slug])
->with('success', 'Edits Created!');
return Inertia::render('ResourceEdits/Index', $data);
} catch (Throwable $e) {
Log::error('Error loading resource edits index', [
'user_id' => Auth::id(),
'query' => $request->query(),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);

// Return an empty page with an error flash so the UI can show a message
session()->flash('error', 'Unable to load resource edits right now.');

return Inertia::render('ResourceEdits/Index', [
'resource_edits' => ResourceEdits::query()->paginate(1),
]);
}
}

public function show(string $slug)
Expand All @@ -83,57 +106,10 @@ public function show(string $slug)
]);
}

public function merge(ResourceEditsService $editsService, ResourceEdits $resourceEdits)
public function merge(ResourceEdits $resourceEdits)
{
if (! $editsService->canMergeEdits($resourceEdits)) {
return redirect()->back()->with('warning', 'Not enough approvals');
}

DB::beginTransaction();
try {
$resource = ComputerScienceResource::findOrFail($resourceEdits->computer_science_resource_id);

// Go through each property in proposed_changes, and if it exists. then set the value
$changes = $resourceEdits->proposed_changes;
$proposedFields = ['name', 'description', 'page_url', 'platforms', 'difficulties', 'pricing'];
foreach ($proposedFields as $field) {
if (array_key_exists($field, $changes)) {
$resource->$field = $changes[$field];
}
}

if (array_key_exists('image_path', $changes)) {
if ($resource->image_path) {
Storage::disk('public')->delete($resource->image_path);
}
$destPath = null;
if (isset($changes['image_path'])) {
// Move the new file from 'resource-edits' to 'resource'
$sourcePath = $changes['image_path'];
$fileExtension = pathinfo($sourcePath, PATHINFO_EXTENSION);
$newFileName = Str::random(40).'.'.$fileExtension;
$destPath = 'resource/'.$newFileName;

// TODO: FIGURE OUT WHAT TO DO IN CASE OF EXCEPTION IN CODE FROM LATER STEPS
Storage::disk('public')->move($sourcePath, $destPath);
}
// Update image_path in DB
$resource->image_path = $destPath;
}

$resource->save();

$proposedTagFields = ['topics_tags', 'programming_languages_tags', 'general_tags'];
foreach ($proposedTagFields as $field) {
if (array_key_exists($field, $changes)) {
$resource->$field = $changes[$field];
}
}

// Delete the edit since we successfully merged the changes
$resourceEdits->delete();

DB::commit();
$resource = $this->resourceEditsService->mergeResourceEdit($resourceEdits);

Log::info('Resource edit merged', [
'resource_id' => $resource->id,
Expand All @@ -145,12 +121,20 @@ public function merge(ResourceEditsService $editsService, ResourceEdits $resourc

return redirect(route('resources.show', ['slug' => $resource->slug]))
->with('success', 'Successfully Merged Changes!');
} catch (\LogicException $e) {
Log::warning('Insufficient approvals for resource edit merge', [
'resource_edit_id' => $resourceEdits->id,
'user_id' => Auth::id(),
'error' => $e->getMessage(),
]);

return redirect()->back()->with('warning', 'Not enough approvals');
} catch (Throwable $e) {
DB::rollBack();
Log::critical('Failed to merge resource edits', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'resource_edit_id' => $resourceEdits->id,
'user_id' => Auth::id(),
]);

return redirect()->back()->withErrors(['error' => 'Failed to merge resource edits. Please try again.']);
Expand Down
7 changes: 7 additions & 0 deletions app/Http/Controllers/ResourceReviewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
use App\Http\Requests\StoreResourceReviewRequest;
use App\Models\ComputerScienceResource;
use App\Models\ResourceReview;
use App\Services\UpvoteService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class ResourceReviewController extends Controller
{
public function __construct(
protected UpvoteService $upvoteService,
) {}

// Store the review on the resource
public function store(StoreResourceReviewRequest $request, ComputerScienceResource $computerScienceResource)
{
Expand Down Expand Up @@ -58,6 +63,8 @@ public function store(StoreResourceReviewRequest $request, ComputerScienceResour
'review_id' => $review->id,
]);

$this->upvoteService->upvote('review', $review->id);
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upvote call is made outside of any database transaction. If the upvote operation fails, the review will still be created but without its automatic upvote. Consider wrapping both operations in a transaction or handling the failure scenario appropriately.

Copilot uses AI. Check for mistakes.

return response()->json($review);
}

Expand Down
9 changes: 0 additions & 9 deletions app/Http/Requests/StoreCommentRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,12 @@

namespace App\Http\Requests;

use App\Services\ModelResolverService;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

class StoreCommentRequest extends FormRequest
{
protected $modelResolver;

public function __construct(ModelResolverService $modelResolver)
{
parent::__construct();
$this->modelResolver = $modelResolver;
}

/**
* Determine if the user is authorized to make this request.
*/
Expand Down
10 changes: 9 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Providers;

use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
Expand All @@ -22,6 +23,13 @@ public function register(): void
*/
public function boot(): void
{
//
Relation::enforceMorphMap([
'resource' => \App\Models\ComputerScienceResource::class,
'review' => \App\Models\ResourceReview::class,
'comment' => \App\Models\Comment::class,
'edit' => \App\Models\ResourceEdits::class,
'user' => \App\Models\User::class,
// Add other model types here
]);
}
}
27 changes: 0 additions & 27 deletions app/Providers/ModelResolverServiceProvider.php

This file was deleted.

Loading