Chia Wilsen
Chia Wilsen

Reputation: 11

Reading JSON in Model

I want to use WhereIn in my model, but its always returning null audience_id, i also wanted to take Audience detail from audience_id that i got in events table. thats why i used WhereIn

`<?php

namespace App\Models;

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

class Event extends Model
{
    /** @use HasFactory<\Database\Factories\EventFactory> */
    use HasFactory;
    protected $guarded =[];

    protected $casts = [
        'audience_id' => 'json',
        'location' => 'json',
        'guest_type' => 'json',
    ];

    public function audiences()
    {
        dd($this->audience_id);
        $audienceId = $this->audience_id['id'] ?? null;
        if ($audienceId) {
            return $this->hasMany(Audience::class, 'id')->whereIn('id', $audienceId);
        }

        return $this->hasMany(Audience::class, 'id')->whereIn('id', []);
    }
}`

whenever i use dd, its always returning null. And here is my table :

`public function up(): void
    {
        Schema::create('events', function (Blueprint $table) {
            $table->id();
            $table->string('category');
            $table->string('title');
            $table->json('location');
            $table->json('audience_id');
            $table->json('guest_type');
            $table->integer('time_span');
            $table->string('type');
            $table->timestamps();
        });
    }
`

Upvotes: 1

Views: 57

Answers (2)

UnderIdentityCrisis
UnderIdentityCrisis

Reputation: 86

It seems like the relationship logic is a bit over-complicated here.

When you're defining a relationship, the method is called before the data is actually retrieved, which is why you're getting null.

// This gives null because data isn't loaded yet
Event::with('audiences')->first();
// This gives the value of audience_id in the first row
Event::first()->audiences;

This works because the data is retrieved before audiences function is called.

Now for the solutions part, It looks like you're trying to create a relationship between Event and Audience. The correct database structure would have the event_id in the audiences table. You can then define the relationship like this:

In Event Model

public function audiences()
{
    return $this->hasMany(Audience::class, 'event_id', 'id');
}

and In Audience Model:

public function event()
{
    return $this->hasOne(Event::class, 'event_id', 'id');
}

If you can't make changes to the database, an alternative approach would be to use a package like https://github.com/staudenmeir/eloquent-json-relations. While I haven't personally used it, it looks like it could solve the issue you're facing.

There is also a same kind of a question which you are refer: Laravel hasMany on json field

Upvotes: 1

JorisJ1
JorisJ1

Reputation: 989

While I do not know why $this->audience_id is empty I am fairly certain this is not how Eloquent relationships are supposed to be used.

If Event-Audience is a one-to-many relationship then I would expect an event_id in the audiences table. If it were a many-to-many I would use a pivot table 'audience_event'.

If you insist on your datamodel you could remove the relation in the Event model, and instead manually add the related items using setRelation(), like this:

// Retrieve an event.
$event = Event::first();

// Get the array of ids, expected contents are something like {"id": [1, 2, 3]}.
$audienceIds = $event->audience_id['id'] ?? null;

// Retrieve audiences.
$audiences = Audience::whereIn('id', $audienceIds)->get();

// Add to the event.
$event->setRelation('audiences', $audiences);

Then a dd($event); would show this, as if there were an actual relationship defined in the model.

App\Models\Event {#1337 ▼ // app\Http\Controllers\MyController.php:23
  <snip>
  #relations: array:1 [▼
    "audiences" => Illuminate\Database\Eloquent\Collection {#1345 ▼
      #items: array:3 [▼
        0 => App\Models\Audience {#1347 ▶}        
        1 => App\Models\Audience {#1360 ▶}
        2 => App\Models\Audience {#1359 ▶}
      ]
      #escapeWhenCastingToString: false
    }
  ]
  <snip>
}

I am not an expert in Eloquent. Maybe there is a way to implement a relationship that looks more like your code, but this is an easy altenative.

Upvotes: 0

Related Questions