Laravel: Adding 'lowest' attribute to an Eloquent collection - php

I have a query returning offers by a given key. This is all functioning well, but I need my application to add a custom key which tells the frontend which offer is the lowest of them all (in price).
How do I achieve this?
My current query:
$offers = Offer::with('user:id,fname', 'review')
->where('isbn', $isbn)
->where('match_id', null)
->orderBy('price', 'ASC')
->paginate(5);

Related

What is the right way to get lowest prices in another table with laravel eloquent

I have these tables to query lowest prices for events tickets;
events, products, product_inventories. I want to list all events with their lowest prices. How can i do it with laravel(every tickets are inside the products table and there are a few price for each ticket in product_inventories).
I tried a few things but i think they are not the best solutions. Here they are:
I defined this function in event model.
public function getLowestPriceAttribute()
{
return ProductInventory::whereIn('product_id',
Product::where('event_id', $this->id)
->pluck('id'))
->min('price');
}
I can get the lowest price with this query, but there are too many queries like in this photo.
Another thing I've tried that I have not called the attribute but I've changed the query in controller. It runs just one query with this code but i don't think it's right in laravel.
$events = Event::leftJoin('products', 'event_id', 'events.id')
->leftJoin('product_inventories', 'product_inventories.product_id', 'products.id')
->groupBy('events.id')
->get(['events.*', \DB::raw('min(product_inventories.price) as min_price')]);
Can you help me that how i can optimize this code?
--A question not that much significant: I got 2 queries for each attribute run. How can I reduce it to one?
Do you have a relationship method setup within ProductInventory?
public function product()
{
return $this->belongsTo(Product::class);
}
If you do have it, you can do it like this
return ProductInventory::query()
->with('product', function ($q) {
return $q->where('event_id', $this->id);
})
->min('price');
Hope this suit your need.

Adding custom collection to the eloquent within Laravel Query Builder

I have a query and I want to add a collection using Laravel query builder.
Hotel::addSelect([
'selectableLocations' => AllLocations::orderBy('name')->get()
])
->with('location')
->get();
Well, this returns:
SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters (SQL: select [{"id":1,"name":"John Avenue"},{"id":4,"name":"Ontirio Village"},{"id":2,"name":"Rovie"},{"id":3,"name":"Movie Lot"}] from dogs limit 100 offset 0)
I know this may seem like an anti-pattern but the reason I need this is because I have a datatable and want to show a select (dropdown) with AllLocations so that user can change in the datatable.
My idea is that I can do $dog->selectableLocations to see all locations. Because if I don't do it like this, it will query for each row individually.
Is there a way to do that?
If I could achieve it like this, that'd be perfect.
$selectableLocations = AllLocations::get();
$hotels = Hotel::addSelect([
'selectableLocations' => $selectableLocations
])
->with('location')
->get();
EDIT:
Because if I don't do it like this, it will query for each row individually.
Since your primary concern is multiple queries, you could avoid db calls if you implement some sort of caching. Why not include the list as a custom attribute, load collection from cache? Like so:
public function getAllLocationsAttribute()
{
return Cache::remember('all_locations', 30, function(){ //add remove seconds as required
return AllLocatiobs::get();
});
}
How about getting merging the two collections?
$hotels = Hotel::get();
$selectableLocations = AllLocations::get();
$hotels->put('selectableLocations', $selectableLocations);
Now, $hotels->selectableLocations will be a collection of AllLocations.

Laravel - Convert array back to a collection

