25
common/Votes/OrdersByWeightedScore.php
Executable file
25
common/Votes/OrdersByWeightedScore.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Votes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
trait OrdersByWeightedScore
|
||||
{
|
||||
public function scopeOrderByWeightedScore(
|
||||
Builder $query,
|
||||
$direction = 'desc',
|
||||
string $positiveCol = 'upvotes',
|
||||
string $negativeCol = 'downvotes',
|
||||
): Builder {
|
||||
return $query
|
||||
->select($query->getQuery()->columns ?? '*')
|
||||
->addSelect([
|
||||
DB::raw(
|
||||
"(($positiveCol + 1.9208) / ($positiveCol + $negativeCol) -.96 * SQRT(($positiveCol * $negativeCol) / ($positiveCol + $negativeCol) + 0.9604) / ($positiveCol + $negativeCol)) / (1 + 3.8416 / ($positiveCol + $negativeCol)) AS weighted_score",
|
||||
),
|
||||
])
|
||||
->orderBy('weighted_score', $direction);
|
||||
}
|
||||
}
|
||||
66
common/Votes/StoreVote.php
Executable file
66
common/Votes/StoreVote.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Votes;
|
||||
|
||||
use Auth;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class StoreVote
|
||||
{
|
||||
public function execute($model, string $newVoteType, ?string $userIp)
|
||||
{
|
||||
$userId = Auth::id();
|
||||
|
||||
// if we can't match current user, bail
|
||||
if (!$userId && !$userIp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentVote = $model
|
||||
->votes()
|
||||
->where(
|
||||
fn(Builder $query) => $query
|
||||
->where('user_id', $userId)
|
||||
->orWhere('user_ip', $userIp),
|
||||
)
|
||||
->first();
|
||||
|
||||
// remove old vote from this user
|
||||
if ($currentVote) {
|
||||
$this->deleteCurrentVote($model, $currentVote);
|
||||
}
|
||||
|
||||
// create a new rating
|
||||
if (!$currentVote || $currentVote->vote_type !== $newVoteType) {
|
||||
$this->storeVote($model, $newVoteType, $userId, $userIp);
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
private function deleteCurrentVote($model, Vote $vote): void
|
||||
{
|
||||
$column = Str::plural($vote->vote_type);
|
||||
if ($model->$column > 0) {
|
||||
$model->decrement($column);
|
||||
}
|
||||
$vote->delete();
|
||||
}
|
||||
|
||||
private function storeVote(
|
||||
$model,
|
||||
string $voteType,
|
||||
?int $userId,
|
||||
?string $userIp,
|
||||
): void
|
||||
{
|
||||
$model->votes()->create([
|
||||
'vote_type' => $voteType,
|
||||
'user_id' => $userId,
|
||||
'user_ip' => $userIp,
|
||||
]);
|
||||
|
||||
$model->increment(Str::plural($voteType), 1);
|
||||
}
|
||||
}
|
||||
24
common/Votes/Vote.php
Executable file
24
common/Votes/Vote.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Votes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Vote extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public function scopeWithCurrentUserVotes(Builder $query): Builder
|
||||
{
|
||||
return $query
|
||||
->where('user_id', auth()->id())
|
||||
->orWhere('user_ip', getIp());
|
||||
}
|
||||
}
|
||||
37
common/Votes/VoteController.php
Executable file
37
common/Votes/VoteController.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Votes;
|
||||
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class VoteController extends BaseController
|
||||
{
|
||||
public function store()
|
||||
{
|
||||
$data = $this->validate(request(), [
|
||||
'vote_type' => 'required|in:upvote,downvote',
|
||||
'model_type' => 'required|string',
|
||||
'model_id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$namespace = modelTypeToNamespace($data['model_type']);
|
||||
$model = app($namespace)::findOrFail($data['model_id']);
|
||||
|
||||
$this->authorize('vote', $model);
|
||||
|
||||
$model = app(StoreVote::class)->execute(
|
||||
$model,
|
||||
$data['vote_type'],
|
||||
getIp(),
|
||||
);
|
||||
|
||||
$model->unsetRelation('votes');
|
||||
$model->load([
|
||||
'votes' => fn($builder) => $builder->withCurrentUserVotes(),
|
||||
]);
|
||||
$model->current_vote = $model->votes->first()?->vote_type;
|
||||
$model->unsetRelation('votes');
|
||||
|
||||
return $this->success(['model' => $model]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user