Laravel Eloquent Lazy Eager Load Count - laravel

I'm ideally looking for a function like
load('relationship')
but which loads a count in the same way
withCount('relationship')
works for eager loading.
I'm thinking it is going to be called loadCount('relationship')

loadCount() is available since Laravel 5.8
$post->loadCount('comments');
$post->comments_count;
Docs

As of Laravel 5.2, this functionality is built-in.
Provided you have a hasMany relationship between Post and Comment, do:
<?php
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
You can even eager load relationships count by default by declaring this in your model:
<?php
// Post model
protected $withCount = ['comments'];

This solution works great for me:
Create a new hasOne relationship on the related model and add a raw select to the query for the count. For example, if you want to eager load the number of tasks for a given user, add this to the User Model:
public function taskCount()
{
return $this->hasOne('App\Task')
->selectRaw('user_id, count(*) as count)
->groupBy('user_id');
}
And then eager load the count like this:
$user = User::where('email', $email)->with('taskCount')->first();
And access the count like this:
$taskCount = $user->task_count->count;

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

getting the latest item of the pivot relationship

I have a pivot relationship as below:
public function users()
{
return $this->belongsToMany(User::class,'pet_owners','pet_id','owner_id');
}
now in my controller i want to get the latest item of this pivot table which is sorted by created_at field . here is how i get it now :
$pets = Pet::with('users')->get();
return new PetResource($pets);
here I want to show all pets with the latest user entered for that pet in the pivot table
You can use this way,
$pets = Pet::whereHas('users', function($q){
$q->orderBy('id', 'DESC');
})->get();
return new PetResource($pets);
// return latest record of pivot table
Limiting eager loads is unfortunately not easily done with Eloquent. There are many issues related to this on the Github repo. You can read this thread for an overview.
One of Laravel's contributors released a package staudenmeir/eloquent-eager-limit to make it easier to limit eager loads. You can install it with:
composer require staudenmeir/eloquent-eager-limit:"^1.0"
You need to add the \Staudenmeir\EloquentEagerLimit\HasEagerLimit; trait to both your Pet and User models. Then you'll be able to use the limit method on a query inside a with method on your model:
$pets = Pet::with(['users' => function ($query) {
return $query->latest()->limit(1);
}])->get();

Retrieve all columns from table A and some from table B using laravel eloquent

I am trying to retrieve the thumb image path by joining the images table to the listing table. As such, I have the following query in my controller.
$listings = Listing::select('listings.*, images.path as image_path')
->where('listings.ownerid', '=', $ownerid)
->leftJoin('images', 'listings.thumbId', '=', 'images.id')->get();
After testing out the function, the query fails since laravel interprets the query as
select `listings`.`*, images`.`path` as `image_path` from `listings` left join `images` on `listings`.`thumbId` = `images`.`id` where `listings`.`ownerid` = 1)
Notice the asterisk (*) is joined with the ", images" word making it '*, images'.
The query works fine without laravel's odd typo. How does one fix this issue?
You need to do one change in your query. You are passing raw select fields so you need to use selectRaw() instead of select(). Like
$listings = Listing::selectRaw('listings.*, images.path as image_path')
->where('listings.ownerid', '=', $ownerid)
->leftJoin('images', 'listings.thumbId', '=', 'images.id')->get();
check by try above query.
I suggest you to use Laravel Eloquent Relationships feature. Since your code above is more like Query Builder rather than Eloquent. Let's see the example bellow:
You will have 2 Models, 1 for each table (listings, images):
App\Listing Model:
<?php
...
use App\Image;
class Listing extends Eloquent {
...
protected $table = 'listings';
// define Eloquent Relationship of Listing model to Image model
public function image() {
return $this->belongsTo(Image::class, 'thumbId');
}
...
}
App\Image Model:
<?php
...
use App\Listing;
class Image extends Eloquent {
...
protected $table = 'images';
...
// define Eloquent Relationship of Image model to Listing model
public function listings() {
return $this->hasMany(Listing::class, 'thumbId');
}
}
So how to get the data?
// get all listing data
$listings = Listing::all();
// loop through the data
foreach ($listing as $listing) {
dump($listing->id);
// because we have define the relationship, we can access the related data of image
dump($listing->image->path);
// call $this->image will return related Image model
dump($listing->image);
}
You can see Laravel official documentation for more example and explanation.
Hope it helps.

Is there an Eloquent way of doing a leftjoin in Laravel 5.4

Is there a eloquent way to do a left join in Laravel?
We'd like to get all games and fill in the progress for each one if it exists in user_games.
Right now we've written the following solution, however this isn't eloquent at all, which we like it to be.
public function usergames($user_id) {
return DB::table('games')->leftJoin('user_games', function ($join) use ($user_id) {
$join->on('user_games.game_id', '=', 'games.id')->where('user_games.user_id', '=', $user_id); })->get();
}
DB model:
Thanks in advance!
A way to do this without you actually writing a left/inner join is to use the eloquent relationships.
In your case you will have 2 model classes: User and Game
class User extends Model {
public function games() {
return $this->belongsToMany(App\Game::class);
}
}
Now, you can access the user's games like so:
$user = App\User::find($user_id);
$usergames = $user->games; // Illuminate\Support\Collection
If you want to get a list of users with games, then look into eager loading. That would look something like this:
User::with('games')->get();
This way, Eloquent will know to lazy load the relationship meaning it will only run 2 queries. One to grab the users. and one to grab the games associated with the user, and then make them available for you in the 'games' property of the user object.

Database relation with Laravel Eloquent ORM

I'm new to Laravel and I'm stuck. This is what I am struggling with:
$questions = Question::find($id)->quiz(); // this code retrieves data from
// the table using the primary key
// in the table. The is a parameter
// that is passed via get.
This is what I have right now:
$questions = Question::where('quiz_id', '=', $id)->quiz();
This is the error I get:
Call to undefined method Illuminate\Database\Query\Builder::quiz()
What I want to do:
I want to run a query to get data from my database table using the foreign key in the table not the primary key, I also want to be able to use relations with this as seen from what I tried to do above.
Edit: Added the Question Model
<?php
class Question extends Eloquent{
protected $table = 'quiz_questions';
public function quiz()
{
return $this->belongsTo('Quiz');
}
}
Calling the quiz() function from Question::find($id)->quiz() will return a Query Builder instance allowing you to query the parent of the Question, its not going to return any data at that point until you call ->get() or another method that actually executes the query.
If you're wanting to return all the questions belonging to a certain quiz then you can do it like this.
$questions = Question::where('quiz_id', $id)->get();
This will return an Eloquent\Collection of the results for all questions with a quiz_id that is equal to $id.
If you've setup the relations between the Quiz and Questions then you can also do this using the Laravel relations.
$quiz = Quiz::findOrFail($id);
foreach($quiz->questions as $question)
{
// Do stuff with $question
}
Laravel will automagically pull Questions from the database that belongTo the Quiz you've already got from the database, this is known as eager loading http://laravel.com/docs/4.2/eloquent#eager-loading
Wader is correct, just calling where() will not execute your query. You either call get() and get an iterable result or use first() if you only want one result.
$quiz = Question::where('quiz_id', '=', $id)->first()->quiz();

Resources