I am trying to get a collection of categories only if they are in the products table. My categories table has 300 items. I only want a collection if a category is attached in the products table. The $categories collection should only result in about 10 categories because there are only about 10 products that have different category_ids
$products = DB::table('products')->groupBy('category_id')->get();
foreach($products as $product){
$categories[] = DB::table('categories')->where('id', '=', $product->category_id)->first();
}
$categories = collect($categories)->all();
Maybe I am going about this wrong and should use a different query builder method?
The end result $categories does get the results I am after but in my blade I get the "Trying to get property of non-object" error.
If using $categories in your blade file as a collection, you will need to remove the ->all() method.
->all() is converting the collection into an array after it is being created:
$categories = collect($categories);
You get Trying to get property of non-object because one of
DB::table('categories')->where('id', '=', $product->category_id)->first();
return null value.
You can fix it this way
$products = DB::table('products')->groupBy('category_id')->get();
$categories = collect();
foreach($products as $product){
$category = DB::table('categories')->where('id', '=', $product->category_id)->first();
if ($category) {
$categories->push($category);
}
}
If you want to get collection instance you must be use collect() helper method with array argument.
For example
collect($categories); // is_array(categories) is true
You are doing many request in foreach. That is not right way. Instead it you can achieve collection instance doing only 2 request.
$categoryIds = DB::table('products')->pluck('category_id')->toArray();
$categories = DB::table('categories')->whereIn('id', $categoryIds)->get();
See docs https://laravel.com/docs/5.8/queries#retrieving-results
This can be done with one simple eloquent query. There's no need to use query builder unless you're doing something overly complex (in my opinion).
whereHas() will only return Categories that have Products.
Categories::with('products')->whereHas('products')->get();
As long as the relationships are correct on the models the above is what you're looking for. As pointed out in the comments, you need models and relationships. Laravel uses the MVC pattern, and the first letter stands for model so I'm going to guess you're using them. If not let me know and I can help set those up because you should be using them.
And if you NEED to use the query builder clean that code up and use something like this so you don't have to worry about recollecting. Also check out the hydrate() method to change these generic class instances into instances of the Categories model.
DB::table('categories')->whereIn('id', function($q){
$q->select('category_id')->from('products');
})->get();

Laravel Eloquent query using where

I have two models. A status and a SaleDetails.
Status
------
id
name
slug
I didn't define any relationship from Status to SaleDetails.
SaleDetails
-----------
id
id_statuses //connects it to the status table
id_products
price
qty
in the Model:
public function status(){
return $this->belongsTo('App\Status', 'id_statuses', 'id');
}
So what I am trying to do here is get all orders that whose status match a certain slug.
E.g dd($this->sale_details->where('status.slug', 'pending')
With what I have using this:
$orders = $this->sale_details->with('product', 'status')->today()->get();
I should be able to filter them based on the status slugs in the frontend, but I want to just do it straight from here.
What's the best way to go about this?
You should use whereHas() it allows you to specify additional filters for the related model to check.
To apply the where condition you can use whereHas() like,
$status = "pending";
$this->sale_details->whereHas('status', function($q) use ($status){
$q->where('slug', $status);
})->get();
i think, better than using query builder if you want fast proccess

Laravel/Eloquent - Order By a Model's Relationship Without Using Table Names

I have a Person eloquent model that belongsTo an Address. My Laravel version is 4.2.5 and I am using PostgreSQL.
class Person extends Eloquent {
public function address() {
return $this->belongsTo('Address');
}
}
My aim is to get a collection of Person resources that are sorted by the address_1 field of their related Address model.
I can accomplish this by referencing table names as show below, but I want to do it instead with Eloquent relationships, since I do not want to deal with tables for abstraction purposes.
Person::join('addresses', 'persons.id', '=', 'addresses.person_id')
->orderBy('address_1', 'asc')->get();
I have attempted the following Eloquent method without success.
Person::with('address')->whereHas('address', function($q)
{
$q->orderBy('address_1', 'asc');
})->get();
This query fails with the error message:
Grouping error: 7 ERROR: column \"addresses.address_1\" must appear in the
GROUP BY clause or be used in an aggregate function
In response to this, I tried adding this line above the orderBy statement which causes the query to succeed, but the ordering has no effect on the resulting Person collection.
$q->groupBy('address_1');
I would much appreciate a solution where I do not have to reference table names if it is possible. I have exhausted all resources on this subject, but surely this is a common use case.
Here you go:
$person = new Person;
$relation = $person->address();
$table = $relation->getRelated()->getTable();
$results = $person->join(
$table, $relation->getQualifiedForeignKey(), '=', $relation->getQualifiedOtherKeyName()
)->orderBy($table.'.address_1', 'asc')
->get();

Categories