Laravel Complex Relationships Through Polymorphism - laravel

I have the following models using Laravel 5.3:
Provider:
// Provider model
$primaryKey = 'id'
public function activities()
{
return $this->hasMany(Activity::class);
}
Activity:
// Activity model
$primaryKey = 'id'
public function provider()
{
return $this-belongsTo(Provider::class);
}
public function semesters()
{
return $this->hasMany(Semester::class);
}
public function semesterPurchases()
{
return $this->hasManyThrough(Purchase::class, Semester::class, 'activity_id', 'purchasable_id')
->where('purchasable_type', Semester::class);
}
Semester:
// Semester model
$primaryKey = 'id'
public function activity()
{
return $this->belongsTo(\App\Models\Activity::class, 'activity_id', 'id');
}
Purchase:
// Purchase model
$primaryKey = 'id'
public function purchasable()
{
return $this->morphTo();
}
In my case Semester::class is the purchasable_type. Is there a way to establish a relationship between Provider::class and Purchase::class? In order to make it possible to do something like this:
$providers = Provider::select('id', 'name', 'address')
->with('purchases')
->where('providers.id', 1)
->get();
I would prefer not to go through activities like so:
$providers = Provider::select('id', 'name', 'address')
->with('activities.purchases')
->where('providers.id', 1)
->get();
which I know I can do using hasManyThrough on the Activity::class

Laravel has no native support for a direct relationship.
I've created a package for cases like this: https://github.com/staudenmeir/eloquent-has-many-deep
class Provider extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function purchases()
{
return $this->hasManyDeep(
Purchase::class,
[Activity::class, Semester::class],
[null, null, ['purchasable_type', 'purchasable_id']]
);
}
}
Provider::find($id)->purchases;

Related

Blank object in Eloquent belongsTo()

I'm trying to display which one attribute (code) of Item. ServiceItem has Item as a foreign key. But I can't get the Item at all.
This one gives a blank object in blade template:
#foreach ($service->serviceItems as $serviceItem )
{{ json_encode($serviceItem->item()) }}
#endforeach
Here's my model declaration:
//ServiceItem model
class ServiceItem extends Model
{
use HasFactory;
public $fillable = ['service_id', 'item_id', 'values'];
public function service()
{
return $this->belongsTo(Service::class, 'foreign_key');
}
// this doesn't work
public function item()
{
return $this->belongsTo(Item::class, 'foreign_key');
}
}
// Service model
class Service extends Model
{
use HasFactory;
public $fillable = ['user_id', 'site_id', 'title', 'status', 'remarks', 'report', 'date'];
public function user()
{
return $this->belongsTo('\App\Models\User');
}
public function site()
{
return $this->belongsTo('\App\Models\Site');
}
public function serviceItems() {
return $this->hasMany('\App\Models\ServiceItem');
}
}
This is my controller:
public function index()
{
$services = Service::latest()->paginate(5);
return view('services.index', compact('services'))
->with('i', (request()->input('page', 1) - 1) * 5);
}
Please help me to display the code attribute in Item from Service!!! Thanks a lot!
I suppose you read the Laravel doc of model relationship definition. They referenced to put foreign key as the second parameter, not the foreign_key as word but your actual foreign key that reference the parent table. you have to change the model code.
class ServiceItem extends Model
{
use HasFactory;
public $fillable = ['service_id', 'item_id', 'values'];
public function service()
{
return $this->belongsTo(Service::class, 'service_id');
}
public function item()
{
return $this->belongsTo(Item::class, 'item_id');
}
}
and then $serviceItem->item should work as expected.

Laravel access to two level parent table in a relationship

i have use laravel and i have this models and relationship between tables
Customers table
class Customers extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'contr_nom',
'contr_cog',
'benef_nom',
'benef_cog',
'email',
'polizza',
'targa',
'iban',
'int_iban',
'cliente',
];
public function claims()
{
return $this->hasMany(Claims::class);
}
public function refunds()
{
return $this->hasManyThrough(Refunds::class, Claims::class);
}
}
claims table
class Claims extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'dossier',
'date_cla',
];
public function refunds()
{
return $this->hasMany(Refunds::class);
}
public function customers()
{
return $this->belongsTo(Customers::class,'customers_id');
}
}
refunds table
class Refunds extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'date_ref',
'status_ref',
'disactive',
'num_pre',
'date_liq',
];
public function services()
{
return $this->belongsToMany(Services::class)
->withPivot(['services_id','services_amount','services_status']);
}
public function claims()
{
return $this->belongsTo(Claims::class,'claims_id');
}
}
in controller i did this function
public function addDateLiq2(Request $request){
$date_liq = request("date_liq");
$refunds = Refunds::whereNotNull('num_pre')->get();
foreach ($refunds as $refund) {
$status_ref= $refund->status_ref;
if ($status_ref == 5){
//send mail
//I need to retrieve mail field from customers table
}
$refund->date_liq = $date_liq;
$refund->save();
if(!$refund->save()){
App::abort(500, 'Error');
}
}
return Response::json(array('success' => 'Date salvate massivamente correttamente!'), 200);
}
that add a date in all records where num_pre is not null.
OK i wanted also send a mail but mail field is in Customer parent table....how can i access it?
Thx
Ok i seem i found a way with this in function addDateLiq2
$data = Claims::with(array('customers'=>function($query){
$query->select('id','email');
}))
->whereHas('refunds', function($query) use($claims_id) {
$query->where('claims_id', $claims_id);
})
->first();

