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
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,23 @@ This query will eager load the reviewer information for each received review.

**Note:** Consider using appropriate eager loading strategies based on your application's needs to optimize query performance.

#### Get a review received from a specified reviewer

To retrieve a review that a reviewer has given to reviewable:

```php
$review = $mentor->getReceivedReview($mentee);
```

This method returns a single `Review` instance or `null` if no review exists.

To include unapproved reviews in the search, pass `true` as the second parameter:

```php
$includeUnapproved = true;
$review = $mentor->getReceivedReview($mentee, $includeUnapproved);
```

#### Get all given reviews

To get all reviews given by a model:
Expand All @@ -158,6 +175,59 @@ Mentee::with('givenReviews.reviewable')->paginate();

This will eager load the reviewable model for each review given by the model.

#### Get a review given from a specified reviewable model

To retrieve a given review that a reviewable has received from reviewer:

```php
$review = $mentee->getGivenReview($mentor);
```

This method returns a single `Review` instance or `null` if no review exists.

To include unapproved reviews in the search, pass `true` as the second parameter:

```php
$includeUnapproved = true;
$review = $mentor->getGivenReview($mentee, $includeUnapproved);
```

### Checking for Reviews

#### Check if a reviewable model has received a review from a specific reviewer

```php
if ($mentor->hasReceivedReview($mentee)) {
// The mentor has received a review from the mentee
}
```

To include unapproved reviews in the check, pass `true` as the second parameter:

```php
$includeUnapproved = true;
if ($mentor->hasReceivedReview($mentee, $includeUnapproved)) {
// The mentor has received a review from the mentee
}
```

#### Check if the current model has given a review to the specified model

```php
if ($mentee->hasGivenReview($mentor)) {
// The mentee has given a review to the mentor
}
```

To include unapproved reviews in the check, pass `true` as the second parameter:

```php
$includeUnapproved = true;
if ($mentee->hasGivenReview($mentor, $includeUnapproved)) {
// The mentee has given a review to the mentor
}
```

### Review Model

The `Fajarwz\LaravelReview\Models\Review` model includes methods for managing and querying reviews:
Expand Down
31 changes: 27 additions & 4 deletions src/Traits/CanBeReviewed.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,35 @@ public function receivedReviews(?Model $model = null): HasMany
/**
* Check if the current model has received a review from the specified model.
*/
public function hasReceivedReview(Model $model): bool
public function hasReceivedReview(Model $model, bool $includeUnapproved = false): bool
{
return $this->receivedReviews()
$query = $this->receivedReviews()
->where('reviewer_type', get_class($model))
->where('reviewer_id', $model->getKey())
->exists();
->where('reviewer_id', $model->getKey());

if ($includeUnapproved) {
$query->withUnapproved();
}

return $query->exists();
}

/**
* Get the current model received review of the specified model.
*
* @param \Illuminate\Database\Eloquent\Model|null $model
*/
public function getReceivedReview(Model $model, bool $includeUnapproved = false): ?Review
{
$query = $this->receivedReviews()
->where('reviewer_type', get_class($model))
->where('reviewer_id', $model->getKey());

if ($includeUnapproved) {
$query->withUnapproved();
}

return $query->first();
}

/**
Expand Down
35 changes: 28 additions & 7 deletions src/Traits/CanReview.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,36 @@ public function givenReviews(?Model $model = null): HasMany

/**
* Check if the current model has given a review for the specified model.
*/
public function hasGivenReview(Model $model, bool $includeUnapproved = false): bool
{
$query = $this->givenReviews()
->where('reviewable_type', get_class($model))
->where('reviewable_id', $model->getKey());

if ($includeUnapproved) {
$query->withUnapproved();
}

return $query->exists();
}

/**
* Get the current model given review for the specified model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param \Illuminate\Database\Eloquent\Model|null $model
*/
public function hasGivenReview($model): bool
public function getGivenReview(Model $model, bool $includeUnapproved = false): ?Review
{
return $this->givenReviews()
$query = $this->givenReviews()
->where('reviewable_type', get_class($model))
->where('reviewable_id', $model->getKey())
->exists();
->where('reviewable_id', $model->getKey());

if ($includeUnapproved) {
$query->withUnapproved();
}

return $query->first();
}

/**
Expand All @@ -50,7 +71,7 @@ public function hasGivenReview($model): bool
*/
public function review($model, float $rating, ?string $reviewContent = null, bool $isApproved = true): Review
{
if ($this->hasGivenReview($model)) {
if ($this->hasGivenReview($model, true)) {
throw new DuplicateReviewException;
}

Expand Down Expand Up @@ -93,7 +114,7 @@ public function updateReview($model, float $newRating, ?string $newReview = null
*/
public function unreview($model): bool
{
if (! $this->hasGivenReview($model)) {
if (! $this->hasGivenReview($model, true)) {
throw new ReviewNotFoundException;
}

Expand Down
80 changes: 80 additions & 0 deletions tests/CanBeReviewedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Fajarwz\LaravelReview\Tests;

use DB;
use Fajarwz\LaravelReview\Models\Review;
use Fajarwz\LaravelReview\Tests\Models\Mentee;
use Fajarwz\LaravelReview\Tests\Models\Mentor;

Expand Down Expand Up @@ -68,6 +69,85 @@ public function test_hasReceivedReview_returns_false_if_a_reviewable_has_not_bee
$this->assertFalse($this->mentor->hasReceivedReview($otherMentee));
}

public function test_hasReceivedReview_returns_true_with_includeUnapproved_true_if_a_reviewable_has_unapproved_review_by_the_given_model()
{
$otherMentee = Mentee::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $otherMentee->id,
'reviewer_type' => get_class($otherMentee),
'reviewable_id' => $this->mentor->id,
'reviewable_type' => get_class($this->mentor),
'rating' => 3,
'approved_at' => null,
]);

$this->assertTrue($this->mentor->hasReceivedReview($otherMentee, true));
}

public function test_hasReceivedReview_returns_false_with_includeUnapproved_false_if_a_reviewable_has_unapproved_review_by_the_given_model()
{
$otherMentee = Mentee::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $otherMentee->id,
'reviewer_type' => get_class($otherMentee),
'reviewable_id' => $this->mentor->id,
'reviewable_type' => get_class($this->mentor),
'rating' => 3,
'approved_at' => null,
]);

$this->assertFalse($this->mentor->hasReceivedReview($otherMentee));
}

