Take the following code snippet. I am loading the OrderDetail model with the relationship to Inventory:
// Load order details for live orders and inventory ids
$order_details = \OrderDetail::whereIn('order_id', $live_order_ids)
->whereIn('inventory_id', $inventory_ids)
->with('inventory')
->get();
Once the collection is loaded, I am doing the following processing:
// Deduct reserved stock from live un-picked orders
foreach ($order_details as $detail)
if (array_key_exists($detail->inventory->sku, $free_stocks))
$free_stocks[$detail->inventory->sku] -=
($detail->qty_ordered - $detail->qty_picked);
As you can see, from the loaded collection, I only need the following:
order_details.qty_ordered
order_details.qty_picked
inventory.sku
I'd like to optimise this and only load what I require, i.e. specify explcitly what columns I want to load from the main and related table.
How can I do this? When I tried the following, it doesn't seem to work:
// Load order details for live orders and inventory ids
$order_details = \OrderDetail::whereIn('order_id', $live_order_ids)
->whereIn('inventory_id', $inventory_ids)
->with('inventory')
->get(['qty_ordered','qty_picked','sku']);
// Load order details for live orders and inventory ids
$order_details = \OrderDetail::whereIn('order_id', $live_order_ids)
->whereIn('inventory_id', $inventory_ids)
->with(['inventory' => function($query) {
$query->addSelect(['id','sku']);
}])
->get(['qty_ordered','qty_picked']);
Any ideas?
You need to use select in query to get selected columns. Or if it will not work you need use laravel joins.
Also Please see select columns from queries in laravel official document.
http://laravel.com/docs/4.2/queries.
// Load order details for live orders and inventory ids
$order_details = \OrderDetail::whereIn('order_id', $live_order_ids)
->whereIn('inventory_id', $inventory_ids)
->with(['inventory' => function($query) {
$query->addSelect(['id','sku']);
}])
->select('qty_ordered','qty_picked')
->get();
Related
I'm having trouble with the whereIn() function.
I have two tables carts and items. Carts stores user IDs and item IDs, while the items table stores item IDs and item information.
What I would like to do is get all items that are in the cart table with in the signed in users ID.
Right now I'm first getting the item ID's matching the active user:
$IDs = Cart::select('item_id')->where('user_id', auth()->id());
Then I want to select all items with an ID in $IDs
$items = Item::all()->whereIn('id', function($query) {
$query->select('item_id')->from($IDs->item_id);
});
However when I try this, I get the error
Object of class Illuminate\Database\Query\Builder could not be converted to int.
When I replace $IDs with something like [1234, 4321], it works.
How can I fix this problem?
$IDs = Cart::select('item_id')->where('user_id', auth()->id());
this line return a query builder not a collection of ids ...
anyway you can get the ids in array and use it directly for whereIn :
$IDs = Cart::where('user_id', auth()->id())->pluck('item_id')->all();
$items = Item::whereIn('id',$IDs)->get();
In my project I'm using Laravel 5.5 with Eloquent and Scout drivers to build a sort of search engine API endpoint.
In my scenario I have a SQL table items that has a price_factor property.
The table is also stored inside an Elasticsearch index.
With this value and with the number of the user related with that item, I can calculate the right price of the object.
An easy example is the item with id: 1 has price_factor: 2 and it is related to 5 users.
The right price of the item is 2 * 5 = 10, of course.
Now, I have to query all results and use where conditions, sorting them by that calcolated property, and return paginated results.
For example: get all items with price between 5 and 10, sort them by price and paginate by 10 elements per page.
In Eloquent I will write:
// Set filters to be applied
$filters = [
['price', '>', 5],
['price', '<', 10],
];
// Sort by "update_at", or "price"
$sort = "price";
// Order by "ASC" mode
$order = "ASC";
// Number of rows per page
$rows = 10;
// Get items
$result = Item::orderBy(
$sort,
$order
// Get related user with item record, where has...
)->with(['users'])->whereHas(
// Take users related models
'users',
// Take the model related ("user") and use filters
function($relation_model) use ($filters) {
// Apply where condition with filters
$relation_model->where($filters);
}
// Paginate for the numbers of row requested
)->paginate($rows);
How to do that if price is not a property of table items?
Should I store price inside the table and update it on every new user relation added? (or every removed relation too).
Is this the correct approach?
I was thinking about website like eBay or other real-time auction that have a similar situation of mine: how do you think they have solved?
Thanks in advance.
Assuming that you have a user_items table that keeps track of the items owned by user, I think something like this might work:
$result = Item::selectRaw('items.*, price_factor * (SELECT COUNT(id) FROM user_items WHERE user_items.item_id = items.id) AS price')
->with(['users'])
->havingRaw('price > ? AND price < ?', [5, 10])
->orderBy('price', 'asc')
->paginate($rows);
You can calculate the price on the fly and alias it. Finally you can apply a havingRaw clause to it that will check if the price is between the range it needs to be. Here's a working example of this in action.
There might be better ways to do this. I am also curious to know :)
I'm trying to access the parent value, the posts date, in a nested function.
In my application, a user has many posts, with each post being associated to a product (textbook). Each time someone views a page with the product, a new row is added to the product-views table.
I want to return the cumulative amount of times the users products have been seen. I've been able to get all the users posts, then the associated product, and then the count of all the views of that product.
Now I'd like to add another where() condition to only return views that have occured after the post was created. To do so, I need to get the posts date, e.g. views->product->post, while constructing the query like user->posts->product->views.
// getting all of the users available posts
$user = $request->user()->posts()->available()->with([
// with the textbook (product) associated with the post
'textbook' => function ($q) {
// get the count of textbook-page views
return $q->withCount([
// from the related table views
'views' => function ($q) {
// ===!!! Q !!!=== How do I access the posts (parent (grandparent?)) date, so that I only select the rows that have been viewed after the post was created ===!!!===
->where('date-viewed', '>=', 'posts.date');
},
]);
//some cleanup
}])->distinct()->get()->unique('isbn')->pluck('textbook.views_count')->sum();
How do I go backwards in a nested function to access the posts date?
It looks like as Jonas said in the comments, each with() relationship was a new query, so I ended up creating a hasManyThrough() relationship between the posts and views through the product.
// getting all of the users available posts
$user = $request->user()->posts()->available()->withCount([
'views' => function ($q) {
// ===!!! A !!!=== Fixed ===!!!===
->whereRaw('`date-viewed` >= posts.date');
},
])->distinct()->get()->unique('isbn')->pluck('views_count')->sum();
I have two tables 'purchases' and 'accounts_purchase_history'. purchase has many accounts history. So I need to get all the purchases with the latest history.
What i have done so far
$purchases = Purchases::with(['accounts_purchase_historie' => function($query){
return $query->orderBy('id','DESC')->first();
}])->orderBy('id','DESC')->where('status','extended')->get();
But this query only gets the first purchase history, How can i solve this?
You can use a HasOne relationship:
public function latest_history() {
return $this->hasOne(...)->orderBy('id','DESC');
}
$purchases = Purchases::with('latest_history')
->orderBy('id','DESC')
->where('status','extended')
->get();
foreach($purchases as $purchase) {
$purchase->latest_history
}
However, this will still fetch all the histories from the database in the background.
I have a table with categories, one table with products and another table products_user which tracks the products a user owns.
When displaying products on a page it should change a button from Buy to Bought if a user owns the product.
I get the products to display through $categories->products. What is the most efficient way to find out which of these products a user already owns?
I don't want to load the entire collection of the user owned products into the memory since these could be several thousands. I also don't want to create a Mysql query for each check.
There is an option for a wherein clause. But even then I am that there is a smarter way to create this clause without looping through every product to build an array.
Can someone help me to come up with a good logic? thank you
You can make use of Constraining Eager Loads to append more information to your products. In this case, the user_id is either NULL or user_id, meaning the user is bought the product or not.
$categories = Category::with(['products' => function ($q) {
$q->select(['products.*', 'user_id'])
->leftJoin('products_user', 'user_products.product_id', '=', 'products.id')
->where(function ($q) {
$q->whereNull('user_id')->orWhere('user_id', Auth::user()->id);
});
}])->get();
foreach ($categories as $category) {
$products = $category->products;
foreach ($products as $product) {
if (empty($product->user_id)) {
// user not yet bought the product
}
else {
// user already bought the product
}
}
}