Ordering of nested relation by attribute with laravel - php

Hi there i'm trying to sort a collection by attribute of the relation.
This is my model
class Song extends \Eloquent {
protected $fillable = ['title', 'year'];
public function artist(){
return $this->hasOne('Artist','id', 'artist_id');
}
}
class SongDance extends \Eloquent {
protected $table = 'song_dances';
protected $fillable = ['rating'];
public function dance(){
return $this->belongsTo('Dance', 'dance_id');
}
public function song(){
return $this->belongsTo('Song', 'song_id');
}
}
class Dance extends \Eloquent {
protected $fillable = ['name'];
public function song_dances(){
return $this->hasMany('SongDance','dance_id','id');
}
public function songs(){
return $this->belongsToMany('Song', 'song_dances', 'dance_id', 'song_id');
}
}
this is how far i'm by now:
$dance = Dance::find(1);
$songs = $dance->songs()
->with('artist')->whereHas('artist', function ($query) {
$query->where('urlName','LIKE','%robbie%');})
->where('song_dances.rating', '=', $rating)
->orderBy('songs.title','asc')
->where('songs.year', '=', 2012)
->get();
Yeah i just could add a ->sortBy('artist.name'); to the query, but he result-collection can be quite big (about 6000 items) therefore i would prefer a databased sorting.
is there a possibility to do this?

Since Eloquent's relations are all queried separately (not with JOINs), there's no way to achieve what you want in the database layer. The only thing you can do is sort the collection in your app code, which you've already dismissed.
If you feel you must sort it in the database then you should write your own join queries instead of relying on Eloquent's relations.

Related

Sort belongsToMany relation base on field in belongTo relation Laravel Eloquent

I have a scenario where User has a belongsToMany relation with PortalBreakdown, PortalBreakdown has a belongsTo relation with Portal. Portal has order column in it. I have a method listing_quota($id) in UserController which returns all breakdowns of the user. I want to sort these breakdowns based on order column of the portal. Below are the code of classes and a method I have tried.
class User extends Model {
protected $table = 'user';
public function listing_quota() {
return $this->belongsToMany('App\PortalBreakdown', 'user_listing_quota')->withPivot(['quota']);
}
}
class PortalBreakdown extends Model {
protected $table = 'portal_breakdown';
public function portal() {
return $this->belongsTo('App\Portal');
}
}
class Portal extends Model {
protected $table = "portal";
protected $fillable = ['name', 'description', 'order'];
}
Below is the method where I am trying to return sorted by order. I tried few things some of which can be seen in commented code but not working.
class UserController extends Controller {
public function listing_quota($id)
{
$user = User::with(['listing_quota' => function ($query) use ($id) {
// $query->sortBy(function ($query) {
// return $query->portal->order;
// });
}, 'listing_quota.portal:id,name,order'])->findOrFail($id);
// $user = User::with(['listing_quota.portal' => function ($q) {
// $q->select(['id', 'name',order']);
// $q->orderBy('order');
// }])->findOrFail($id);
return $this->success($user->listing_quota);
}
}
I also tried chaining orderBy directly after relation in Model class but that's also not working from me. Thank you in advance.
NOTE: I am using Laravel Framework Lumen (5.7.8) (Laravel Components 5.7.*)

how can we append two attributes by one function?

I want to add two appends variables in laravel model. by using one getVariableNameAttribute() function.
Is this possible?
MY motto is, I want to reduce SQL query load 2 times to into 1 time
class Like extends Model{
use HasFactory;
protected $fillable = ['like_id', 'like_type', 'liked', 'user_id'];
protected $appends = ['likes','dislikes'];
protected function getLikesAttribute($type)
{
return \DB::table("likes")
->select(\DB::raw("SUM(liked) as likes"))
->where('like_type',self::class)->where('like_id',$this->id)->first()->likes;
}
public function getDislikesAttribute()
{
return \DB::table("likes")
->select(\DB::raw("SUM(!liked) as dislikes"))
->where('like_type',self::class)->where('like_id',$this->id)->first()->dislikes;
}}
Combining both methods to return an object is possible.
protected function getVariableNameAttribute()
{
return \DB::table("likes")
->selectRaw("SUM(liked) as likes")
->selectRaw("SUM(!liked) as dislikes")
->where('like_type', self::class)
->where('like_id', $this->id)
->first();
}
$...->variable_name->likes
$...->variable_name->dislikes