public function test_getReceivedReview_retrieves_only_approved_review_by_default()
{
$review = $this->mentor->getReceivedReview($this->mentee);

$this->assertInstanceOf(Review::class, $review);
$this->assertEquals($this->mentee->id, $review->reviewer_id);
$this->assertEquals(4.5, $review->rating);
}

public function test_getReceivedReview_returns_null_for_unapproved_review_by_default()
{
$newMentee = Mentor::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $newMentee->id,
'reviewer_type' => get_class($newMentee),
'reviewable_id' => $this->mentor->id,
'reviewable_type' => get_class($this->mentor),
'rating' => 3,
'approved_at' => null,
]);

$review = $this->mentor->getReceivedReview($newMentee);

$this->assertNull($review);
}

public function test_getReceivedReview_can_retrieve_unapproved_review_when_includeUnapproved_is_true()
{
$newMentee = Mentee::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $newMentee->id,
'reviewer_type' => get_class($newMentee),
'reviewable_id' => $this->mentor->id,
'reviewable_type' => get_class($this->mentor),
'rating' => 3,
'approved_at' => null,
]);

$review = $this->mentor->getReceivedReview($newMentee, true);

$this->assertInstanceOf(Review::class, $review);
$this->assertEquals($newMentee->id, $review->reviewer_id);
$this->assertEquals(3, $review->rating);
}

public function test_a_reviewable_can_display_its_review_summary()
{
$this->assertTrue($this->mentor->reviewSummary->exists());
Expand Down
80 changes: 80 additions & 0 deletions tests/CanReviewTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use DB;
use Fajarwz\LaravelReview\Exceptions\DuplicateReviewException;
use Fajarwz\LaravelReview\Exceptions\ReviewNotFoundException;
use Fajarwz\LaravelReview\Models\Review;
use Fajarwz\LaravelReview\Models\ReviewSummary;
use Fajarwz\LaravelReview\Tests\Models\Mentee;
use Fajarwz\LaravelReview\Tests\Models\Mentor;
Expand Down Expand Up @@ -70,6 +71,85 @@ public function test_hasGivenReview_returns_false_if_a_reviewer_has_not_reviewed
$this->assertFalse($this->mentee->hasGivenReview($otherMentor));
}

public function test_hasGivenReview_returns_true_with_includeUnapproved_true_if_a_reviewer_has_unapproved_review_for_the_given_model()
{
$otherMentor = Mentor::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $this->mentee->id,
'reviewer_type' => get_class($this->mentee),
'reviewable_id' => $otherMentor->id,
'reviewable_type' => get_class($otherMentor),
'rating' => 3,
'approved_at' => null,
]);

$this->assertTrue($this->mentee->hasGivenReview($otherMentor, true));
}

public function test_hasGivenReview_returns_false_with_includeUnapproved_false_if_a_reviewer_has_unapproved_review_for_the_given_model()
{
$otherMentor = Mentor::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $this->mentee->id,
'reviewer_type' => get_class($this->mentee),
'reviewable_id' => $this->mentor->id,
'reviewable_type' => get_class($this->mentor),
'rating' => 3,
'approved_at' => null,
]);

$this->assertFalse($this->mentee->hasGivenReview($otherMentor));
}

public function test_getGivenReview_retrieves_only_approved_review_by_default()
{
$review = $this->mentee->getGivenReview($this->mentor);

$this->assertInstanceOf(Review::class, $review);
$this->assertEquals($this->mentor->id, $review->reviewable_id);
$this->assertEquals(4.5, $review->rating);
}

public function test_getGivenReview_returns_null_for_unapproved_review_by_default()
{
$newMentor = Mentor::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $this->mentee->id,
'reviewer_type' => get_class($this->mentee),
'reviewable_id' => $newMentor->id,
'reviewable_type' => get_class($newMentor),
'rating' => 3,
'approved_at' => null,
]);

$review = $this->mentee->getGivenReview($newMentor);

$this->assertNull($review);
}

public function test_getGivenReview_can_retrieve_unapproved_review_when_includeUnapproved_is_true()
{
$newMentor = Mentor::factory()->create();

DB::table('reviews')->insert([
'reviewer_id' => $this->mentee->id,
'reviewer_type' => get_class($this->mentee),
'reviewable_id' => $newMentor->id,
'reviewable_type' => get_class($newMentor),
'rating' => 3,
'approved_at' => null,
]);

$review = $this->mentee->getGivenReview($newMentor, true);

$this->assertInstanceOf(Review::class, $review);
$this->assertEquals($newMentor->id, $review->reviewable_id);
$this->assertEquals(3, $review->rating);
}

public function test_a_reviewer_can_review_the_given_model()
{
$newMentor = Mentor::factory()->create();
Expand Down