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.
Related
Suppose we got a collection from the database like this:
$projects = Projects::all();
Now, for example, I want to get the specific project using the specified column's value. For example, suppose any project has a unique pr_code, Now I want to get an item of collection that pr_code is 1234.
Note = I Know using Projects::where('pr_code', 1234)->first() But I didn't want this. I want use inside collection
How I could do that?
Collections also have where and first (and whereFirst ) functions. So you just need to change the order of functions in your chain: Projects::all()->firstWhere('pr_code', 1234)
Projects::all() queries all records in projects and returns them as a Collection of Projects models, this could be quite a performance hit.
Projects::where('pr_code', 1234)->get() will query for the specific projects and return a Collection of Projects models that have pr_code of 1234.
Projects::where('pr_code', 1234)->first() will do the same, but return the first as a Projects model.
I recommend naming the model Project rather than the plural Projects. The Laravel model is smart enough to know to use the plural name of the database table.
I have the following relationships:
Users have polymorphic many to many (morphedByMany) relationships with Customers, Locations, and Vendors.
Customers, Locations, and Vendors each have hasMany or hasManyThrough relationships with Datasets.
I'd like to get the Datasets that a given User has access to via its relationships. Also, some of the datasets through the different relationships might be the same, so I want a unique list.
I created the following method on my User model that works correctly (inspired by Laravel get a collection of relationship items):
public function accessibleDatasets()
{
$datasetsByCustomers = $this->customers()->with('datasets')->get()->pluck('datasets')->flatten();
$datasetsByLocations = $this->locations()->with('datasets')->get()->pluck('datasets')->flatten();
$datasetsByVendors = $this->vendors()->with('datasets')->get()->pluck('datasets')->flatten();
$datasets = $datasetsByCustomers;
$datasets = $datasets->merge($datasetsByLocations);
$datasets = $datasets->merge($datasetsByVendors);
return $datasets->unique();
}
Is there a "right way" or more efficient way to get this (besides using some sort of a reduce function for the merges)? Is loading the models, then flattening better? The associated values aren't going to change too often, so I can cache the results, but wanted to get some feedback.
The most efficient way would be by performing a single database query as Tim already mentioned.
Since you can cache the results and should you want to spare some lines of code I'd rather do:
public function accessibleDatasets()
{
$this->load('customers.datasets', 'locations.datasets', 'vendors.datasets');
return $this->customers->flatMap->datasets
->merge($this->locations->flatMap->datasets)
->merge($this->vendors->flatMap->datasets);
}
The merging operation should take care of duplicate keys but feel free to add a unique call if it doesn't.
I have 3 tables:
clinics
departments
clinics_in_departments
Using Query Builder:
$department = ClinicsInDepartment::whereIn('clinic_id',[1,2,3])
->join('departments', 'clinics_in_departments.department_id', '=', 'departments.id')
->get();
How can this be done with the relationship, and is it worth it?
If you look at the documentation of Laravel at the Many to Many section https://laravel.com/docs/5.6/eloquent-relationships#many-to-many it's already explained in there. If you're planning to keep using Laravel I would recommend using the best practises of Eloquent. It's easier to understand and read for other developers. It's always worth to make your product the best you can. It also gives possibilities to quickly extend and maintain your application.
All you need to do is to define a relationship in your model clinics
// second, third and fourth parameter could also be optional
function departments(){
return $this->belongsToMany('App\Clinics', 'clinics_in_departments', 'department_id', 'clinic_id');
}
To retrieve the data you can use
$clinics = Clinics::with('departments')->get();
// this would hold a list of departments for each clinic
To get exactly the same data extend the query to this
$clinics = Clinics::with('departments')->whereIn('clinic_id',[1,2,3])->get();
Because it's a Many to Many relationship you could also define a relationship for the model Departments and do exactly the same as mentioned above.
You can define a belongs to many relation inside Clinics model like below code
function departments(){
return $this->belongsToMany('App\Clinics', 'clinics_in_departments');
}
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
I created some models in Laravel extending the Eloquent class. In these models I added relations, like in this example:
//in the User class
public function group()
{
return $this->belongsTo('Group');
}
in this way I'm able to retrieve the group object for a specific user in this simply way:
User::find(1)->group
and this is nice. I need to fetch the entire table and display it in json format, so I do
User::all()->toJson()
I obtain the whole table, but I have the "group_id" column instead of the group object. Is there a simple way to include related object in JSON? I know that I can loop in the collection and manually add the object, but I think that should be bad practice because I need to do a lot of query, when this problem can be solved using a single query with a join. Any idea?
I believe eager loading is what you're looking for: http://laravel.com/docs/eloquent#eager-loading
User::with('group')->get()->toJson()
Edit: fixing call to get() from all()
Soved!
User::with('group')->get()->toJson()