Laravel eloquent multiple levels of relations

Alright so I am basically trying to retrieve all animal_registry codes based on a user ID.
Idea is that
1 user has many jobs.
Jobs are consisted of many "Jobs data".
Jobs data has many "Animal registry" entries.
These are my relations
Image relations link (click)
And these are my relations in Laravel
class User
{
public function jobs()
{
return $this->hasMany('App\Models\RegistryJobs', 'employee', 'id');
}
}
class RegistryJobs extends Model
{
protected $table = "registry_jobs";
protected function jobsData()
{
$this->hasManyThrough('App\Models\AnimalRegistry', 'App\Models\RegistryJobsData', 'id', 'animal_registry_id');
}
}
class RegistryJobsData extends Model
{
protected $table = "registry_jobs_data";
public function jobs()
{
$this->belongsTo('App\Models\RegistryJobs', 'id', 'registry_jobs_id');
}
public function animals()
{
$this->hasMany('App\AnimalRegistry', 'id', 'animal_registry_id');
}
}
class AnimalRegistry extends Model
{
protected $table = "animal_registry";
}
And now I am trying to query it from a controller in a way
$data = User::whereHas('jobs', function ($query) {
$query->where('id', 1);
})->get();
But I am unable to access the properties from the animal_registry.
Can you try like this :
public function animals(){
return $this->hasManyThrough('App\Registry_Jobs_data','App\Registry_Jobs', 'employee',
'registry_jobs_id', 'id' ,'id')->join('//Do the joining')->select();
}
Check the hasManyThrough i am not sure..

type casting in laravel json response in relationships eager loading

This is my post model.
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
protected $fillable = ['title','featuring_image', 'brief', 'body', 'seen_count'];
public function user(){
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function someComments()
{
return $this->comments()->limit(Constants::COMMENTS_COUNT_LIMIT);
}
public function commentsCount()
{
return $this->comments()
->selectRaw('post_id, count(*) as count')
->groupBy('post_id');
}
public function likes()
{
return $this->hasMany(Like::class);
}
public function isLiked()
{
return $this->likes()->where('user_id', auth()->check() ? auth()->user()->id : 0);
}
public function likesCount()
{
return $this->likes()
->selectRaw('post_id, count(*) as count')
->groupBy('post_id');
}
}
I executed this query on this model.
$post = Post::with(['categories', 'user', 'commentsCount', 'likesCount', 'isLiked'])->find($post->id);
Because of the relation between this table and like and comment table, The output of this query for 'commentsCount', 'likesCount', 'isLiked' is an array. But I need to receive numbers for 'commentsCount' and 'likesCount', and a boolean for 'isliked' as an output, in laravel josn response.
You might find it easier to use the withCount() the comes with Eloquent instead.
Then for is_liked you could use a scope to get the value and the cast it to a boolean:
public function scopeIsLiked($query)
{
if (is_null($query->getQuery()->columns)) {
$query->select([$query->getQuery()->from . '.*']);
}
$relation = Relation::noConstraints(function () {
return $this->likes();
});
$q = $this->likes()->getRelationExistenceCountQuery(
$relation->getRelated()->where('user_id', auth()->check() ? auth()->user()->id : 0)->newQuery(), $query
);
$query->selectSub($q->toBase(), 'is_liked');
}
Please note you will need to add the use statement for Relation to the top of the class:
use Illuminate\Database\Eloquent\Relations\Relation;
You model could then look like:
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
protected $fillable = ['title', 'featuring_image', 'brief', 'body', 'seen_count'];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function someComments()
{
return $this->comments()->limit(Constants::COMMENTS_COUNT_LIMIT);
}
public function likes()
{
return $this->hasMany(Like::class);
}
/**
* Scope to add the "is_liked" flag.
*
* #param $query
*/
public function scopeIsLiked($query)
{
if (is_null($query->getQuery()->columns)) {
$query->select([$query->getQuery()->from . '.*']);
}
$relation = Relation::noConstraints(function () {
return $this->likes();
});
$q = $this->likes()->getRelationExistenceCountQuery(
$relation->getRelated()->where('user_id', auth()->check() ? auth()->user()->id : 0)->newQuery(), $query
);
$query->selectSub($q->toBase(), 'is_liked');
}
}
And your query would look something like:
$post = Post::with('categories', 'user')
->withCount('likes', 'comments')
->isLiked()
->find($post->id);
Hope this helps!
You can use Laravel casts:
Inside the each model you can add the following to cast a value, per example:
protected $casts = [
'isLiked' => 'boolean',
];
Rwd's answer gives a nice solution using scopes, but for laravel 5.4+ you could get away with aliasing the withCount() result and then casting it to boolean with a $cast variable on the model or an accessor (with accessor, you can only get snake case is_liked). This way we don't need to write complex scopes.
The model would be
class Post extends Model
{
// rest of model
protected $casts = ['isLiked'=>'boolean'];
public function likes()
{
return $this->hasMany(Like::class);
}
}
Then in your controller
$post = Post::with('categories', 'user')
->withCount(
[
'likes as likesCount', 'comments as commentsCount',
'likes as isLiked' =>function($query){
$query->where('user_id', auth()->check() ? auth()->user()->id : 0);
}
]
)
->find($post->id);
And now you get likesCount (int), commentsCount (int) and isLiked (boolean)

