Skip to content

Commit

Permalink
UPDATE delete mark relations upon entity deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardodallavia committed May 13, 2022
1 parent 5152934 commit 723ef1c
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 20 deletions.
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Default prefix is set to `markable_`.
Here's an example migration for bookmarks:

``` php
class CreateBookmarksTable extends Migration
return new class extends Migration
{
public function up()
{
Expand All @@ -164,9 +164,12 @@ class CreateBookmarksTable extends Migration
}
```

Once done, you can create a new class which extends the abstract `Mark` class and implement the `markableRelationName` method, which defines the name of the relation.
Once done, you can create a new class which extends the abstract `Mark` class and implement the `markableRelationName` method, which is used to retrieve the users who marked a given model entity with the mark entity as pivot.

Here's an example model for bookmarks:
You can also override the `markRelationName` method, which is used to retrieve the list of marks of a given model entity.
By default, the relation name is the plural name of the mark class name.

Here's an example model for the bookmarks mark:

``` php
<?php
Expand All @@ -181,6 +184,15 @@ class Bookmark extends Mark
{
return 'bookmarkers';
}

/**
* The override is useless in this case, as I am returning the default
* relation name which is the plural name of the mark class name (bookmarks, indeed)
*/
public static function markRelationName(): string
{
return 'bookmarks';
}
}
```

Expand Down Expand Up @@ -227,15 +239,24 @@ Reaction::has($post, $user, 'heart'); // returns whether the user has reacted wi
Reaction::count($post, 'person_raising_hand'); // returns the amount of 'person_raising_hand' reactions for the given post
```

### Retrieve mark relation with eloquent
### Retrieve the list of marks of an entity with eloquent

``` php
use App\Models\Course;
use App\Models\Post;

Course::firstOrFail()->likers; // returns the collection of likes to the given course with their user
Course::firstOrFail()->likes; // returns the collection of like marks related to the course
Post::firstOrFail()->reactions; // returns the collection of reaction marks related to the post
```

### Retrieve the list of users who marked an entity with eloquent

``` php
use App\Models\Course;
use App\Models\Post;

Post::firstOrFail()->reacters; // returns the collection of reactions to the given post with their user and value
Course::firstOrFail()->likers; // returns the collection of users who liked the course along with the mark value as pivot
Post::firstOrFail()->reacters; // returns the collection of users who reacted to the post along with the mark value as pivot
```

### Filter marked models with eloquent
Expand Down
9 changes: 9 additions & 0 deletions src/Mark.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@

abstract class Mark extends MorphPivot
{
public $incrementing = true;

abstract public static function markableRelationName(): string;

public static function markRelationName(): string
{
return Str::of(
class_basename(static::class)
)->plural()->lower()->__toString();
}

public static function allowedValues(): ?array
{
$className = Str::lower(static::getMarkClassName());
Expand Down
52 changes: 38 additions & 14 deletions src/Markable.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ trait Markable
public static function bootMarkable(): void
{
static::registerMarks();

static::addGlobalScope(new MarkableScope);

static::deleted(
fn ($markable) => self::deleteMarks($markable)
);
}

public static function marks(): array
Expand All @@ -31,33 +36,52 @@ public function scopeWhereHasMark(Builder $builder, Mark $mark, Model $user, ?st
);
}

protected static function registerMarks(): void
protected static function deleteMarks(self $markable): void
{
foreach (static::marks() as $mark) {
static::registerMark($mark);
foreach (static::marks() as $markClass) {
$markModel = self::getMarkModelInstance($markClass);

$markRelationName = $markModel->markRelationName();

$markable->$markRelationName()->delete();
}
}

protected static function registerMark(string $mark)
protected static function registerMarks(): void
{
$instance = new $mark;

if (! $instance instanceof Mark) {
throw InvalidMarkInstanceException::create();
foreach (static::marks() as $markClass) {
static::addMarkableRelation($markClass);
}

static::addMarkableRelation($instance);
}

protected static function addMarkableRelation(Mark $mark)
protected static function addMarkableRelation(string $markClass): void
{
$markModel = self::getMarkModelInstance($markClass);

static::resolveRelationUsing(
$mark->markableRelationName(),
$markModel->markableRelationName(),
fn ($markable) => $markable
->morphToMany(config('markable.user_model'), 'markable', $mark->getTable())
->using($mark->getMorphClass())
->morphToMany(config('markable.user_model'), 'markable', $markModel->getTable())
->using($markModel->getMorphClass())
->withPivot('value')
->withTimestamps()
);

static::resolveRelationUsing(
$markModel->markRelationName(),
fn ($markable) => $markable
->morphMany($markClass, 'markable')
);
}

protected static function getMarkModelInstance(string $markClass): Mark
{
$instance = new $markClass;

if (! $instance instanceof Mark) {
throw InvalidMarkInstanceException::create();
}

return $instance;
}
}
53 changes: 53 additions & 0 deletions tests/MarkableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,57 @@ public function can_filter_marked_models()
$this->assertCount(2, Article::whereHasMark(app(Like::class), $users[0])->get());
$this->assertCount(1, Article::whereHasMark(app(Like::class), $users[1])->get());
}

/** @test */
public function it_should_delete_marks_related_to_deleted_markables()
{
$article = Article::factory()->create();
$user = User::factory()->create();

$mark = Like::add($article, $user);

$this->assertDatabaseHas($mark->getTable(), [
'user_id' => $user->getKey(),
'markable_id' => $article->getKey(),
'markable_type' => $article->getMorphClass(),
]);

$article->delete();

$this->assertDatabaseMissing($mark->getTable(), [
'user_id' => $user->getKey(),
'markable_id' => $article->getKey(),
'markable_type' => $article->getMorphClass(),
]);
}

/** @test */
public function it_should_retrieve_the_list_of_users_who_marked_an_entity()
{
$article = Article::factory()->create();
$users = User::factory(2)->create();

Like::add($article, $users[0]);
Like::add($article, $users[1]);

$this->assertEquals([
$users[0]->getKey(),
$users[1]->getKey(),
], $article->likers->modelKeys());
}

/** @test */
public function it_should_retrieve_the_list_of_marks_of_an_entity()
{
$article = Article::factory()->create();
$users = User::factory(2)->create();

$like1 = Like::add($article, $users[0]);
$like2 = Like::add($article, $users[1]);

$this->assertEquals([
$like1->getKey(),
$like2->getKey(),
], $article->likes->modelKeys());
}
}

0 comments on commit 723ef1c

Please sign in to comment.