Laravel Eloquent - Combining queries to single one using relations - php

I'm working on a project where I'm using Laravel Eloquent to fetch all my data from my database.
I'm storing basketball clubs, teams and players in different tables.
Now I want to select all basketball players that belong to a team that belongs to a club.
I created a relation in the Club model:
public function basketballPlayers()
{
return $this->hasManyThrough('App\Models\Basketball\BasketballPlayer','App\Models\Basketball\BasketballTeam','id','team_id');
}
And a user relation in the BasketballPlayer model:
public function user()
{
return $this->hasOne('App\User','id','user_id');
}
Now when I execute the following command, DB::getQueryLog() returns 3 Queries.
Club::findOrFail($club_id)->basketballPlayers()->with('user')->get();
But when I execute the following command without using relations, DB::getQueryLog() returns only 1 query.
$players = BasketballPlayer::Join('basketball_teams','basketball_teams.id','=','basketball_players.team_id')
->join('users','users.id','=','basketball_players.user_id')
->where('basketball_teams.club_id','=',$authUser->club_id)
->get(['basketball_players.*','users.firstname','users.lastname','basketball_teams.name']);
I don't need all the data from the club and the user (see ->get(...) in the Join query).
Is it possible to achieve the same result like in the "long" manual Join-query using the much cleaner and simpler Eloquent relation approach?
Thanks for your help!

You can read less data using with (not all user columns as example) by this
Club::findOrFail($club_id)
->basketballPlayers()
->with('user:id,firstname,lastname') // <- here is change
->get();
The id (and foreign keys) column is important. You should be also able to make nested with in following way (I write code from head - so please test it):
Club::findOrFail($club_id)
->with('BasketballPlayer:id,user_id')
->with('BasketballPlayer.user:id,firstname,lastname')
->get();

Related

Laravel recursive whereHas on BelongsToMany relationship

I have a model called User which has a BelongsToMany relationship on it called lineManagers(). This relationship returns a collection of User models. The nature of this setup allows for a parent-child style relationship to operate on multiple levels.
The BelongsToMany uses a table called line_manager_user which has a simple schema of mapping a user id to a line manager user id:
| user_id | line_manager_id |
User A -> lineManagers()-> User B
User C -> lineManagers() -> User D
Depending upon certain permissions, I want to be able to query this relationship to multiple levels for users who have a line manager with a specific users.id, potentially using a whereHas() but I'm aware that this could be quite a detriment to performance.
I had tried the below query but to no avail (the last section is the relevant part):
$query = User::query()
->with('lineManagers')
->orderBy('first_name')
->orderBy('last_name')
->havingEmploymentStatus(UserEmploymentStatus::EMPLOYED)
->whereHas('lineManagers.lineManagers.lineManagers', function (Builder $query) {
$query->where('id', $this->getAuthenticatedUser()->id);
});
I don't specifically want 3 levels, ideally the query would retrieve lineManagers until an empty collection is hit. This does need to be something that I done at query level rather than collection level unfortunately
If you made it a function that called itself until it drilled down to the empty collection you could do it. That wouldn't matter how many levels there are then
Check out https://www.sitepoint.com/laravel-blade-recursive-partials/
I wouldn't recommend doing it in the blade file, rather work their "Plain old PHP" code into a function

How to make Query between two tables in laravel

Hello I want to ask for how to make query in 2 tables to get the summary of value from table payments where type_id = id from types table and this is tables structure
this is the charts code
$chartـin_contract = Charts::create('bar', 'highcharts')
->title('My nice chart')
->labels($value_type->type)
->values([5,10])
->dimensions(0,500);
in labels i get the values from database but did't work with me
and this is the direct query
$value_type = DB::select('select type, value from payments,types where payments.`type_id` = types.id');
I want view the data that came from database in chart I am using laravel chart package
Please review Eloquent Relationships in the Laravel docs - https://laravel.com/docs/5.5/eloquent-relationships
The relationship between the two tables is defined (as in a one-to-one, one-to-many or many-to-many relationship) and then you will be able to query those relationships.
For this you need to establish the Relationship in tables.
NOTE: I am assuming that your task may have multiple payments
In your tasks model its one to many relation.
Task.php (Model add the following)
public function payments(){
return $this->hasMany(Payments::class);
}
And in your payments model its many to one relation.
Payments.php (Assuming)
public function task(){
return $this->belongsTo(Task::class);
}
Think now you want to get all the payments for a task. Let me show the eg: in command line
php artisan tinker
$task = App\Task::where('id',1)->get(); //Think that you have first task
$task->payments; //You can call the relation ship established condition function
Think you are browising for payment and want a task then
php artisan tinker
$payment = App\Payment::where('id',1)->get();//find the payment with id 1, just for the sake of example
$payment->task; //This will be the task() function that you have written int the model

