Laravel. How to handle sort_order column shift - php

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.

Related

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...

Yii2 Combine 2 unrelated models with some shared properties into one Gridview

I have 2 separate models (for 2 separate tables) containing some similar properties, which I want to combine into one GridView. The models are unrelated.
Model 1:
+----+------+----------------+-----+
| id | name | email | age |
+----+------+----------------+-----+
| 1 | Bob | bob#bob.bob | 22 |
| 2 | Ross | ross#ross.ross | 24 |
+----+------+----------------+-----+
Model 2:
+----+-------+-----------------+----------+-----------+
| id | name | email | location | Interests |
+----+-------+-----------------+----------+-----------+
| 1 | Mr | mr#mr.mr | Middle | Computers |
| 2 | Robot | robot#robot.bot | Nowhere | Hacking |
+----+-------+-----------------+----------+-----------+
I want to export the following data to a CSV (using kartik/Grid/Gridview/ExportMenu), which works similarly to a GridView:
+----+-------+-----------------+----------+-----+-----------+
| id | name | email | Location | Age | Interests |
+----+-------+-----------------+----------+-----+-----------+
| 1 | Mr | mr#mr.mr | Middle | | Computers |
| 2 | Robot | robot#robot.bot | Nowhere | | Hacking |
| 3 | Bob | bob#bob.bob | | 22 | |
| 4 | Ross | ross#ross.ross | | 24 | |
+----+-------+-----------------+----------+-----+-----------+
The export widget works the same as a CGridView. You supply a dataProvider (could have pagination) and the exported CSV contains all rows.
Currently I'm using Search models to return 2 ActiveDataProviders, and then I combine them with ArrayDataProvider:
$searchModel = new RegisteredUserSearch([$argumentsArray1]);
$dataProvider1 = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider1->pagination = ['pageSize' => 12];
$collectedEmailSearchModel = new CollectedEmailSearch([$argumentsArray2]);
$dataProvider2 = $collectedEmailSearchModel->search(Yii::$app->request->queryParams);
$dataProvider2->pagination = ['pageSize' => 12];
$data = array_merge($dataProvider1->getModels(), $dataProvider2->getModels());
$dataProvider = new ArrayDataProvider([
'allModels' => $data,
'pagination'=>[
'pageSize'=> 0
]
]);
Using $dataProvider1 or $dataProvider2 as the GridView's dataProvider works fine, but using the combined $dataProvider results in exporting only 24 rows. I tried changing the pageSize of the dataProviders to 0, but that doesn't appear to make a difference.
You can make relationship between models on same email. For example
In Model1.php you must define relationship in this case by same email
public function getSameEmail()
{
return $this->hasOne(Model2::className(),['email'=>'email']);
}
To display in grid view values from other table you must join it in searchModel1.php file. You are joining tables by same email.
after validation you can add relationship you define to query. Like this
$query->joinWith('sameEmail');
And you will get values of columns from another table. Be careful with columns with same name.

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

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

MySQL: Updating values of "children" from their "parents"

I have a database with rows of "parents" and "children". Similar entries, but one entry is generic version of the more specific child. However, I want these entries to match exactly in certain columns.
Here's an example of my database:
| ID | IsChildOfID | Food | Type |
| 1 | | | Fruit |
| 2 | 1 | Apple | Fruit |
| 3 | 1 | Pear | Vegetable |
| 4 | 1 | Banana | Vegetable |
| 5 | | | Vegetable |
| 6 | 5 | Lettuce | Fruit |
| 7 | 5 | Celery | Vegetable |
| 8 | 5 | Cabbage | Fruit |
In this example there are 2 parents and 6 children. The value of "type" field is inconstant with some of the children. I want to be able to find any children in the database and replace it with their parent's value in only some of the columns. Is this possible with purely MySQL or do I need do it with php? Thanks.
UPDATE name_of_table SET Type = "Fruit" WHERE IsChildOfID = 1
and
UPDATE name_of_table SET Type = "Vegetable" WHERE IsChildOfID = 5
But if you want to do it dynamicaly please use php or some other language...
Also I would prefer to use 2 tables for this kind of data...
Generally, when you use parent/children relationships in sql, you should make two separate database tables for each. In your case, you should create a database entitled "types" and include a type_id for each element in the child table.
Example
Child table:
| ID | TYPE_ID | Food |
| 2 | 1 | Apple |
| 3 | 2 | Pear |
| 4 | 2 | Banana |
| 6 | 1 | Lettuce |
| 7 | 2 | Celery |
| 8 | 1 | Cabbage |
Type table:
| ID | Type |
| 1 | Fruit |
| 2 | Vegetable |
You can then reference it by looping through the type table, and using a sql statement like
$types = mysql_query ( 'SELECT * FROM type_table');
WHILE ( $type = mysql_fetch_array ( $types ) )
{
$sql = 'SELECT * FROM child_table WHERE TYPE_ID = "' . $type['type'] . '"';
}
Similar answer here:
UPDATE multiple tables in MySQL using LEFT JOIN
I was going to write this:
UPDATE foods c
JOIN foods p ON p.id = c.IsChildOfId
SET c.type = p.type
WHERE p.isChildOfId IS NULL
But then upon further reading of the link above, not sure you can reference the target table. Worth a try.

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