Laravel 5.1 Eloquent with() and max() using multiple has-many relationships - php

I'm trying to use Eloquent to find the max value of a column on the last table of a multiple has-many relationship.
Given the following table structure.
Buildings
+----+---------------+
| id | building_name |
+----+---------------+
| 1 | Building 1 |
| 2 | Building 2 |
+----+---------------+
Rooms
+----+-----------+-------------+
| id | room_name | building_id |
+----+-----------+-------------+
| 1 | Room 1 | 1 |
| 2 | Room 2 | 1 |
| 3 | Room 3 | 2 |
+----+-----------+-------------+
maintenancelog
+----+-------------------+---------+---------------------+
| id | maintenance_value | room_id | timestamp |
+----+-------------------+---------+---------------------+
| 1 | Cleaned | 1 | 2015-09-06 00:54:59 |
| 2 | Cleaned | 1 | 2015-09-06 01:55:59 |
| 3 | Cleaned | 2 | 2015-09-06 02:56:59 |
| 4 | Cleaned | 2 | 2015-09-06 03:57:59 |
| 5 | Cleaned | 3 | 2015-09-06 04:58:59 |
| 6 | Cleaned | 3 | 2015-09-06 05:59:59 |
+----+-------------------+---------+---------------------+
I'd like to see if it's possible to generate an eloquent statement that would retrieve the building name, room name, and ONLY the LAST maintenance log date value.
The following works to give me a collection of ALL the values.
$buildings = Building::with('rooms.maintenancelog')->get();
but this errors out, and it looks like it's trying to call max(maintenancelog.timestamp) on the buildings table..
$buildings = Building::with('rooms.maintenancelog')->max('maintenancelog.timestamp')->get();
Error returned:
.....(SQL: select max(`maintenancelog`.`timestamp`) as aggregate from `buildings`)
Am I just asking too much from eloquent, and should I just use the basic query builder

Add the following relationship to the Rooms model...
public function latestMaintenance()
{
return $this->hasOne('App\Maintenancelog')->latest();
}
and change the Eloquent statement to..
$buildings = Building::with('rooms.latestMaintenance')->get();
referenced Getting just the latest value on a joined table with Eloquent

Related

Laravel belongs to many by two intermediate tables?

Hi guys I have to create many to many relationship but through a another table. but laravel BelongsToMany give only 1 intermediate table relation
here table structure:
features Table:
+----+-----------+
| id | text |
+----+-----------+
| 1 | feature 1 |
+----+-----------+
| 2 | feature 2 |
+----+-----------+
feature_values table:
+----+-----------+-------+
| id | feature_id | text |
+----+-----------+-------+
| 1 | 1 | val 1 |
+----+-----------+-------+
| 2 | 1 | val 2 |
+----+-----------+-------+
| 3 | 2 | val 3 |
+----+-----------+-------+
products table:
+----+-------+
| id | name |
+----+-------+
| 1 | tv |
+----+-------+
| 2 | phone |
+----+-------+
product_features table:
+----+------------+------------+
| id | product_id | feature_id |
+----+------------+------------+
| 1 | 1 | 1 |
+----+------------+------------+
| 2 | 2 | 1 |
+----+------------+------------+
| 3 | 2 | 2 |
+----+------------+------------+
product_feature_values table:
+----+--------------------+------------------+
| id | product_feature_id | feature_value_id |
+----+--------------------+------------------+
| 1 | 1 | 1 |
+----+--------------------+------------------+
| 2 | 2 | 1 |
+----+--------------------+------------------+
| 3 | 2 | 2 |
+----+--------------------+------------------+
I'm trying to create a relationship between feature_values and and products through product_feature_values and product_features for get product by feature_values.
I know data can obtain by joining tables, but I looking for a solution that using by laravel model relationships
thank you.
I tried like this
class FeatureValue extends Model
{
public function products()
{
return $this->belongsToMany(Product::class,'product_feature_values')->withTimestamps();
}
}
I'm afraid, that Laravel didn't offer that out-of the box. But there's a package called staudenmeir/belongs-to-through that does.

Laravel. How to handle sort_order column shift

I have such a table, and SomeModel, that represents it
| id | name | sort_order |
| --------------- | ---------------- | ---------------- |
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |
| 4 | D | 8 |
I'm implementing an API for this table. How to handle properly sort_order column shift during inserting the same values.
For example:
I want to save such an object:
{
"name": "B2",
"sort_order": 2
}
and receive such a table:
| id | name | sort_order |
| --------------- | ---------------- | ---------------- |
| 1 | A | 1 |
| 5 | B2 | 2 |
| 2 | B | 3 | <--- initially was 2
| 3 | C | 4 | <--- initially was 3
| 4 | D | 8 |
How to implement it?
I'd do it like this:
use App\SomeModel;
use Illuminate\Http\Request;
public function insert_data(Request $request){
SomeModel::where("sort_order", ">=", $request->sort_order)->increment("sort_order");
SomeModel::create($request->all());
}
Since you have some model you can do this:
In your AppServiceProvider
Event::listen("eloquent.inserting: ".SomeModel::class, function (SomeModel $model) {
SomeModel::where("sort_order", ">=", $model->sort_order)
->update([ "sort_order" => \DB::raw("sort_order+1") ])
});
The idea is to update all other models with a sort order higher or equal to the one you're inserting and increment it by one. You might want to do something similar with the deleting event to fill in the gaps and perhaps the updating event to swap sort orders.
You can (and should) dig deeper into the event system for models by reading the documentation
Of course you could always set the sort_order column as unique and create a DB trigger to keep the sort orders in check for you which is probably better since this is a data integrity concern.

