feat: implement user interaction system with like functionality
- Updated interaction models and relations for handling of user-deal interactions. - Added frontend interactivity with `interaction.js` for toggling like buttons.
This commit is contained in:
parent
a82e1b6b06
commit
16c9ff3cee
@ -10,4 +10,5 @@ enum InteractionType: string
|
|||||||
|
|
||||||
case Like = 'like';
|
case Like = 'like';
|
||||||
case Favorite = 'favorite';
|
case Favorite = 'favorite';
|
||||||
|
case Redirection = 'redirect';
|
||||||
}
|
}
|
||||||
|
|||||||
47
app/Http/Controllers/InteractionController.php
Normal file
47
app/Http/Controllers/InteractionController.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Enums\InteractionType;
|
||||||
|
use App\Models\Deal;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class InteractionController extends Controller
|
||||||
|
{
|
||||||
|
public function like(Deal $deal)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Check for existing like of the user with deal
|
||||||
|
$user = Auth::user();
|
||||||
|
$existingLike = $deal->interactions()
|
||||||
|
->where('user_id', $user->id)
|
||||||
|
->where('type', InteractionType::Like)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
// Delete the existing like if exists
|
||||||
|
if ($existingLike) {
|
||||||
|
$existingLike->delete();
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Successfully unliked the post.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, create a new like
|
||||||
|
$data = [
|
||||||
|
'type' => InteractionType::Like,
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
];
|
||||||
|
|
||||||
|
Deal::unguard();
|
||||||
|
$deal->interactions()->create($data);
|
||||||
|
Deal::reguard();
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Error when liked a deal: id'.$deal->id.$e->getMessage(), $e->getTrace());
|
||||||
|
|
||||||
|
return response()->json(['error' => 'Something went wrong.'], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Successfully liked the post.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,7 +20,6 @@ public function create()
|
|||||||
public function store(StoreRegisterdUser $request)
|
public function store(StoreRegisterdUser $request)
|
||||||
{
|
{
|
||||||
$data = $request->validated();
|
$data = $request->validated();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($data) {
|
DB::transaction(function () use ($data) {
|
||||||
if ($data['role'] === UserTypes::Broker->value) {
|
if ($data['role'] === UserTypes::Broker->value) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
class Deal extends Model
|
class Deal extends Model
|
||||||
{
|
{
|
||||||
@ -18,8 +18,8 @@ public function category(): BelongsTo
|
|||||||
return $this->belongsTo(DealCategory::class, 'deal_category_id');
|
return $this->belongsTo(DealCategory::class, 'deal_category_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function interactions(): MorphMany
|
public function interactions(): HasMany
|
||||||
{
|
{
|
||||||
return $this->morphMany(Interaction::class, 'interactable');
|
return $this->hasMany(Interaction::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,13 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\InteractionType;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
class Interaction extends Model
|
class Interaction extends Model
|
||||||
{
|
{
|
||||||
public function interactable(): MorphTo
|
public function user(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->belongsTo(Deal::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deal(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Deal::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => InteractionType::class,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,4 +68,9 @@ public function type(): MorphTo
|
|||||||
{
|
{
|
||||||
return $this->morphTo('role');
|
return $this->morphTo('role');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function interactions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(User::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,8 @@ public function up(): void
|
|||||||
$table->foreignIdFor(User::class);
|
$table->foreignIdFor(User::class);
|
||||||
$table->foreignIdFor(Deal::class);
|
$table->foreignIdFor(Deal::class);
|
||||||
$table->enum('type', InteractionType::values());
|
$table->enum('type', InteractionType::values());
|
||||||
$table->timestamp('created_at')->useCurrent();
|
$table->unsignedBigInteger('count')->default(1);
|
||||||
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
resources/js/interaction.js
Normal file
14
resources/js/interaction.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
async function like(e, id){
|
||||||
|
try {
|
||||||
|
let response = await axios.post('/like/' + id);
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
let likeBtns = e.querySelectorAll('.like');
|
||||||
|
likeBtns.forEach((e) => e.classList.toggle('hidden'))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.like = like;
|
||||||
@ -1,6 +1,8 @@
|
|||||||
|
@props(['id'])
|
||||||
<div class="">
|
<div class="">
|
||||||
<x-ui.button-sm class="text-accent-600">
|
<x-ui.button-sm class="text-accent-600" onclick="like(this, {{$id}})">
|
||||||
<x-heroicon-o-heart class="w-4"/>
|
<x-heroicon-o-heart class="like w-4"/>
|
||||||
|
<x-heroicon-s-heart class="like w-4 hidden text-red-500"/>
|
||||||
</x-ui.button-sm>
|
</x-ui.button-sm>
|
||||||
|
|
||||||
<x-ui.button-sm class="text-accent-600">
|
<x-ui.button-sm class="text-accent-600">
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<x-ui.button-sm variant="neutral">
|
<x-ui.button-sm variant="neutral">
|
||||||
{{$deal->category->name}}
|
{{$deal->category->name}}
|
||||||
</x-ui.button-sm>
|
</x-ui.button-sm>
|
||||||
<x-dashboard.user.action-toolbar />
|
<x-dashboard.user.action-toolbar :id="$deal->id" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="font-bold text-lg ">{{$deal->title}}</p>
|
<p class="font-bold text-lg ">{{$deal->title}}</p>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
@props(['deals' => []])
|
@props(['deals' => []])
|
||||||
<div class="grid md:grid-cols-2 gap-6">
|
<div class="grid md:grid-cols-2 gap-6">
|
||||||
@forelse($deals as $deal)
|
@forelse($deals as $deal)
|
||||||
@ds($deal)
|
|
||||||
<x-dashboard.user.listing-card :deal="$deal" :broker="$deal->broker"/>
|
<x-dashboard.user.listing-card :deal="$deal" :broker="$deal->broker"/>
|
||||||
@empty
|
@empty
|
||||||
<p class="col-span-2 text-sm text-center text-accent-600">No Deals found till now !</p>
|
<p class="col-span-2 text-sm text-center text-accent-600">No Deals found till now !</p>
|
||||||
|
|||||||
@ -88,5 +88,5 @@
|
|||||||
|
|
||||||
<x-dashboard.user.listing :deals="$deals"/>
|
<x-dashboard.user.listing :deals="$deals"/>
|
||||||
</section>
|
</section>
|
||||||
@vite('resources/js/menu.js')
|
@vite(['resources/js/menu.js', 'resources/js/interaction.js'])
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
use App\Http\Controllers\BrokerDealController;
|
use App\Http\Controllers\BrokerDealController;
|
||||||
use App\Http\Controllers\ExplorePageController;
|
use App\Http\Controllers\ExplorePageController;
|
||||||
use App\Http\Controllers\HomeController;
|
use App\Http\Controllers\HomeController;
|
||||||
|
use App\Http\Controllers\InteractionController;
|
||||||
use App\Http\Controllers\RegisteredUserController;
|
use App\Http\Controllers\RegisteredUserController;
|
||||||
use App\Http\Middleware\HasRole;
|
use App\Http\Middleware\HasRole;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
@ -14,7 +15,9 @@
|
|||||||
Route::get('/', HomeController::class)->name('home');
|
Route::get('/', HomeController::class)->name('home');
|
||||||
|
|
||||||
Route::middleware('guest')->group(function () {
|
Route::middleware('guest')->group(function () {
|
||||||
Route::resource('/login', AuthenticatedUserController::class)->only(['create', 'store']);
|
Route::resource('/login', AuthenticatedUserController::class)
|
||||||
|
->middleware('throttle:6,1')
|
||||||
|
->only(['create', 'store']);
|
||||||
Route::resource('/register', RegisteredUserController::class)->only(['create', 'store']);
|
Route::resource('/register', RegisteredUserController::class)->only(['create', 'store']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -37,4 +40,8 @@
|
|||||||
|
|
||||||
Route::resource('profile', BrokerProfileController::class)->except('index', 'store', 'create');
|
Route::resource('profile', BrokerProfileController::class)->except('index', 'store', 'create');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::post('/like/{deal}', [InteractionController::class, 'like'])
|
||||||
|
->middleware('throttle:30,1')
|
||||||
|
->name('like');
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user