Concept of table relations in Laravel

I have two tables:
Cards
Notes
Each Card has multiple Notes. So there is a relation between them like this:
class Card extends Model {
public function notes ()
{
return $this->hasMany(Note::class);
}
}
Ok well, all fine.
Now I need to understand the concept of these two lines:
$card()->$notes()->first();
and
$card()->$notes->first();
What's the difference between them? As you see in the first one $note() is a function and in the second one $note isn't a function. How will they be translated in PHP?
The first one points out to the card table and the second one points out to the notes table, right? or what? Anyway I've stuck to understand the concept of tham.
I don't know about $ before the $notes in your code but if you trying to say something like this.
1- $card->notes()->first();
2- $card->notes->first();
In the code in line 1, first you have a $card model and then you wanted to access all notes() related to that $card, and because of adding () after notes you simply call query builder on notes, show you can perform any other database query function after that, something like where, orderBy, groupBy, ... and any other complicated query on database.
But in the second one you actually get access to a collection of notes related to that $card, we can say that you get all related notes from database and set it into laravel collections and you are no more able to perform database query on notes.
Note: because laravel collections have some methods like where(), groupBy(), whereIn(), sort(), ... you can use them on the second one, but in that case you perform those methods on collections and not database, you already get all results from database

laravel Eloquent join and Object-relationship mapping

Ok so i'm kind of newish to eloquent and laravel (not frameworks tho) but i hit a wall here.
I need to perform some queries with conditions on different tables, so the eager load (::with()) is useless as it creates multiples queries.
Fine, let use the join. But in that case, it seems that Laravel/Eloquent just drops the concept of Object-relationship and just return a flat row.
By exemple:
if i set something like
$allInvoicesQuery = Invoice::join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();
and then looping such as
foreach ($allInvoicesQuery as $oneInvoice) {
... working with fields
}
There is no more concept of $oneInvoice->invoiceFieldName and $oneInvoice->contact->contactFieldName
I have to get the contacts fields directly by $oneInvoice->contactFieldName
On top of that the same named columns will be overwrited (such as id or created_at).
So my questions are:
Am i right assuming there is no solution to this and i must define manually the field in a select to avoid the same name overwritting like
Invoice::select('invoices.created_at as invoice.create, contacts.created_at as contact_create)
In case of multiple joins, it makes the all query building process long and complex. But mainly, it just ruins all the Model relationship work that a framework should brings no?
Is there any more Model relationship oriented solution to work with laravel or within the Eloquent ORM?
Instead of performing this join, you can use Eloquent's relationships in order to achieve this.
In your Invoice model it would be:
public function contact(){
return $this->belongsTo('\App\Contact');
}
And then of course inside of your Contact model:
public function invoices(){
return $this->hasMany('\App\Invoice');
}
If you want to make sure all queries always have these active, then you'd want the following in your models:
protected $with = ['Invoice']
protected $with = ['Contact'];
Finally, with our relationships well defined, we can do the following:
$invoices = Invoice::all();
And then you can do:
foreach($invoices as $invoice)[
$invoice->contact->name;
$invoice->contact->phone;
//etc
}
Which is what I believe you are looking for.
Furthermore, you can find all this and much more in The Eloquent ORM Guide on Laravel's site.
Maybe a bit old, but I've been in the same situation before.
At least in Laravel 5.2 (and up, presumably), the Eloquent relationships that you have defined should still exist. The objects that are returned should be Invoice objects in your case, you could check by dd($allInvoiceQuery); and see what the objects are in the collection. If they are Invoice objects (and you haven't done ->toArray() or something), you can treat them as such.
To force only having the properties in those objects that are related to the Invoice object you can select them with a wildcard: $allInvoicesQuery = Invoice::select('invoices.*')->join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();, assuming your corresponding table is called invoices.
Hope this helps.

laravel one to many(select all rows from second table)

i want to select all services rows from servs table
_____ i have two tables users with model(User) ..... and servs with model(servs) ... . uwant to select all rows from servs when it auth User
How can i do that ???
public function postserv(){
$serv = User::find(Auth::user()->id)->servs;
$serv = $serv->first();
return $serv->serv_id;
}
I'm not sure of the model name, but it should be something like Serv::all()
Your question is very vague and it's hard to determine what's going on in your project but I'll give it a shot.
If you want to select all rows of a model use the following:
Services::all()
Whilst that is what you're explicitly asking for, your question seems to pertain to a relationship where you select all services for a user.
User::find(Auth::user()->id)->servs()->get();
This will return all services that are joined to the authorised user, on the note of naming conventions you should make your relations more readable.
Also note that you must have your relationships set up in your Eloquent models else the above code would fail.
In future try to add a little more detail for your questions, there is more information about relationships in the Eloquent ORM on the Laravel website.
ModelName::all();
Returns all rows from a model/table.

Categories