diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fd6a6a3e..2a0dccb3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -20,13 +20,15 @@ - - + + + - - + + + diff --git a/tests/Stubs/Models/Article.php b/tests/Stubs/Models/Article.php index eb0aed22..6d4f9891 100644 --- a/tests/Stubs/Models/Article.php +++ b/tests/Stubs/Models/Article.php @@ -17,6 +17,7 @@ use Cog\Laravel\Love\Reactable\Models\Traits\Reactable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphMany; /** * @method static ArticleEloquentBuilder query() @@ -48,4 +49,9 @@ public function newEloquentBuilder($query): ArticleEloquentBuilder { return new ArticleEloquentBuilder($query); } + + public function morphableEntities(): MorphMany + { + return $this->morphMany(MorphableEntity::class, 'morphable'); + } } diff --git a/tests/Stubs/Models/MorphableEntity.php b/tests/Stubs/Models/MorphableEntity.php new file mode 100644 index 00000000..138106ee --- /dev/null +++ b/tests/Stubs/Models/MorphableEntity.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Cog\Tests\Laravel\Love\Stubs\Models; + +use Cog\Contracts\Love\Reactable\Models\Reactable as ReactableInterface; +use Cog\Laravel\Love\Reactable\Models\Traits\Reactable; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; + +final class MorphableEntity extends Model implements + ReactableInterface +{ + use HasFactory; + use Reactable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'morphable_entities'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + ]; +} diff --git a/tests/Unit/Reactable/ReactableEloquentBuilderTraitTest.php b/tests/Unit/Reactable/ReactableEloquentBuilderTraitTest.php index f00cb9b2..08d37917 100644 --- a/tests/Unit/Reactable/ReactableEloquentBuilderTraitTest.php +++ b/tests/Unit/Reactable/ReactableEloquentBuilderTraitTest.php @@ -17,6 +17,8 @@ use Cog\Laravel\Love\Reaction\Models\Reaction; use Cog\Laravel\Love\ReactionType\Models\ReactionType; use Cog\Tests\Laravel\Love\Stubs\Models\Article; +use Cog\Tests\Laravel\Love\Stubs\Models\Entity; +use Cog\Tests\Laravel\Love\Stubs\Models\MorphableEntity; use Cog\Tests\Laravel\Love\Stubs\Models\User; use Cog\Tests\Laravel\Love\TestCase; use Illuminate\Support\Str; @@ -1140,4 +1142,80 @@ public function it_can_chain_multiple_join_reaction_counter_of_type_with_custom_ ]; })->toArray()); } + + /** @test */ + public function it_breaks_when_join_reaction_counter_with_type_is_used_with_count_on_morphables(): void + { + Reactant::factory()->create(); // Needed to have not same ids with Reactant + + $reactionType1 = ReactionType::factory()->create([ + 'name' => 'Like', + 'mass' => 2, + ]); + $reactionType2 = ReactionType::factory()->create([ + 'mass' => 1, + ]); + $reactable1 = Article::factory()->has(MorphableEntity::factory()->count(3))->create(); + $reactable2 = Article::factory()->has(MorphableEntity::factory()->count(2))->create(); + $reactable3 = Article::factory()->has(MorphableEntity::factory()->count(4))->create(); + Reaction::factory()->count(2)->create([ + 'reaction_type_id' => $reactionType1->getId(), + 'reactant_id' => $reactable1->getLoveReactant()->getId(), + ]); + Reaction::factory()->count(3)->create([ + 'reaction_type_id' => $reactionType1->getId(), + 'reactant_id' => $reactable2->getLoveReactant()->getId(), + ]); + Reaction::factory()->count(1)->create([ + 'reaction_type_id' => $reactionType1->getId(), + 'reactant_id' => $reactable3->getLoveReactant()->getId(), + ]); + Reaction::factory()->count(4)->create([ + 'reaction_type_id' => $reactionType2->getId(), + 'reactant_id' => $reactable1->getLoveReactant()->getId(), + ]); + Reaction::factory()->count(5)->create([ + 'reaction_type_id' => $reactionType2->getId(), + 'reactant_id' => $reactable2->getLoveReactant()->getId(), + ]); + Reaction::factory()->count(6)->create([ + 'reaction_type_id' => $reactionType2->getId(), + 'reactant_id' => $reactable3->getLoveReactant()->getId(), + ]); + + $reactionType1CountKey = 'reaction_' . Str::snake($reactionType1->getName()) . '_count'; + $reactionType1WeightKey = 'reaction_' . Str::snake($reactionType1->getName()) . '_weight'; + + $reactablesOrderedAsc = Article::query() + ->withCount(['morphableEntities']) + ->joinReactionCounterOfType($reactionType1->getName()) + ->orderBy($reactionType1CountKey, 'asc') + ->get(); + + $reactablesOrderedDesc = Article::query() + ->joinReactionCounterOfType($reactionType1->getName()) + ->orderBy($reactionType1CountKey, 'desc') + ->get(); + + $assertAsc = [ + ['name' => $reactable3->name, "$reactionType1CountKey" => 1, "$reactionType1WeightKey" => 2], + ['name' => $reactable1->name, "$reactionType1CountKey" => 2, "$reactionType1WeightKey" => 4], + ['name' => $reactable2->name, "$reactionType1CountKey" => 3, "$reactionType1WeightKey" => 6], + ]; + $assertDesc = array_reverse($assertAsc); + $this->assertEquals($assertAsc, $reactablesOrderedAsc->map(function (Article $reactable) use ($reactionType1CountKey, $reactionType1WeightKey) { + return [ + 'name' => $reactable->getAttributeValue('name'), + "$reactionType1CountKey" => $reactable->{$reactionType1CountKey}, + "$reactionType1WeightKey" => $reactable->{$reactionType1WeightKey}, + ]; + })->toArray()); + $this->assertEquals($assertDesc, $reactablesOrderedDesc->map(function (Article $reactable) use ($reactionType1CountKey, $reactionType1WeightKey) { + return [ + 'name' => $reactable->getAttributeValue('name'), + "$reactionType1CountKey" => $reactable->{$reactionType1CountKey}, + "$reactionType1WeightKey" => $reactable->{$reactionType1WeightKey}, + ]; + })->toArray()); + } } diff --git a/tests/database/factories/MorphableEntityFactory.php b/tests/database/factories/MorphableEntityFactory.php new file mode 100644 index 00000000..6e03962c --- /dev/null +++ b/tests/database/factories/MorphableEntityFactory.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Cog\Tests\Laravel\Love\Database\Factories; + +use Cog\Tests\Laravel\Love\Stubs\Models\MorphableEntity; +use Illuminate\Database\Eloquent\Factories\Factory; + +final class MorphableEntityFactory extends Factory +{ + protected $model = MorphableEntity::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name(), + ]; + } +} diff --git a/tests/database/migrations/2016_09_02_173301_create_morphable_entities_table.php b/tests/database/migrations/2016_09_02_173301_create_morphable_entities_table.php new file mode 100644 index 00000000..24623da9 --- /dev/null +++ b/tests/database/migrations/2016_09_02_173301_create_morphable_entities_table.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + public function up(): void + { + Schema::create('morphable_entities', function (Blueprint $table) { + $table->increments('id'); + $table->morphs('morphable'); + $table->unsignedBigInteger('love_reactant_id')->nullable(); + $table->string('name'); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('morphable_entities'); + } +};