Skip to content
Open
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
44 changes: 18 additions & 26 deletions resources/views/reviews.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,25 @@
<div class="text-sm text mt-1 text-center font-normal">@lang('Rating')</div>
</div>
</div>
<div class="min-h-[164px]">
<lazy>
<graphql v-cloak query='@include('rapidez-reviews::queries.reviews', ['sku' => $product->sku])' :variables="{pageSize: 9999, page: 1}" v-slot="{ data, ratings, c_ratings }">
<div
v-if="data"
:set="ratings = data?.products?.items[0]?.reviews?.items ?? []"
class="mt-6 flex flex-col-reverse gap-y-2.5"
>
@for ($i = 1; $i <= 5; $i++)
<div class="flex flex-wrap items-center justify-between" :set="c_ratings = ratings?.filter(e => e.average_rating == {{ $i * 20 }})">
<div class="text-sm text flex items-center gap-x-2.5 font-medium">
<div class="w-2">{{ $i }}</div>
<div class="flex items-center justify-center relative size-[18px] shrink-0" :class="c_ratings?.length ? 'bg-emerald-600' : 'bg-emphasis'">
<x-rapidez::reviews-star />
</div>
</div>
<x-rapidez-reviews::bar class="mx-4 flex-1" score="c_ratings.length / (ratings.length == 0 ? 1 : ratings.length) * 100" />
<div class="text-sm text-muted text-left font-normal min-w-20">
@{{ c_ratings.length }}
<template v-if="c_ratings.length == 1">@lang('Review')</template>
<template v-else>@lang('Reviews')</template>
</div>
</div>
@endfor
<div class="mt-6 flex flex-col gap-y-2.5">
@foreach($product->reviewCountPerStar()->reverse() as $star => $reviewsCount)
<div class="flex flex-wrap items-center justify-between">
<div class="text-sm text flex items-center gap-x-2.5 font-medium">
<div class="w-2">{{ $star }}</div>
<div @class([
"flex items-center justify-center relative size-[18px] shrink-0",
"bg-emerald-600" => $reviewsCount,
"bg-emphasis" => !$reviewsCount,
])>
<x-rapidez::reviews-star />
</div>
</div>
</graphql>
</lazy>
<x-rapidez-reviews::bar class="mx-4 flex-1" :score="$reviewsCount / ($product->reviews_count == 0 ? 1 : $product->reviews_count) * 100" />
<div class="text-sm text-muted text-left font-normal min-w-20">
@choice(':count Review|:count Reviews', $reviewsCount)
</div>
</div>
@endforeach
</div>
<div class="mt-8 flex flex-col gap-y-1.5">
<div class="text-lg text font-semibold">@lang('Share your experience')</div>
Expand Down
23 changes: 23 additions & 0 deletions src/Models/RatingOptionVote.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Rapidez\Reviews\Models;

use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Rapidez\Core\Models\Model;

class RatingOptionVote extends Model
{
protected $table = 'rating_option_vote';

protected $primaryKey = 'vote_id';

public function product(): BelongsTo
{
return $this->belongsTo(config('rapidez.models.product'), 'entity_pk_value', 'entity_id');
}

public function review(): BelongsTo
{
return $this->belongsTo(Review::class, 'review_id', 'review_id');
}
}
34 changes: 34 additions & 0 deletions src/Models/Review.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Rapidez\Reviews\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Rapidez\Core\Models\Model;

class Review extends Model
{
protected $table = 'review';

protected $primaryKey = 'review_id';

protected static function booting()
{
static::addGlobalScope('approved', function ($builder) {
$builder->where('status_id', 1); // Approved
});
static::addGlobalScope('product', function ($builder) {
$builder->where('entity_id', 1); // Product
});
}

public function ratingOptionVotes(): HasMany
{
return $this->hasMany(RatingOptionVote::class, 'review_id');
}

public function averagePercent(): Attribute
{
return Attribute::get(fn () => $this->loadMissing('ratingOptionVotes')->ratingOptionVotes->avg('percent'));
}
}
23 changes: 23 additions & 0 deletions src/Models/Scopes/WithReviewsScope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Rapidez\Reviews\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class WithReviewsScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->with('reviews.ratingOptionVotes');
$builder
->selectRaw('ANY_VALUE(review_entity_summary.rating_summary) AS reviews_score')
->selectRaw('ANY_VALUE(review_entity_summary.reviews_count) AS reviews_count')
->leftJoin('review_entity_summary', function ($join) use ($model) {
$join->on($model->getTable().'.entity_id', '=', 'review_entity_summary.entity_pk_value')
->where('review_entity_summary.entity_type', 1)
->where('review_entity_summary.store_id', config('rapidez.store'));
});
}
}
52 changes: 52 additions & 0 deletions src/ReviewsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
namespace Rapidez\Reviews;

use BladeUI\Icons\Factory;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Rapidez\Core\Models\Product;
use Rapidez\Reviews\Models\RatingOptionVote;
use Rapidez\Reviews\Models\Review;
use TorMorten\Eventy\Facades\Eventy;

class ReviewsServiceProvider extends ServiceProvider
Expand All @@ -17,6 +22,53 @@ public function boot()
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/rapidez-reviews'),
], 'views');

config('rapidez.models.product')::resolveRelationUsing('reviews', function (Product $product) {
return $product->hasMany(Review::class, 'entity_pk_value', 'entity_id');
});

config('rapidez.models.product')::macro('reviewCountPerPercent', function () {
return Cache::store('array')->rememberForever('reviewsGroupedByAveragePercent:'.$this->getKey(), function () {
$review = new Review();
$ratingOptionVote = new RatingOptionVote();

$reviewAverages = $review
->newQuery()
->selectRaw($review->qualifyColumn('review_id').', AVG('.$ratingOptionVote->qualifyColumn('percent').') as average_percent')
->join(
$ratingOptionVote->getTable(),
$ratingOptionVote->qualifyColumn('review_id'),
'=',
$review->qualifyColumn('review_id')
)
->where($review->qualifyColumn('entity_pk_value'), $this->getKey())
->groupBy($review->qualifyColumn('review_id'));

return DB::query()
->fromSub($reviewAverages->toBase(), 'review_averages')
->selectRaw('average_percent, COUNT(*) as reviews_count')
->groupBy('average_percent')
->orderBy('average_percent')
->pluck('reviews_count', 'average_percent')
->mapWithKeys(fn ($reviewsCount, $averagePercent) => [$averagePercent + 0 => $reviewsCount]);
});
});

config('rapidez.models.product')::macro('reviewCountPerStar', function (int $stars = 5) {
$reviewsGroupedByAveragePercent = $this->reviewCountPerPercent();

$reviewsCountPerStar = [];
for ($i = 1; $i <= $stars; $i++) {
$reviewsCountPerStar[$i] = 0;
foreach ($reviewsGroupedByAveragePercent as $averagePercent => $reviewsCount) {
if (ceil($averagePercent / (100 / $stars)) == $i) {
$reviewsCountPerStar[$i] += $reviewsCount;
}
}
}

return collect($reviewsCountPerStar);
});
}

public function register()
Expand Down
Loading