Laravel Eloquent: How to automatically fetch relations when serializing through toArray/toJson - php

I figures this works for automatically fetching user and replies when I am serializing my object to JSON, but is overriding toArray really the proper way of doing this?
<?php
class Post extends Eloquent
{
protected $table = 'posts';
protected $fillable = array('parent_post_id', 'user_id', 'subject', 'body');
public function user()
{
return $this->belongsTo('User');
}
public function replies()
{
return $this->hasMany('Post', 'parent_post_id', 'id');
}
public function toArray()
{
$this->load('user', 'replies');
return parent::toArray();
}
}

Instead of overriding toArray() to load user and replies, use $with.
Here's an example:
<?php
class Post extends Eloquent
{
protected $table = 'posts';
protected $fillable = array('parent_post_id', 'user_id', 'subject', 'body');
protected $with = array('user', 'replies');
public function user()
{
return $this->belongsTo('User');
}
public function replies()
{
return $this->hasMany('Post', 'parent_post_id', 'id');
}
}
Also, you should be using toArray() in your controllers, not your models, like so:
Post::find($id)->toArray();
Hope this helps!

I must submit a new answer since I'm a SO pleb. A more proper way to accomplish this for those finding this on Google like I did would be to avoid using protected $with if you don't have to and instead move that with() call to your retrieval.
<?php
class Post extends Eloquent
{
protected $table = 'posts';
protected $fillable = array('parent_post_id', 'user_id', 'subject', 'body');
public function user()
{
return $this->belongsTo('User');
}
public function replies()
{
return $this->hasMany('Post', 'parent_post_id', 'id');
}
}
And then you could modify the Post call to pre-load as needed:
Post::with('user','replies')->find($id)->toArray();
This way, you won't be including un-needed data every time you grab a record, if you don't need it.

Related

How to obtain three level model data laravel

Updated
User model
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens, HasRoles;
const MALE = 'male';
const FEMALE = 'female';
protected $guard_name = 'sanctum';
public function educationalBackgrounds()
{
return $this->hasMany("App\Models\Users\EducationalBackground", "user_id");
}
public function seminars()
{
return $this->hasMany("App\Models\Users\Seminar", "user_id");
}
}
I have child table EducationalBackground which is related to User table
class EducationalBackground extends Model
{
use HasFactory;
protected $table = 'users.educational_backgrounds';
protected $fillable = [
'user_id',
'studies_type',
'year',
'course',
];
public function user()
{
return $this->belongsTo('App\Models\User', 'user_id');
}
public function educationalAwards()
{
return $this->hasMany("App\Models\Users\EducationalAward", "educational_background_id");
}
}
And a third table that i want to access the award field
class EducationalAward extends Model
{
use HasFactory;
protected $table = 'users.educational_awards';
protected $fillable = [
'educational_background_id',
'award',
'photo',
];
public function educationalBackground()
{
return $this->belongsTo('App\Models\Users\EducationalBackground', 'educational_background_id');
}
}
I have api get route here
Route::get('/educational-background/{id}', [UserProfileController::class, 'getEducationalBackground']);
Here is my api method it works fine. But i want to go deeper and access the data of third table.
public function getEducationalBackground($id)
{
$educationalBackground = EducationalBackground::with('user')->where('user_id', $id)->get();
return response()->json($educationalBackground, 200);
}
It looks like you're not really grasping the concept of relations yet. Also, I'd advise you to look into route model binding :) What you basically want to be doing is:
public function getEducationalBackground($id)
{
$user = User::find($id);
return $user->educationalBackgrounds()->with('educationalAwards')->get();
}
Also, when you're pretty sure that whenever you want to use backgrounds, you also want to use the awards, you can add the with(...) to the model definition like so:
class EducationalBackground extends Model
{
...
protected $with = ['educationalAwards'];
}
That way, you can simplify your controller method to:
public function getEducationalBackground($id)
{
$user = User::find($id);
return $user->educationalBackgrounds;
}

Relation isn't showed in query with Laravel