Laravel - Retrieving specific column from a releted query

I have 4 tables:
conversations
- id (pk)
- userId1 (fk)
- userId2 (fk)
users
- id (pk)
- name
- surname
.
.
.
- roleId (fk)
- userStatusId (fk)
roles
- id (pk)
- type (fk)
user_status
- id (pk)
- description (fk)
this are my models:
class Conversation extends Eloquent {
public function user1(){
return $this->hasOne('User', 'id', 'userId1');
}
public function user2(){
return $this->hasOne('User', 'id', 'userId2');
}
}
class User extends Eloquent {
public function role(){
return $this->hasOne('Role', 'id', 'roleId');
}
public function userStatus(){
return $this->hasOne('UserStatus', 'id', 'userStatusId');
}
// public function conversation1(){
// return $this->belongsToMany('Conversation', 'id', 'userId1');
// }
// public function conversation2(){
// return $this->belongsToMany('Conversation', 'id', 'userId2');
// }
}
class UserStatus extends Eloquent {
public $timestamps = false;
protected $table = 'user_status';
public function user(){
return $this->belongsToMany('User', 'id', 'userStatusId');
}
}
class Role extends Eloquent {
public $timestamps = false;
public function user(){
return $this->belongsToMany('User', 'id', 'roleId');
}
}
Now what I want to do is, for example, take all the conversations where the "userId1" (on conversations) is of a "user" who have the status "description" equal to "registered".
That's what I do:
Route::get('/', function(){
$conversation = Conversation::with(array('user1.userStatus' => function ($query){
$query->where('description', '=', 'registered');
}))->get();
foreach ($conversation as $conv) {
echo '<br \>';
echo $conv;
}
});
I expect to receive all the conversations record where the status of the userId1 is "registered" and nothing else... Instead what I receive are all the conversations records and, for each one, the user records and the records of the userStatus table (of this last I receive just the one who match the where clause and the ones who are not have a null value).
I know my english is terrible but I hope someone could understand and help me. Thanks!
All your relations are wrong. You need to read http://laravel.com/docs/eloquent#relationships and in your case it's:
class Conversation extends Eloquent {
public function user1(){
return $this->belongsTo('User', 'userId1');
}
public function user2(){
return $this->belongsTo('User', 'userId2');
}
}
class User extends Eloquent {
public function role(){
return $this->belongsTo('Role', 'roleId');
}
public function userStatus(){
return $this->belongsTo('UserStatus', 'userStatusId');
}
}
class UserStatus extends Eloquent {
public $timestamps = false;
protected $table = 'user_status';
public function user(){
return $this->hasMany('User', 'userStatusId');
}
}
class Role extends Eloquent {
public $timestamps = false;
public function user(){
return $this->hasMany('User', 'roleId');
}
}
Now, to retrieve only those conversations you want this:
Conversation::whereHas('user1', function ($q) {
$q->whereHas('userStatus', function ($q) {
$q->where('description', 'registered');
});
})->get();
or using this PR https://github.com/laravel/framework/pull/4954
Conversation::whereHas('user1.userStatus', function ($q) {
$q->where('description', 'registered');
})->get();
Also, to make it more verbose, you can wrap that code in a scope:
// User model
public function scopeRegistered($query)
{
$q->whereHas('userStatus', function ($q) {
$q->where('description', 'registered');
});
}
then:
Conversation::whereHas('user1', function ($q) {
$q->registered();
})->get();
You may try this:
$conversations = Conversation::whereHas('user1.userStatus', function ($query){
$query->where('description', '=', 'registered');
})->get();
This will return only the Conversation models whose related user1.userStatus is registered.

Resources