Portatour Reviews Best May 2026
return response()->json(['stats' => $stats, 'reviews' => $reviews]); import useState, useEffect from 'react'; import StarRating from './StarRating'; const PortaTourReviews = ( tourId ) => const [reviews, setReviews] = useState([]); const [stats, setStats] = useState(null); const [newReview, setNewReview] = useState( rating: 5, title: '', comment: '' );
$reviews = PortaTourReview::where('tour_id', $tourId) ->where('is_approved', true) ->with('user:id,name,avatar') ->orderBy('created_at', 'desc') ->paginate(10); // Aggregate rating breakdown $stats = [ 'average_rating' => PortaTourReview::where('tour_id', $tourId) ->where('is_approved', true) ->avg('rating'), 'total_reviews' => $reviews->total(), 'rating_counts' => PortaTourReview::where('tour_id', $tourId) ->where('is_approved', true) ->selectRaw('rating, count(*) as count') ->groupBy('rating') ->pluck('count', 'rating') ]; portatour reviews
<div className="reviews-list"> reviews.map(review => ( <div key=review.id className="review-card"> <div className="reviewer-info"> <img src=review.user.avatar alt=review.user.name /> <strong>review.user.name</strong> review.is_verified_purchase && <span className="verified">✓ Verified Tour</span> </div> <StarRating rating=review.rating readonly /> <h3>review.title</h3> <p>review.comment</p> review.images?.length > 0 && ( <div className="review-images"> review.images.map(img => <img key=img src=img />) </div> ) <button onClick=() => markHelpful(review.id)>👍 Helpful (review.helpful_count)</button> </div> )) </div> useEffect from 'react'
class PortaTourReview extends Model