I have in model Report following
public function reportedItem()
{
return $this->belongsTo('App\Item', 'item_id', 'id');
}
In Item model
public function report()
{
return $this->hasMany('App\Report', 'item_id','id');
}
In controller
public function details( $item_id )
{
$flags = Item::find($item_id)->report->unique('user_id');
return view('flags.details', compact('flags'));
}
Why when I do {{ dd(collect($flags)) }} in my view.blade I don't see anything from items table even when I query it Item::find($item_id)?
dd output
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Report extends Model
{
protected $table = 'reports';
protected $primaryKey = 'report_id';
protected $fillable = [
'item_id', 'report_body', 'user_id', 'report_reason'
];
public function reportedItem()
{
return $this->belongsTo('App\Item', 'item_id', 'id');
}
public function user()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
}
One way is to use something like this in your controller
public function details( $item_id )
{
$flags = Report::with('reportedItem')->where('item_id', '$item_id')->get();
try this
$flags = Item::find($item_id)->with('report')->get()
The below should work
$flags = Item::with('reportedItem')->find($item_id);
Then when you dd($flags) this look for the "relations" tab and inside that you'll see the info.

Delete hasManyThrough relationship rows using Laravel's Eloquent

I have three models, Advertiser, PtcAd, and PtcCampaign. When deleting a Advertiser I want to delete all related PtcAds and PtcCampaigns. The Advertiser has many PtcCampaigns through PtcAds.
Advertiser Model
use SoftDeletes;
protected $dates = ['deleted_at'];
public function ptcAds()
{
return $this->hasMany('App\PtcAd');
}
public function ptcCampaigns()
{
return $this->hasManyThrough('App\PtcCampaign', 'App\PtcAd');
}
public function delete()
{
$this->ptcAds()->delete();
// I'VE TRIED WITH AND WITHOUT THIS
$this->ptcCampaigns()->delete();
return parent::delete();
}
PtcAd Model
use SoftDeletes;
protected $fillable = ['advertiser_id', 'title'];
protected $dates = ['deleted_at'];
public function advertiser()
{
return $this->belongsTo('App\Advertiser');
}
public function ptcCampaigns()
{
return $this->hasMany('App\ptcCampaign');
}
public function delete()
{
$this->ptcCampaigns()->delete();
return parent::delete();
}
PtcCampaign Model
use SoftDeletes;
public $timestamps = false;
protected $fillable = ['ptc_ad_id', 'clicks'];
protected $dates = ['paused_at', 'deleted_at'];
public function ptcAd()
{
return $this->belongsTo('App\PtcAd');
}
My tests:
public function test_delete_advertiser()
{
$advertiser = factory(Advertiser::class)->create();
$ptcAd = factory(PtcAd::class)->create(['advertiser_id' => $advertiser->id]);
$ptcCampaign = factory(PtcCampaign::class)->create(['ptc_ad_id' => $ptcAd->id]);
$this->assertTrue($advertiser->delete());
$this->assertFalse(Advertiser::all()->contains($advertiser));
$this->assertFalse(PtcAd::all()->contains($ptcAd));
// THE FOLLOWING TEST DOESN'T WORK!
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign));
}
// ALL OF THE FOLLOWING TESTS WORK!
public function test_delete_ad()
{
$ptcAd = factory(PtcAd::class)->create();
$ptcCampaign = factory(PtcCampaign::class)->create(['ptc_ad_id' => $ptcAd->id]);
$this->assertTrue($ptcAd->delete());
$this->assertFalse(PtcAd::all()->contains($ptcAd));
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign));
}
The $this->assertFalse(PtcCampaign::all()->contains($ptcCampaign)) in the test_delete_advertiser() test fails, why?
I have more tests to make sure all the relationships work so I really don't know what could possibly be wrong. My next attempt would be to make foreach in the Advertiser's delete() method but maybe there's something simpler and I want to understand why this doesn't work.
It looks the problem is with the sequence of delete statement.
Try by changing the sequence like below:
public function delete()
{
$this->ptcCampaigns()->delete();
$this->ptcAds()->delete();
return parent::delete();
}
You can use Laravel's Model Events (deleting) to delete related models like this:
class Advertiser extends Eloquent
{
public function ptcAds()
{
return $this->hasMany('PtcAd');
}
// this is a recommended way to declare event handlers
protected static function boot() {
parent::boot();
static::deleting(function($adv) { // before delete() method call this
$adv->ptcAds()->delete();
// do the rest of the cleanup...
});
}
}
// Same for PtcCompaigns
class PtcAd extends Eloquent
{
public function ptcCompaigns()
{
return $this->hasMany('PtcCompaigns');
}
// this is a recommended way to declare event handlers
protected static function boot() {
parent::boot();
static::deleting(function($ptc_ad) { // before delete() method call this
$ptc_ad->ptcCompaigns()->delete();
// do the rest of the cleanup...
});
}
}
Hope this helps!

