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
2 changes: 1 addition & 1 deletion app/Events/ResourceReviewProcessed.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ResourceReviewProcessed implements ShouldDispatchAfterCommit
* Create a new event instance.
*/
public function __construct(
public int $resource,
public int $resource_id,
public ?array $oldReview,
public ?array $newReview,
) {}
Expand Down
3 changes: 1 addition & 2 deletions app/Filament/Admin/Resources/ComputerScienceResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ public static function table(Table $table): Table
// Custom delete action that uses model delete() method
DeleteAction::make()
->action(function (ModelsComputerScienceResource $record) {
// This calls the model's delete() method, triggering all events
$record->delete();
$record->forceDelete();
}),
])
->bulkActions([
Expand Down
11 changes: 4 additions & 7 deletions app/Http/Controllers/ResourceEditsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,12 @@ public function merge(ResourceEditsService $editsService, ResourceEdits $resourc
}

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

if ($resource->image_path) {
Storage::disk('public')->delete($resource->image_path);
}
$destPath = null;
if (isset($changes['image_path'])) {
// Copy the new file from 'resource-edits' to 'resource' (do not delete the old one)
// Move the new file from 'resource-edits' to 'resource'
$sourcePath = $changes['image_path'];
$fileExtension = pathinfo($sourcePath, PATHINFO_EXTENSION);
$newFileName = Str::random(40).'.'.$fileExtension;
Expand All @@ -139,7 +137,6 @@ public function merge(ResourceEditsService $editsService, ResourceEdits $resourc
// 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;
}
Expand Down
26 changes: 17 additions & 9 deletions app/Listeners/UpdateResourceReviewSummary.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,29 @@ public function __construct()
public function handle(ResourceReviewProcessed $event): void
{
Log::debug('Handling ResourceReviewProcessed', [
'resource_id' => $event->resource,
'resource_id' => $event->resource_id,
'old_review' => $event->oldReview,
'new_review' => $event->newReview,
]);

if ($event->oldReview == null && $event->newReview == null) {
Log::critical('Update Resource Review Summary Listener reached impossible condition', [
'resource_id' => $event->resource,
'resource_id' => $event->resource_id,
'error' => 'Both oldReview and newReview are null',
]);

return;
}

$review_summary = ResourceReviewSummary::firstOrNew(
['computer_science_resource_id' => $event->resource],
);
$reviewSummary = ResourceReviewSummary::where(
'computer_science_resource_id',
$event->resource_id
)->first();

if (! $reviewSummary) {
// The resource review summary is created by the resource observer
return;
}

$fields = [
'community',
Expand All @@ -52,14 +58,16 @@ public function handle(ResourceReviewProcessed $event): void
foreach ($fields as $field) {
$old = $event->oldReview[$field] ?? 0;
$new = $event->newReview[$field] ?? 0;
$review_summary->$field += ($new - $old);
$reviewSummary->$field += ($new - $old);
}

$reviewSummary->review_count = $reviewSummary->review_count ?? 0;
if ($event->oldReview === null) {
// It's a new review, so increase the count
$review_summary->review_count += 1;
$reviewSummary->review_count++;
} elseif ($event->newReview === null) {
$reviewSummary->review_count--;
}

$review_summary->save();
$reviewSummary->save();
}
}
18 changes: 15 additions & 3 deletions app/Models/ComputerScienceResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use ShiftOneLabs\LaravelCascadeDeletes\CascadesDeletes;
use Spatie\Activitylog\LogOptions;
Expand All @@ -35,9 +36,20 @@ class ComputerScienceResource extends Model
use HasVotes;
use LogsActivity;
use Sluggable;

// TODO: ADD A TEST FOR RESOURCE DELETION. DO NOT USE YET IN PRODUCTION
protected $cascadeDeletes = ['votes', 'upvoteSummary', 'comments', 'commentsCountRelationship', 'edits', 'reviewSummary', 'reviews', ''];
use SoftDeletes;

protected $cascadeDeletes = [
// Votes
'votes',
'upvoteSummary',
// Comments
'comments',
'commentsCountRelationship',
'edits',
// Reviews
'reviews',
'reviewSummary',
];

protected $table = 'computer_science_resources';

Expand Down
31 changes: 18 additions & 13 deletions app/Observers/ComputerScienceResourceObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace App\Observers;

use App\Events\TagFrequencyChanged;
use App\Models\ComputerScienceResource;
use App\Models\ResourceReviewSummary;
use Illuminate\Support\Facades\Storage;

class ComputerScienceResourceObserver
Expand All @@ -13,7 +13,9 @@ class ComputerScienceResourceObserver
*/
public function created(ComputerScienceResource $computerScienceResource): void
{
// TagFrequencyChanged is in store ComputerScienceResource controller
ResourceReviewSummary::create(
['computer_science_resource_id' => $computerScienceResource->id],
);
}

/**
Expand All @@ -27,13 +29,7 @@ public function updated(ComputerScienceResource $computerScienceResource): void
/**
* Handle the ComputerScienceResource "deleted" event.
*/
public function deleted(ComputerScienceResource $computerScienceResource): void
{
// Delete the image
if ($computerScienceResource->image_path) {
Storage::disk('public')->delete($computerScienceResource->image_path);
}
}
public function deleted(ComputerScienceResource $computerScienceResource): void {}

/**
* Handle the ComputerScienceResource "restored" event.
Expand All @@ -43,11 +39,20 @@ public function restored(ComputerScienceResource $computerScienceResource): void
//
}

public function deleting(ComputerScienceResource $computerScienceResource): void
{
$computerScienceResource->topic_tags = [];
$computerScienceResource->programming_language_tags = [];
$computerScienceResource->general_tags = [];

// Delete the image
if ($computerScienceResource->image_path) {
Storage::disk('public')->delete($computerScienceResource->image_path);
}
}

/**
* Handle the ComputerScienceResource "force deleted" event.
*/
public function forceDeleted(ComputerScienceResource $computerScienceResource): void
{
//
}
public function forceDeleted(ComputerScienceResource $computerScienceResource): void {}
}
4 changes: 2 additions & 2 deletions app/Observers/ResourceReviewObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public function updated(ResourceReview $resourceReview): void
}

/**
* Handle the ResourceReview "deleted" event.
* Handle the ResourceReview "deleting" event.
*/
public function deleted(ResourceReview $resourceReview): void
public function deleting(ResourceReview $resourceReview): void
{
ResourceReviewProcessed::dispatch(
$resourceReview->computer_science_resource_id,
Expand Down
6 changes: 1 addition & 5 deletions app/Services/ComputerScienceResourceService.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,6 @@ function () use ($computerScienceResource, $sortBy, $request) {
*/
private function existingConflictingResource(array $data): ?ComputerScienceResource
{
// If there is a resource with these matching properties, it is safe to say
// it is the same resource
return ComputerScienceResource::where('name', $data['name'])
->where('page_url', $data['page_url'])
->first();
return ComputerScienceResource::where('page_url', $data['page_url'])->first();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('computer_science_resources', function (Blueprint $table) {
$table->softDeletes();
$table->index('page_url');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('computer_science_resources', function (Blueprint $table) {
$table->dropSoftDeletes();
$table->dropIndex(['page_url']);
});
}
};
71 changes: 36 additions & 35 deletions resources/js/Components/Resources/Reviews/CreateResourceReview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import ListInput from "@/Components/ListInput.vue";
import Button from "primevue/button";
import FormSaverChip from "@/Components/Form/FormSaverChip.vue";

import { yupResolver } from "@primevue/forms/resolvers/yup";
import { resourceReviewFields } from "@/Helpers/validation";
import { router } from "@inertiajs/vue3";
import axios from "axios";
Expand Down Expand Up @@ -71,44 +70,47 @@ const {
clearLocalStorage,
} = useLocalStorageSaver(form, props.resourceId, formFields, "review-draft");

const resolver = ref(yupResolver(resourceReviewFields));
const isSubmitting = ref(false);
const error = ref(null);

const submitReview = async (event) => {
if (!event.valid) {
console.error("Validation errors");
return;
}
const errors = ref({});

const submitReview = async () => {
isSubmitting.value = true;

const url = props.isEditingMode
? route("reviews.update", { computerScienceResource: props.resourceId })
: route("reviews.store", { computerScienceResource: props.resourceId });

const method = props.isEditingMode ? "put" : "post";

axios[method](url, form)
.then(() => {
// Clear localStorage on successful submission
clearLocalStorage();

const routeParams = { slug: props.resourceSlug };
if (props.isEditingMode) {
routeParams.tab = "reviews";
routeParams.sort_by = "recently_updated";
} else {
routeParams.sort_by = "latest";
}
router.visit(route('resources.show', routeParams));
})
.catch((err) => {
isSubmitting.value = false;
error.value =
"Something went wrong with submitting your review, please refresh or try again.";
console.error(err);
});
try {
await resourceReviewFields.validate(form, { abortEarly: false });
errors.value = {};

const url = props.isEditingMode
? route("reviews.update", { computerScienceResource: props.resourceId })
: route("reviews.store", { computerScienceResource: props.resourceId });

const method = props.isEditingMode ? "put" : "post";

await axios[method](url, form);
clearLocalStorage();

const routeParams = { slug: props.resourceSlug };
if (props.isEditingMode) {
routeParams.tab = "reviews";
routeParams.sort_by = "recently_updated";
} else {
routeParams.sort_by = "latest";
}
router.visit(route('resources.show', routeParams));
} catch (e) {
isSubmitting.value = false;
if (e.inner) {
const yupErrors = {};
e.inner.forEach((error) => {
yupErrors[error.path] = error.errors;
});
errors.value = yupErrors;
} else {
error.value = "Something went wrong with submitting your review, please refresh or try again.";
console.error(e);
}
}
};
</script>

Expand All @@ -126,7 +128,6 @@ const submitReview = async (event) => {
</h2>

<Form
:resolver="resolver"
:initialValues="form"
@submit="submitReview"
class="flex flex-col gap-6"
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/CommentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ public function test_upvote_summaries_created_for_all_comments()
/**
* Test that upvote summaries and upvotes are cleaned up when comments are deleted.
*/
public function test_upvote_summaries_and_upvotes_cleaned_up_when_comments_deleted()
public function test_cleaned_up_when_deleted()
{
$this->actingAs(User::factory()->create());

Expand Down
Loading