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
16 changes: 16 additions & 0 deletions app/Exceptions/Resources/ResourceAlreadyCreatedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Exceptions\Resources;

use Exception;

class ResourceAlreadyCreatedException extends Exception
{
public $resource;

public function __construct($resource)
{
parent::__construct('Resource already exists');
$this->resource = $resource;
}
}
7 changes: 7 additions & 0 deletions app/Exceptions/Resources/ResourceInvalidTabException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace App\Exceptions\Resources;

use Exception;

class ResourceInvalidTabException extends Exception {}
4 changes: 2 additions & 2 deletions app/Http/Controllers/CommentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public function store(StoreCommentRequest $request)
} catch (Throwable $e) {
DB::rollBack();
Log::critical('Failed to save comment', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'validated_data' => $validatedData,
'user_id' => Auth::id(),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);

return response()->json(['message' => 'Failed to save comment'], 500);
Expand Down
149 changes: 27 additions & 122 deletions app/Http/Controllers/ComputerScienceResourceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,23 @@

namespace App\Http\Controllers;

use App\Exceptions\Resources\ResourceAlreadyCreatedException;
use App\Exceptions\Resources\ResourceInvalidTabException;
use App\Http\Requests\StoreResourceRequest;
use App\Models\ComputerScienceResource;
use App\Models\NewsPost;
use App\Models\ResourceEdits;
use App\Models\ResourceReview;
use App\Services\CommentService;
use App\Services\ComputerScienceResourceFilter;
use App\Services\ResourceReviewService;
use App\Services\SortingManagers\GeneralVotesSortingManager;
use App\Services\SortingManagers\ResourceSortingManager;
use App\Services\UpvoteService;
use App\Services\ComputerScienceResourceService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Throwable;

class ComputerScienceResourceController extends Controller
{
public function __construct(
protected CommentService $commentService,
protected GeneralVotesSortingManager $generalVotesSortingManager,
protected ResourceReviewService $reviewService,
protected ResourceSortingManager $resourceSortingManager,
protected UpvoteService $upvoteService,
protected ComputerScienceResourceService $resourceService,
) {}

/**
Expand Down Expand Up @@ -66,72 +57,25 @@ public function create()
public function store(StoreResourceRequest $request)
{
$validatedData = $request->validated();

DB::beginTransaction();
try {
// Store the image onto storage
$path = null;
if (array_key_exists('image_file', $validatedData) && $imageFile = $validatedData['image_file']) {
$path = $imageFile->store('resource', 'public');
if (! $path) {
Log::error('Failed to store image file', [
'user_id' => Auth::id(),
'file_info' => $imageFile,
]);

$fileName = $imageFile->getClientOriginalName();
throw new \RuntimeException(
"Could not save the image file '{$fileName}' for user ID ".Auth::id().'.'
);
}
}

$resource = ComputerScienceResource::create([
'user_id' => Auth::id(),
'name' => $validatedData['name'],
'image_path' => $path,
'description' => $validatedData['description'],
'page_url' => $validatedData['page_url'],
'platforms' => $validatedData['platforms'],
'difficulty' => $validatedData['difficulty'],
'pricing' => $validatedData['pricing'],
]);

// Add topics as tags
$resource->topic_tags = $validatedData['topic_tags'];

// Add programming languages as tags (if provided)
if (isset($validatedData['programming_language_tags'])) {
$resource->programming_language_tags = $validatedData['programming_language_tags'];
}

// Add general tags (if provided)
if (isset($validatedData['general_tags'])) {
$resource->general_tags = $validatedData['general_tags'];
}

DB::commit();

$this->upvoteService->upvote('resource', $resource->id);
$resource = $this->resourceService->createResource($validatedData);
session()->flash('success', 'Created Resource!');

Log::info('Resource created', [
'resource_id' => $resource->id,
return response()->json($resource);
} catch (ResourceAlreadyCreatedException $e) {
Log::warning('Resource already exists', [
'user_id' => Auth::id(),
'name' => $resource->name,
'slug' => $resource->slug,
'platforms' => $resource->platforms,
'resource_id' => $e->resource->id ?? null,
'name' => $e->resource->name ?? null,
]);
session()->flash('warning', 'Resource Already Exists!');

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

return response()->json($resource);
return response()->json($e->resource);
} catch (Throwable $e) {
DB::rollBack();
Log::critical('Failed to create resource', [
Log::error('Error creating resource', [
'user_id' => Auth::id(),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id(),
'data' => $validatedData,
]);

return response()->json([], 500);
Expand All @@ -143,62 +87,23 @@ public function store(StoreResourceRequest $request)
*/
public function show(Request $request, string $slug, string $tab = 'reviews')
{
$computerScienceResource = ComputerScienceResource::where('slug', $slug)->firstOrFail();
// Get the review summaries
$computerScienceResource->load('reviewSummary');
$computerScienceResource->load('user');
try {
$result = $this->resourceService->getShowResourceData($request, $slug, $tab);

$validTabs = ['reviews', 'discussion', 'edits'];
return Inertia::render('Resources/Show', $result);
} catch (ResourceInvalidTabException $e) {
Log::warning('Invalid resource tab requested', [
'user_id' => Auth::id(),
'slug' => $slug,
'requested_tab' => $tab,
'error' => $e->getMessage(),
]);

if (! in_array($tab, $validTabs)) {
// Redirect to default if invalid
return redirect()->route('resources.show', [
'slug' => $computerScienceResource->slug,
'slug' => $slug,
'tab' => 'reviews',
]);
])->with('warning', 'Invalid tab requested, redirected to reviews.');
}

// return the resource and tab
$data = [
'tab' => $tab,
'resource' => $computerScienceResource,
];

$sortBy = $request->query('sort_by', 'top');
// Load only the necessary tab data
if ($tab === 'reviews') {
$userReview = null;
if ($userId = Auth::id()) {
$userReview = ResourceReview::whereBelongsTo($computerScienceResource)
->firstWhere('user_id', $userId);
}

$data['userReview'] = $userReview;

$data['reviews'] = Inertia::defer(
function () use ($computerScienceResource, $sortBy, $request) {
$query = ResourceReview::whereBelongsTo($computerScienceResource);
$query = $this->generalVotesSortingManager->applySort($query, $sortBy, ResourceReview::class);

return $query->with('user')->paginate(10)->appends($request->query());
}
);
} elseif ($tab === 'edits') {
$data['resourceEdits'] = Inertia::defer(
function () use ($computerScienceResource, $sortBy, $request) {
$query = ResourceEdits::whereBelongsTo($computerScienceResource);
$query = $this->generalVotesSortingManager->applySort($query, $sortBy, ResourceEdits::class);

return $query->with('user')->paginate(10)->appends($request->query());
}
);
} elseif ($tab === 'discussion') {
$data['discussion'] = Inertia::defer(
fn () => $this->commentService->getPaginatedComments('resource', $computerScienceResource->id, 0, 150, $sortBy)
);
}

return Inertia::render('Resources/Show', $data);
}

/**
Expand Down
12 changes: 8 additions & 4 deletions app/Http/Controllers/ResourceEditsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function store(ComputerScienceResource $computerScienceResource, StoreRes

$actualChanges = $this->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'])) {
Expand Down Expand Up @@ -122,7 +123,11 @@ public function merge(ResourceEditsService $editsService, ResourceEdits $resourc
}

if (array_key_exists('image_path', $changes)) {
// TODO: Removed code to delete photo, will be handled in a cron job
// TODO: Remove the old photo resource photo, will be handled in a cron job,
//
// photo image_url history can be viewed via activity log.
//

$destPath = null;
if (isset($changes['image_path'])) {
// Copy the new file from 'resource-edits' to 'resource' (do not delete the old one)
Expand All @@ -131,7 +136,8 @@ public function merge(ResourceEditsService $editsService, ResourceEdits $resourc
$newFileName = Str::random(40).'.'.$fileExtension;
$destPath = 'resource/'.$newFileName;

Storage::disk('public')->copy($sourcePath, $destPath);
// 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
Expand All @@ -141,11 +147,9 @@ public function merge(ResourceEditsService $editsService, ResourceEdits $resourc
$resource->save();

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

Expand Down
1 change: 1 addition & 0 deletions app/Http/Controllers/UpvoteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function downvote($typeKey, $id)
{
$result = $this->upvoteService->downvote($typeKey, $id);

// TODO: REFACTOR TO EXCEPTIONS
if (isset($result['error'])) {
return response()->json(['message' => $result['error']], $result['status']);
}
Expand Down
Loading