stevenc21's avatar

Cannot Fix This relationship?

I am messing up some sort relationship here. And I can't figure out why.

I'm getting this error when I try the test below.

I don't think it should be looking for this column in the asks table, but should instead be saving them in the Activity table. The activity table does have the right columns.

Basically I have Events, which can have multiple types of different activities associated, one of which is an Ask, other might be a RSVP for instance.

SQLSTATE[HY000]: General error: 1 no such column: activitable_id (SQL: update "asks" set "activitable_id" = 1, "activitable_type" = App\Models\Event, "updated_at" = 2024-03-21 11:50:35 where "id" = 1)

Any ideas?

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Event extends Model
{
    use HasFactory;

    protected $fillable = ['description', 'shortdescription', 'status'];

    public function activities()
    {
        return $this->morphMany(Activity::class, 'activitable');
    }
}

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Ask extends Model
{
    use HasFactory;

    protected $fillable = ['description', 'shortdescription', 'status'];

    public function activities()
    {
        return $this->morphMany(Activity::class, 'activitable');
    }
}

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Activity extends Model
{

    protected $guarded = [];


    public function activitable()
    {
        return $this->morphTo();
    }

    public function contact()
    {
        return $this->belongsTo(Contact::class);
    }
}


public function test_creating_an_ask()
    {
        $this->actingAs($user = User::factory()->create());
    
        //Create the event
        $this->actingAs($user = User::factory()->create());
        $newEvent = new Event;
        $newEvent->name = "The new Event";
        $newEvent->description = "My description of event";
        
        $newEvent->save();

        $this->assertModelExists($newEvent);

        //create the ask
        $ask = Ask::create([
            'description' => 'Test Event Description',
            'shortdescription' => 'Test Event Short Description',
            'status' => 'active', // or any other status
        ]);
        
        $newEvent->activities()->save($ask);
        
        // Assert that the Ask was associated with the event
        $this->assertEquals($newEvent->id, $ask->activitable_id);
        $this->assertEquals(Ask::class, $ask->activitable_type);

        // Assert that the Ask was added to the database
        $this->assertDatabaseHas('activities', [
            'id' => $ask->id,
            'activitable_id' => $event->id,
            'activitable_type' => Ask::class,
            'status' => 'active', // or any other status
        ]);
      
    }
0 likes
2 replies
LaryAI's avatar
Level 58

It seems like there is a misunderstanding in how the polymorphic relationships are set up in your Eloquent models. The Ask model should not have a morphMany relationship to Activity, because Ask is not the parent in this polymorphic relation, it is the child. Instead, Ask should have a morphOne or morphMany relationship to Activity through the activitable method.

Here's how you can fix the relationships:

  1. Remove the activities method from the Ask model, as it is not needed.
  2. Ensure that the Activity model has the morphTo relationship correctly set up, which it does in your provided code.
  3. When you are associating the Ask with an Event, you should create an Activity instance and set the activitable relationship to the Ask.

Here's the corrected code:

// Event model stays the same
class Event extends Model
{
    // ... existing code ...

    public function activities()
    {
        return $this->morphMany(Activity::class, 'activitable');
    }
}

// Ask model without the activities() method
class Ask extends Model
{
    use HasFactory;

    protected $fillable = ['description', 'shortdescription', 'status'];

    // No activities() method needed here
}

// Activity model stays the same
class Activity extends Model
{
    // ... existing code ...

    public function activitable()
    {
        return $this->morphTo();
    }
}

// Test method with corrections
public function test_creating_an_ask()
{
    // ... existing code ...

    // Create the ask
    $ask = Ask::create([
        'description' => 'Test Event Description',
        'shortdescription' => 'Test Event Short Description',
        'status' => 'active', // or any other status
    ]);

    // Create an activity and associate it with the ask
    $activity = new Activity();
    $activity->activitable()->associate($ask);
    $newEvent->activities()->save($activity);

    // Assert that the Activity was associated with the ask and event
    $this->assertEquals($newEvent->id, $activity->activitable->id);
    $this->assertEquals(get_class($newEvent), $activity->activitable_type);

    // Assert that the Activity was added to the database
    $this->assertDatabaseHas('activities', [
        'id' => $activity->id,
        'activitable_id' => $ask->id,
        'activitable_type' => get_class($ask),
        'status' => 'active', // or any other status
    ]);
}

Make sure that your activities table has the correct columns for a polymorphic relationship: activitable_id and activitable_type. The activitable_id column should store the ID of the related model (in this case, the Ask ID), and the activitable_type should store the class name of the related model (in this case, 'App\Models\Ask').

Also, ensure that you have run the migrations to add these columns to the activities table if you haven't already.

tisuchi's avatar

@stevenc21 You can try this:

public function test_creating_an_ask()
{
    $this->actingAs($user = User::factory()->create());

    $newEvent = Event::create([
        'name' => "The new Event",
        'description' => "My description of event",
    ]);

    $this->assertModelExists($newEvent);

    $ask = Ask::create([
        'description' => 'Test Event Description',
        'shortdescription' => 'Test Event Short Description',
        'status' => 'active', // or any other status
    ]);

    $activity = new Activity([
        // Set properties for the activity if needed
    ]);

    // Associate the activity with the ask
    $ask->activities()->save($activity);

    // Now, relate the activity to the event
    $newEvent->activities()->save($activity);

    // Assertions
    $this->assertEquals($newEvent->id, $activity->activitable_id);
    $this->assertEquals(get_class($newEvent), $activity->activitable_type);

    // Assert that the Activity associated with the Ask and Event is added to the database
    $this->assertDatabaseHas('activities', [
        'id' => $activity->id,
        'activitable_id' => $newEvent->id,
        'activitable_type' => get_class($newEvent),
    ]);
}

1 like

Please or to participate in this conversation.