I have the following prices-table:
shop_id (int)
product_id (int)
price (float)
created (DateTime)
Every hour a cronjob checks the shops and inserts new entries (current prices) into these price-table.
Now I want to display the newest price for a product. I have to GROUP BY the shop_id because I only want one price per shop but I only want the newest entry (created).
Can I solve this with Eloquent Query-Builder or do I have to use raw SQL? Is it possible to pass the result of a raw SQL-query into a model if the columns are the same?
You can try it as:
Price::select('*', DB::raw('MAX(created_at) as max_created_at'))
->groupBy('shop_id')
->get()
Assuming model name is Price
Eloquent (purist) approach:
Price::orderBy('created', 'desc')->groupBy('shop_id')
->get('shop_id', 'price');
References:
https://laravel.com/api/5.3/Illuminate/Database/Query/Builder.html#method_orderBy
https://laravel.com/api/5.3/Illuminate/Database/Query/Builder.html#method_groupBy
https://laravel.com/api/5.3/Illuminate/Database/Query/Builder.html#method_get
*untested though
Q: Is it possible to pass the result of a raw SQL-query into a model if the columns are the same?
A: you could pass it to Model's contructor - but it might need model's field to be fillable - or hydrate a model. Alternatively, just access it like an keyed-array, ie. $something[0]['price'] <-- assuming an array of prices with price column.
I solved the problem without QueryBuilder. Instead I use a raw SQL-statement and generating the models with the hydrateRaw()-function of the Model-class.
$prices = Price::hydrateRaw( 'SELECT p.*
FROM prices p
INNER JOIN (
SELECT shop_id, max(created_at) AS max_ca
FROM prices p1
GROUP BY shop_id
) m ON p.shop_id = m.shop_id AND p.created_at = m.max_ca');
Related
i want to orderby products with max price from details table.
products one to many relation with details table.
in laravel documentation we have aggregate methods like withMax ,withMin and withCount
. These methods will place a {relation}_{function}_{column} attribute on your resulting models.
now we only need to orderBy with this results.
$products = Product::withMax('details', 'price')
->orderBy('details_max_price' , 'desc')
->paginate(15);
I have a problem in getting the sum of time taken between an order
I can able the get the created time by the following code:
$collection = Mage::getResourceModel('sales/order_status_history_collection')
->addAttributeToSelect('created_at')
->addAttributeToFilter('status', array('eq'=>'complete'))
->load()
is there a way to get the sum of time taken between two status like sum(OrderInvoiced+OrderShipped) ?
Any help would be appreciated.
You can use the direct SQL queries method to get the time difference between invoice and shipment created.
Magento Direct SQL Queries
Invoice is saved under sales_flat_invoice and shipment are saved under sales_flat_shipment. There are 2 dates for each created_at and updated_at in both tables.
Here is the SQL query to get the difference between invoice and shipment based on created_at column
SELECT TIMEDIFF(ss.created_at,si.created_at) As TimeTaken
FROM `sales_flat_invoice` si
JOIN `sales_flat_shipment` ss
ON si.order_id=ss.order_id
WHERE si.order_id=1
To get the time difference for all the order just remove WHERE si.order_id=1 from the above query.
I have a model User that has many Orders. Orders have many products, with the pivot table order-product. I don't want to preload and iterate through the orders if at all possible.
I need to return users where
signed_date === true on User
order_date on Order is after signed_date on User
order-product shows product hasn't been paid
I am failing on number 2.
In the following code, the first query within whereHas is wrong. I don't know how to reference the signed date of the user from within the where has. If I was iterating through users in a collection I could do something like ($query) use $user, but how do I do this without preloading all the users?
return User::whereNotNull('signed_date')
->whereHas('orders', function ($query) {
$query->where('order_date', '<=', 'user.signed_date');
$query->whereHas('products', function ($q) {
$q->where('paid', false);
});
})
->get(['id','fname','lname', 'title', 'signed_date']);
I would like to use eloquent if possible. If that is not possible, I would be happy for tips in solving this problem using the query builder/sql.
The Eloquent query builder has a special function called whereColumn('a', '<=', 'b') to compare columns instead of a column against a value. Using this function instead of a normal where() is necessary because of the way the query builder builds the actual query. You need to let the query builder know that you are going to pass a column name instead of a value for proper escaping and formatting of the query string.
Anyway, it seems you can also pass column names prefixed with a table name to the function, allowing you to compare columns across tables:
$query->whereColumn('orders.order_date', '<=', 'users.signed_date')
This works because you are using whereHas() in your query. Your query basically gets translated to:
SELECT id, fname, lname, title, signed_date
FROM users
WHERE signed_date NOT NULL
AND EXISTS (
SELECT 1
FROM orders
WHERE orders.order_date <= users.signed_date
AND EXISTS (
SELECT 1
FROM products
WHERE paid = 0
)
)
It might actually be not necessary at all to use the table name together with the column name in whereColumn(). But in case you'll ever add a column named the same on another table, the query might break - so IMHO it is good practice to use the table name in custom queries.
By the way, the reason this will not work together with with('relationship') is that this function results in an additional query and you obviously cannot compare columns across queries. Imagine the following:
Order::with('user')->take(5)->get();
It will be translated into the following:
SELECT *
FROM orders
LIMIT 5
SELECT *
FROM users
WHERE id IN (?, ?, ?, ?, ?)
where the five ? will be the user_ids of the orders. If the first query returns multiple rows with the same user_id, the amount of rows fetched from the users table gets reduced of course.
Note: All the queries are only examples. Might be that the query builder builds different queries based on the database type and/or escapes them differently (i.e. column names in backticks).
Given I have a simple join between two tables user and filled forms, filled forms has columns: value, sales and income.
Sales and income are boolean columns if any of them are set to true then I know which value are sales and which are income.
User::where('id', $id)
->leftJoin("filled_forms", function($join){
$join->on("user.form_id", "filled_forms.id")
->where("filled_forms.sales", true)
->where("filled_forms.income", true);
})->get();
Now all the fields that sales are set to true I want to
count(filled_forms.value) as number_of_sales and all the incomes
that are set to true I want to do sum(filled_forms.value) as income
I know that I could do something with DB::Raw maybe ?
You can create a SQL View for such requirements as it will provide you with a table with real time updated information.
http://beginner-sql-tutorial.com/sql-views.htm
Then you can follow standard laravel where condition to fetch data from the view.
I'm having some trouble with my pivot table. I've recognized too late, that it is possible, that some pivot rows doesn't have unique values in my project, means I've to add an auto_increment ID field to my pivot table.
This is my structure:
Order.php
public function items()
{
return $this->belongsToMany('Item', 'orders_items', 'order_id', 'item_id')->withPivot(['single_price', 'hours', 'prov', 'nanny_id', 'vat', 'vat_perc', 'invoice_id','id']);
}
orders_items
id, order_id, item_id, nanny_id, hours
I've conntected Orders and Items through a pivot table ('orders_items)'. It is possible, that one order has 2 or more same items in the pivot table. So I've to add an unique ID to identify and update them.
Now I try to update a pivot row. Problem is, if I have 2 or more items, he updates them all, not only one. This is my update command:
$order = Order::find($orderId);
$items = $order->items()->whereNull('nanny_id');
$free_item = $items->first();
$free_item->pivot->nanny_id = 123;
$free_item->pivot->save();
With this command, he updates all pivot rows from the order. I know the problem: Laravel uses here the wrong identifiers (it uses order_id and item_id as defined in my belongsToMany relationship - and they aren't unique). For example, Laravel tries to execute this code on save():
UPDATE orders_items SET [...] WHERE order_id = 123 AND item_id = 2;
I want, that Laravel changes the query to this one:
UPDATE orders_items SET [...] WHERE order_id = 123 AND item_id = 2 AND id = 45;
// Edit
Okay, this solution works:
$free_item->pivot->where('id',$free_item->pivot->id)->update('nanny_id',123);
But is there an easier way, f.e. adding a custom pivot model that adds the id automatically to save() and update() methods?