AngularJS & Laravel 4.2 retrieving data from multip tables and joining them

I've recently started to work with Laravel and I just recently got help to insert the data into multiple tables from one controller. Now I'm trying to retrieve data from the database and populate the form if an edit is needed or if someone needs to look at the data to be able to confirm it is correct. I don't understand how I join the tables is this in the model in Laravel? Or do I have to create a join SQL query?
Travelbill.php Model
<?php
class Travelbill extends Eloquent {
protected $table = 'travelbill';
protected $primaryKey = 'TravelbillId';
protected $fillable = array('ResourceId', 'Destination', 'StartDay', 'StartTime', 'EndDay', 'EndTime', 'Invoice', 'TravelCompensation');
public $timestamps = true;
public function travelbill() {
return $this->belongsTo('User', 'ResourceId', 'ResourceId');
}
public function cost() {
return $this->hasOne('Cost', 'TravelbillId', 'TravelbillId');
}
public function allowance() {
return $this->hasOne('Allowance', 'TravelbillId', 'TravelbillId');
}
}
?>
Cost.php Model
<?php
class Cost extends Eloquent {
protected $table = 'cost';
protected $primaryKey = 'CostId';
// protected $fillable = array('TravelbillId', 'Description', 'DescriptionByHiq', 'SekInklMoms', 'SekExklMoms', 'SekInklMomsByHiq', 'SekExklMomsByHiq', 'Currency', 'ExchangeRate');
protected $fillable = array('TravelbillId', 'Description', 'DescriptionByHiq', 'SekInklMoms', 'SekMoms', 'SekInklMomsByHiq', 'SekMomsByHiq', 'Currency', 'ExchangeRate');
public $timestamps = false;
public function cost() {
return $this->belongsTo('Travelbill', 'TravelbillId', 'TravelbillId');
}
}
?>
Allowance.php Model
<?php
class Allowance extends Eloquent {
protected $table = 'allowance';
protected $primaryKey = 'AllowanceId';
protected $fillable = array('TravelbillId', 'DayAllowance', 'Breakfast', 'Lunch', 'Dinner');
public $timestamps = false;
public function allowance() {
return $this->belongsTo('Travelbill', 'TravelbillId', 'TravelbillId');
}
}
?>
Or where am i doing this join so if i get the travelbill it also gets the Cost and Allowance data with the request?
EDIT:
I need to get the data into my AngularJS form so if a person with id = 1 needs to edit his Travelbill he should be able to click a edit button and see the information ha has filled out.
The simplest form of data retrieval you can do here is to do the following:
$travelBills = Travelbill::with(['code','allowance'])->get();
This is Eager Loading and will perform three queries:
Load all the travel bills
Load all codes that have foreign keys matching all the travelbill ids and assign them to each travel model
Do the same with allowances
What you'll have in the end that every Travelbill model will already have an associated Code and Allowance model, allowing you to work like:
echo $travelBill->cost->SekInklMoms;
for one of the Travelbills you loaded. Note a couple of things in the first query:
The travelbills are not filtered, we are loading them all at this point.
We are doing it simply, not necessarily efficiently. I recommend first get comfortable with the relationships loading before get onto things like joins (which break the spirit of Eloquent ORM in any case)

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);
});
}

Laravel Eloquent ORM: Change model so left join is *always* performed

I have a very simple Eloquent Model:
class HelpdeskComment extends Model
{
protected $table = 'helpdesk_comment';
protected $guarded = ['id', 'helpdesk_topic_id'];
public function topic () {
return $this->belongsTo ('\\App\\Model\\HelpdeskTopic');
}
public function user () {
return $this->belongsTo ('\\App\\Model\\User');
}
}
This all works quite well when doing stuff like
$user = $helpdeskComment->user;
However, I would like to change the model so that the User table (specifically the "username" field) is always left-joined against the HelpdeskComment table, regardless of the query function called. Is there a simple/central way to achieve this or do I have to override each function (all(), etc.)?
Thanks for any ideas & pointers.

Categories