Pivot one table in MySQL

I'm currently running into a problem where I am required to pivot many rows into a single row, defined by a specific key. I know the GROUP_CONCAT functionality and am using that currently, but I would like to avoid having to use explode in PHP after fetching my results. My table looks like this:
----------------------------------
| primary_id | key_value | value |
----------------------------------
| 1 | my_key_1 | val_1 |
| 1 | my_key_2 | val_2 |
| 1 | my_key_3 | val_3 |
| 2 | my_key_4 | val_4 |
| 2 | my_key_5 | val_5 |
----------------------------------
And I would like to build a MySQL-Query that presents this exactly like this for primary id 1 and primary id 2:
-----------------------------------------------
| primary_id | my_key_1 | my_key_2 | my_key_3 |
-----------------------------------------------
| 1 | val_1 | val_2 | val_3 |
-----------------------------------------------
------------------------------------
| primary_id | my_key_4 | my_key_5 |
------------------------------------
| 2 | val_4 | val_5 |
------------------------------------
So I can retrieve the output as an array in PHP, with the form:
$output[1][my_key_1] = val_1
$output[1][my_key_2] = val_2
...
The GROUP_CONCAT functionality works, but it would be much nicer to just have the value in the form I needd it only using SQL.
Would appreciate any pointers, best regards.
Use a Pivot
select max(case (when key_value=my_key_1 then val_1 end)),
max(case (when key_value=my_key_2 then val_2 end)),
from...
etc...

Query results depends on result from seperate query

What's the most effective way to go about querying the table to output the results as shown?
Table:
+-----+----------+-----+
| PN | date | QTY |
+-----+----------+-----+
| AB1 | 01/12/14 | 4 |
| AB2 | 01/12/14 | 2 |
| AB3 | 01/12/14 | 7 |
| AB1 | 01/22/14 | 8 |
| AB3 | 01/25/14 | 3 |
| AB5 | 01/25/14 | 9 |
+-----+----------+-----+
Results:
+-----+----------+----------+----------+
| | 01/12/14 | 01/22/14 | 01/25/14 |
+-----+----------+----------+----------+
| AB1 | 4 | 8 | 0 |
| AB2 | 2 | 0 | 3 |
| AB5 | 7 | 0 | 9 |
+-----+----------+----------+----------+
What you want to do is called pivoting the data. It can be achieved in MySQL with some CASE statements and aggregate functions.
E.G.
SELECT PN,
SUM(CASE WHEN date='20141201' THEN qty END) AS day1Qty,
SUM(CASE WHEN date='20141202' THEN qty END) AS day2Qty
FROM table
GROUP BY PN
Of course you'll likely want to do something to dynamically handle the dates that are being aggregated so it isn't just a static list (unless that is what you're after).
See these answers for more info:
Pivot Table Using MySQL
MySQL pivot table

Add dynamic columns to MySQL result

I have a table in mysql, let's call it foo and it has a limitied amount of columns.
| id | name | date |
--------------------------
| 1 | bar | 2012-05-08 |
| 2 | buba | 2012-05-09 |
My users can add records to the table foo_field (stuff like, code, description, time...).
| id | name |
--------------------
| 1 | code |
| 2 | description |
| 3 | time |
In the table foo_field_value the values for the user-defined fields are stored, like so:
| id | foo_id | foo_field_id | value |
------------------------------------------
| 1 | 1 | 1 | b |
| 2 | 1 | 2 | Lalalala |
| 3 | 1 | 3 | 12:00 |
| 1 | 2 | 1 | c |
| 2 | 2 | 2 | We are go |
| 3 | 2 | 3 | 14:00 |
Ideally, I'd want one query which would give me a result like
| id | name | date | code | description | time |
------------------------------------------------------
| 1 | bar | 2012-05-08 | b | Lalalala | 12:00 |
| 2 | buba | 2012-05-09 | c | We are go | 14:00 |
Is this even possible without doing an inner join on the foo_fields_value table for every foo_field (generating the query with PHP by doing another query first).
It's possible to do it in just one, and quite simple.
We are going to modify the foo_field table a bit, adding a column corresponding to the foo table's id column, which I assume is the primary key.
so now we have
* foo
|------|
| id |
| name |
| date |
|------|
* foo_field
|-------------|
| foo_id |
| code |
| description |
| time |
|-------------|
Which means we can add the extra fields with one simple query:
SELECT * FROM foo
LEFT JOIN foo_field ON foo.id = foo_field.foo_id
Which will give us a result set of
| id | name | date | foo_id | code | description | time |
|----+-------+------------+--------+--------+-------------+----------|
| 1 | asdw | 2012-05-16 | 1 | asdasd | asdasd | 15:03:41 |
| 2 | fdgfe | 2012-05-18 | 2 | asdas | asdas | 15:03:41 |
| 3 | asdw | 2012-05-16 | 3 | asdas | asdas | 15:03:52 |
| 4 | fdgfe | 2012-05-18 | 4 | asdasd | asdasd | 15:03:52 |
I am still not sure I surely understood your question. If you want to create truly dynamic values and datastructures, I suggest you save a serialized array into a TEXT field in your database, but I also suggest you to overlook your solution if this is the case; if you want your solution to be able to grow, you want to manage as strict structures as possible.
What you are looking for is a pivot query. And since you have dynamic fields that need to converted to columns, check this article here about making automatic pivot queries
http://www.artfulsoftware.com/infotree/queries.php#523

Categories