Obtaining a list of non-linked models, in many_to_many relations, using Laravel Eloquent

I have a User-Roles model, using Laravel 4 where a user can have many roles, using Eloquent. I can access all roles linked to a user easily using this code :
class User extends Model {
protected $table = 'user';
protected $fillable = array('name');
public function rolesLinked() {
return $this->hasMany('App\UserRoleLink', 'user_id');
}
}
I've been trying to obtain the roles that are not linked to a user, to display on the specific user's page in a select box. Using this function, included in the User class.
public function rolesNotLinked() {
$user = this
$roles = Roles::whereDoesntHave('App\UserRoleLink',function($query) use ($user){
$query->where('user_id',$user->id);
});
}
The problem is, calling this function gives me the following error.
Call to undefined method Illuminate\Database\Query\Builder::App\UserRoleLink()
I've tried using has with < 1 to see if the function was problematic, but after reading this and the online source code, the function call pretty much does what I've tried.
Is something wrong in my function call, or have I messed up configurations somewhere?
For reference, here are my other Model classes:
class UserRoleLink extends Model{
protected $table = 'user_role_link';
protected $fillable = array('role_id','user_id);
public function role() {
return $this->hasOne('App\Role', 'role_id');
}
}
class Role extends Model{
protected $table = 'role';
protected $fillable = array('name');
}
EDIT: I've found out that I messed up by fillables when I copy-pasted. It didn't fix the issue, but I guess that's one step closer.
To use whereDoesntHave method, you must add the relation in your Role Model.
class Role extends Model{
protected $table = 'role';
protected $fillable = array('name');
public function UserRoles() {
return $this->hasMany('App\UserRoleLink', 'id');
}
}
Also, the whereDoesntHave method first parameter is not thte model but the function of the relation:
public function rolesNotLinked() {
$user = this
$roles = Roles::whereDoesntHave('UserRoles',function($query) use ($user){
$query->where('user_id',$user->id);
});
}

Eloquent ORM Movie Location

I am using Laravel 4 and eloquent orm to build a movie session database.
Now I have the following Models.
class Location extends \Eloquent
{
public $timestamps = false;
public $table = 'movsys_location';
protected $fillable = array('name', 'desc');
public function sessions(){
return $this->hasMany('Session', 'location_id');
}
}
class Session extends \Eloquent
{
public $timestamps = false;
public $table = 'movsys_session';
protected $fillable = array('time');
public function location(){
return $this->hasOne('Location', 'id');
}
}
(Note: These models are stripped to the necessary code.)
Now in my controller, I have the following.
$Sessions = Session::all();
foreach($Sessions as $Session){
echo (isset($Session->location->name) ? $Session->location->name : 'NO LOCATION');
}
And this is what my database looks like:
Now, everything seems to work, but even though both sessions have the same location, ONLY the first session will return the name of the location! The second echo will return "NO LOCATION".
Any idea or help as to why would be appreciated. If this answer isnt clear enough let me know.
Try this one in place of yours:
class Session extends \Eloquent
{
public $timestamps = false;
public $table = 'movsys_session';
protected $fillable = array('time');
public function location(){
return $this->belongsTo('Location');
}
}

Categories