Using Model Relationship in a Single Query - php

Consider the following:
$posts = $this->model->newQuery()
->whereIn('user_id', $user->following) // specifically this line
->orWhere('user_id', $user->id)
->get();
The problem with the above is that there are two queries:
Get following: $user->following
Get posts: Above
This would be much more efficient with the use of a subquery, however, I cannot actually remember the correct way to do it...
I have tried all of the following:
// This was a long-shot...
...->whereIn('user_id', function ($query) use ($user) {
$query->raw($user->following()->toSql());
});
// This works but pretty sure it can be done better with eloquent...
...->whereIn('user_id', function ($query) use ($user) {
$query->select('follow_id')
->from('user_follows')
->where('user_id', $user->id);
});
Is there a way that this can be achieved by using the previously defined relationship $user->following() instead of manually defining the relationship query like the last example above?
Reference
The following relationship is defined as follows:
/**
* Get the users that the user follows.
*/
public function following()
{
return $this->belongsToMany('SomeApp\User\Models\User', 'user_follows', 'user_id', 'follow_id')
->withTimestamps();
}

Use this:
->whereIn('user_id', $user->following()->getQuery()->select('id'))

Related

How to query many to many relation tables in laravel?

i am trying to query many to many relation for my get api call. i have three table as shown here but i am not using pivot table.
This is my Projects model class and this the function
public function projectRewd()
{
return $this
->belongsToMany('App\Rewards','rewards','project_id','reward_id');
}
And this is my Rewards model class and function
public function projectShip()
{
return $this->belongsToMany('App\Shipping_location','shipping_location','projects_id','rewards_id');
}
This is my api controller function
Route::get('projects/{id}', function($id) {
$proj = Projects::whereHas('projectRewd', function($q)
{
$q->where('id', $id);
});
return $proj;
});
i am using this link for api call
http://localhost:8000/api/projects/1
i want to extract rewards data and shipping_location data associate with project_id.
i am getting this error
"message": "Object of class Illuminate\\Database\\Eloquent\\Builder could not be converted to string"
i check and tried all related error from different post.
i also search and tried many technique. Cant solve my problem.
Please suggest me how to do this??
can i do this type of query in larvel without using pivot table??
You are getting Builder model because you forgot to add ->first() or ->get().
You should write:
$proj = Projects::whereHas('projectRewd', function($q){
$q->where('id', $id);
})->first();
Your closure-based controller returns your query-builder object. Not a project. You need to retrieve results from the query by fetching e.g. the first result (->first()) or all (->get()).
Route::get('projects/{id}', function($id) {
$proj = Projects::whereHas('projectRewd', function($q)
{
$q->where('id', $id);
})->first();
return $proj;
});
Referencing $id:
The reason why $id is unknown, is that the closure doesn't know about it.
You can pass it to the closure using use(...).
Route::get('projects/{id}', function($id) {
$proj = Projects::whereHas('projectRewd', function($q) use ($id)
{
...
Further:
Your whereHas query looks incorrect to me:
$q->where('id', $id);
Apparently $id is the project id. But the 'id' column in projectRewd is the primary key of projectRewd (unless you have modified the defaults).
I assume you want to query all projects that have at least one projectRewd:
Route::get('projects/{id}', function($id) {
$proj = Projects::has('projectRewd')->first();
return $proj;
});
And if you want to eager load the joined tables:
Route::get('projects/{id}', function($id) {
$proj = Projects::with('projectRewd. projectShips')->has('projectRewd')->first();
return $proj;
});

Laravel Eloquent, multiple schema query

I have the below query (simplified):
$q = ModelOne::with('relation_one', 'relation_two')
->whereHas('relation_three', function ($q) {
$q->where('object', 'Obj1');
})
->whereHas('relation_four', function ($q) {
$q->where('object', 'Obj2');
})
->get();`
It loads the relation_one and relation_two relationships fine, I also need to load another relationship per row, either relation_three or relation_four depending on the value of ModelOne->object.
The issue I am having is that ModelOne is from schema1 and the tables used in relation_three & relation_four are from schema2.
Both models are set up correct with their individual protected $connection and protected $table variables.
The error I am recieving is that the tables for relationship_three or relationship_four does not exist as the sub-query is checking the wrong schema.
Can anyone suggest how to fix this? Have had a look through the docs but couldn't find a solution.
Maybe not the most elegant solution but got this working by calling relationships and joining as follows:
$q = ModelOne::with('relation_one', 'relation_two')
->with(['relation_three' => function ($q) {
$q->leftJoin(
'schema1.model_one',
'table_three.id',
'=',
'model_one.object_id'
)
->where('object', 'Obj1');
}])
->with(['relation_four' => function ($q) {
$q->leftJoin(
'schema1.model_one',
'table_four.id',
'=',
'model_one.object_id'
)
->where('object', 'Obj2');
}])
->get();`
If anyone can suggest some improvements or a more efficient way to do this please let me know
I would suggest separating the different databases relations to different fields, at least. This way you can then load both (as suggested in comments) and differentiate the logic within controller/model code.
Also, I guess you'll need to define the connection name on the Model level, if not done yet:
class Model_Two_Relation {
protected $connection = 'your-database-name-from-config';
}
You also might want to specify the connection within the relation join condition:
$q = ModelOne::with('relation_one', 'relation_two')
->whereHas('relation_three', function ($q) {
$q->from('resources.one')->where('object', 'Obj1');
})
->whereHas('relation_four', function ($q) {
$q->from('resources.two')->where('object', 'Obj2');
})
->get();
Links: http://fideloper.com/laravel-multiple-database-connections

How to fetch users not assigned to a particular role in Laravel [duplicate]

In Laravel we can setup relationships like so:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Allowing us to to get all items in a pivot table for a user:
Auth::user()->items();
However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.
Is there a simple way to do this?
Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of has)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
this works correctly for me!
For simple "Where not exists relationship", use this:
User::doesntHave('Role')->get();
Sorry, do not understand English. I used the google translator.
For simplicity and symmetry you could create a new method in the User model:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
To use call:
Auth::user()->availableItems();
It's not that simple but usually the most efficient way is to use a subquery.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
If this was something I did often I would add it as a scope method to the Item model.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Then use that later like this.
$items = Item::whereNotRelatedToUser($user_id)->get();
How about left join?
Assuming the tables are users, items and item_user find all items not associated with the user 123:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
this should work for you
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);
Ended up writing a scope for this like so:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
And then call:
Items::avail()->get();
Works for now, but a bit messy. Would like to see something with a keyword like not:
Auth::user()->itemsNot();
Basically Eloquent is running the above query anyway, except with a = instead of a <>.
Maybe you can use:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Source: http://laravel.com/docs/4.2/queries#advanced-wheres
This code brings the items that have no relationship with the user.
$items = $this->item->whereDoesntHave('users')->get();

Sorting users through relation in laravel

i want to sort the users through voornaam(firstname). but im getting the data via a relation.
How do i make my query so that, the relation users are sorted by firstname by alphabet
my function:
public function sortfirstname($id) {
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get()->all();
//This query ^^
$toernooi = Toernooi::findOrFail($id);
dd($ingeschrevenspelers);
return view('adminfeatures.generatespelerslijst', compact('ingeschrevenspelers', 'toernooi'));
}
What i want to sort
any help is appreciated
thanks in advance
Writing code in your own language doesn't make it very easy for other developers to understand your code.
That being said, you can try the orderBy() method on your relationship
In your model where you define the relationship:
public function relationship()
{
return $this->belongsTo(SomeClass::class)->orderBy('name', 'DESC');
}
Don't fire all() function at the end thus obtaining a Collection instance of result
//query without the all function
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get();
//
$ingeschrevenspelers = $ingeschrevenspelers->sortBy('users.firstname');
An alternative to Jordy Groote's answer if you do not want to modify the Model class itself, you can query it with a closure.
$ingeschrevenspelers = UserToernooi::with(['users' => function($q) {
$q->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();
Reference: https://laravel.com/docs/5.3/eloquent-relationships#constraining-eager-loads
Sidenote: I don't think you need a ->all() when you already did a ->get()
$ingeschrevenspelers = UserToernooi::with(['users' => function($query){
$query->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();

Laravel Multiple Models Eloquent Relationships Setup?

I have 3 models
User
Pick
Schedule
I'm trying to do something like the following
$picksWhereGameStarted = User::find($user->id)
->picks()
->where('week', $currentWeek)
->first()
->schedule()
->where('gameTime', '<', Carbon::now())
->get();
This code only returns one array inside a collection. I want it to return more than 1 array if there is more than 1 result.
Can I substitute ->first() with something else that will allow me to to return more than 1 results.
If not how can I set up my models relationship to allow this to work.
My models are currently set up as follow.
User model
public function picks()
{
return $this->hasMany('App\Pick');
}
Schedule model
public function picks()
{
return $this->hasMany('App\Pick');
}
Pick model
public function user()
{
return $this->belongsTo('App\User');
}
public function schedule()
{
return $this->belongsTo('App\Schedule');
}
Since you already have a User model (you used it inside you find method as $user->id), you can just load its Pick relationship and load those Picks' Schedule as follows:
EDIT:
Assuming you have a schedules table and your picks table has a schedule_id column. Try this.
$user->load(['picks' => function ($q) use ($currentWeek) {
$q->join('schedules', 'picks.schedule_id', '=', 'schedules.id')
->where('schedules.gameTime', '<', Carbon::now()) // or Carbon::now()->format('Y-m-d'). See what works.
->where('picks.week', $currentWeek);
}])->load('picks.schedule');
EDIT: The code above should return the user's picks which have a schedules.gameTime < Carbon::now()
Try it and do a dump of the $user object to see the loaded relationships. That's the Eloquent way you want.
Tip: you may want to do $user->toArray() before you dump $user to see the data better.
EDIT:
The loaded picks will be in a form of Collections so you'll have to access it using a loop. Try the following:
foreach ($user->picks as $pick) {
echo $pick->schedule->gameTime;
}
If you only want the first pick from the user you can do: $user->picks->first()->schedule->gameTime
I think a foreach loop may be what you're looking for:
$picks = User::find($user->id)->picks()->where('week', $currentWeek);
foreach ($picks as $pick){
$pickWhereGameStarted = $pick->schedule()->where('gameTime', '<', Carbon::now())->get();
}
Try this and see if it's working for you

Categories