phplaraveldatabaserelation

How retrieve data from polymorphic relationship properly?


Hello I've got a problem with retrieving data from polymorphic relation in my Laravel 11 application. I am trying to retrieve all reports which are associated with one single Post.

It works with db facade like this

 $reports = DB::table('reports')
    ->where('reportable_id', $this->post->id)
    ->where('reportable_type', 'Post')
    ->get();

And it gave me 2 reports which are associeted with one current post and that's correct according to database.

But with relations it doesn't retrieve anything.

This is relation in Report model

  <?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;


class Report extends Model
{
    public $guarded = ['id'];


    public function reportable(): MorphTo
    {
        return $this->morphTo();
    }

And this is my Post model

<?php

namespace App\Models;

use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
    use HasFactory;
    public $guarded = ['id'];

    protected $casts = [
        'archived_at' => 'datetime',
    ];


    public function reports(): MorphMany
    {
        return $this->morphMany(Report::class, 'reportable');
    }

and this is how I'm using relation for retrieving data, it shows me empty collection.

$post = $this->post->reports;

this is reports migration

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('reports', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id')->nullable();
            $table->unsignedBigInteger('reportable_id')->nullable();
            $table->string('reportable_type')->nullable();
            $table->text('description')->nullable();
            $table->boolean('spam')->nullable()->default(null);
            $table->boolean('victimize')->nullable()->default(null);
            $table->boolean('offensive')->nullable()->default(null);
            $table->boolean('solved')->default(false);
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('reports');
    }
};

this is post migration

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')
            ->constrained('users')
            ->cascadeOnDelete();
            $table->foreignId('post_category_id')
            ->constrained('post_categories')
            ->cascadeOnDelete();
            $table->string('title');
            $table->text('body');
            $table->date('archived_at')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Just let you know, that I've got same logic by Likes and Comments and it works perfectly and I'm able to retrieve data through relation. But by reports I'm not. :/ Maybe is here something what I don't see.


Solution

  • Code you shared is ok. It seems that problem is when you create reports: "reportable_type" should be Fully Qualified Class Name or, in this case "App\Models\Post" not just Post like in your DB example.

    So, this should not work:

    DB::table('reports')
        ->where('reportable_type', 'Post')
        ...
    

    Reports Should be done like this:

    $post->reports()->create([
            'user_id' => $user->id,
            'reason' => 'Offensive',
        ]);
    

    or like this:

    Report::create([
                'user_id' => $user->id,
                'reportable_id' => $post->id,
                'reportable_type' => Post::class,
                'reason' => 'Spam',
            ]);