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.
Related
My goal is to retrieve all of a user's 'items' and display then in my view grouped by their 'status'. There are 4 possible statuses, each with their own <div> on the page containing the items' info. After some poking around I believe I need to use the groupBy() method like so:
$items = Item::ownedBy( Auth::id() )->groupBy('status')->get();
This does seem to do some sort of grouping, but when I iterate over the collection I get a max of 4 results, one for each status. This doesn't really solve my problem as I need to display all of a user's items for each status, not just one. I must be missing something here, I'd really like to avoid making a query for each status and displaying them that way. I suppose I could filter the collection by status and create 4 new collections, but isn't this what groupBy() is supposed to do?
You can do that very easy with laravel and Collections. Collections have powerful API and a lot of handy methods, to easily achieve what you want.
Your mistake here is that you are calling groupBy on the QueryBuilder object which returns only groupped by records from the database. Instead you must select all of the records you need, then they will be returned as a collection. After that you can manipulate the collection as you wish. So what you need is:
$items = Item::ownedBy( Auth::id() )->get()->groupBy('status');
You can view all of the Colletion class useful methods here.
A few words before
I know that you can append variables to model arrays and json representations by using the protected $appends = ["your", "vars", "here"]; array. But imagine the following situation:
The situation
Our use case would be a fictional game or similiar:
Imagine that we have a User model that holds simple information about an (human) user, like the full name, address and so on.
Now, we also have a Faction model that represents the faction/origin/guild/... of this user.
The Faction model is eager-loaded when retrieving users, because the Faction name is wanted almost every time when displaying the user information.
A User also has DailyStatistics, which holds some information about their daily scores (simple points would be enough).
The Clue
Because I want to know the points of the a faction, which is the sum of the user points, I thought about appending a new variable totalPoints.
The getTotalPointsAttribute function would look like this:
function getTotalPointsAttribute(){
return $this->users->sum->getTotalPoints();
}
The problem
Everytime when we retrieve a user now, the eager-loaded faction would also want to calculate the totalPoints attribute. That means, that we have a lot of overhead per user.
The question
Is there a way to avoid situations like this? Can I "conditionally" append variables? Are properties calculated when they are hidden?
I tried to wrap the totalPoints variable in a simple function, instead of an accessor instead. The problem is, that Frontend-Frameworks like VueJS would need access to the totalPoints variable (or to an endpoint to retrieve that value, but this solution is the least favorable).
I met this problem as I wanted to Appends on the fly but don't want this to auto-appends on any other Controller/Models (The other way is produce 2 Models for the same Table, which is difficult to maintain).
Currently I'm maintaining a Laravel 5.4 (Since they refuse to upgrade PHP5.6 to PHP7)
For Laravel 5.4 and below
Just add a closure after completed the query builder get()
->each(function ($items) {
$items->append('TotalPoints');
);
Source of original solutions: laravel-how-to-ignore-an-accessor
$openOrders = Order::open()->has('contents')
->get(['id','date','tableName'])
->each(function ($items) {
$items->append('TotalPoints');
);
Your model still contains the
public function getTotalPointsAttribute()
{
return $this->users->sum->getTotalPoints();
}
Now you can remove/comment out the the appends in your models :
protected $appends = [
'TotalPoints',
];
Alternatively, if you're on Laravel 5.5 and above, you could use the collection magic like so:
$openOrders->each->setAppends(['TotalPoints']);
Laravel 5.5 and above now have a Laravel 5.6 #Appending At Run Time
I'm working on octoberCMS(laravel framework), I have a doubt on retrieving relation model on where clause.
I have fetched a clothing_type record from "clothing type" model based on its primary key "id".
$clothing_type = ClothingType::where('id',post('clothingtype_id'))->where('status','Active')->first();
This "clothing type" model is related with "products" model, the relation is => each clothing type hasMany products.
Every thing works fine; Now my business logic has two cases, one is to get all the products of the clothing type and another is to get the first product of the clothing type. So I have used the $clothing_type->products to get all the products and $clothing_type->products->first() to get the first product.
Now I have to apply a condition for both the cases. The condition is that only the product whose status is "Active" should be fetched, hence
$products = $clothing_type->products->where('status','Active'); and$first_product_detail = $products->first();.
Every thing works as expected but how come the products are fetched without "get()" method. $clothing_type->products->where('status','Active')->get(); Since I'm new to relation I want to know how this works or is this a bad way to get records or improper assumption. But every thing works good.
$clothing_type = ClothingType::where('id',post('clothingtype_id'))->where('status','Active')->first();
if(count($clothing_type->products))
{
$products = $clothing_type->products->where('status','Active');
$first_product_detail = $products->first();
}
You are doing it the correct way. When you access the relationship as an attribute Eloquent automatically retrieves the records.
However, if you access the relationship as a method, you get the query itself, to which you can add your filters:
if(count($clothing_type->products))
{
$products = $clothing_type->products()->where('status','Active')->get();
$first_product_detail = $products->first();
}
This would solve your problems
(documentation is over here (see the first item))
Edit: Also note that the first method is not a method of Eloquent, but from Collection, which is pretty powerful!
Edit2:
I misread the part of your question where you want to know HOW this is possible. Both Eloquent and Collections have a where method. I assume you understand the working of the Eloquent one, but the one from Collection is pretty much the same (see documentation on the Collection where here)
I prefer the Eloquent one myself, because that limits the amount of records that is retrieved from the database. But if you need all the products (even the inactive ones) later on in your code, just use the Collection method to filter the active ones out
There is nothing to be afraid of...
first() and where()
are functions of both Illuminate\Database\Query\Builder as well as Illuminate\Support\Collection and all first does is limit the records to take 1 and then give you the first record. When you use Builder a query is made to get 1 record and 1 you use it on a collection, all records are first get() and then the first of those records is returned.
Here,
When you do,
$clothing_type->products, Laravel gives you a collection of products...
So...
$products is an object of Illuminate\Support\Collection
and
$products->first() calls for the first() function in that class.
Documentation on where and first methods of a collection...
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.
I want to ask if its clean possible to change the mapping process.
For example, if i have an database, with products and offers, so the relation is 1 product n offers.
I want to get on fetch an product with multiple offers not an collection of offers, instead of i want to get an class named "AggregateOffers" which calculates the highest the lowest price and includes the offers collection.
Did anyone know an good/clean solution?
For an better understanding, schema.org defines the relation between an product an multiple offers, within an aggregateoffer, so i want to abstract them, to generate at the end an json-ld.
My actually design looks like schema, as you can see i have an 1-n relation from Product to Offer, what i actually want to get when i read from the DB, is an extended relation.
For example:
Product -> getOffers (should not return an collection of offers, instead it should return an "AggregateOffers" which hold the collection of offers).
The only idea, is to update the "getOffers" function and return die Aggregation class, but this acts not really good on me.