How can I update multiple rows at the same time by their report_id? My Inventory table looks like this:
| id | report_id | product_id | shelf_quantity | display_quantity |
|----|-----------|------------|----------------|------------------|
| 1 | 2 | 12 | 1 | 5 |
| 2 | 2 | 13 | 2 | 6 |
| 3 | 2 | 14 | 3 | 23 |
My attempt to solve the problem:
My controller:
function update($id, Request $request) {
$report = Report::find($id);
$inputs = $request->input('display');
$report->user_id = $user_id;
$report->save();
//Updating inventory table
$inventory = Inventory::where('report_id', $report->id)->get();
foreach($inputs as $key => $value) {
$inventory->display = $request->input('display')[$key] ?: 0;
$inventory->storage = $request->input('storage')[$key] ?: 0;
$inventory->save();
}
My web route:
Route::put('/reports/{id}', 'ReportController#update');
The problem here is that it is only saving the last value that I input.
Additional information
I'm also using the same code in storing new rows and it's working fine. Please see code below:
public function store(Request $request) {
//Adding inventory table
$new_inventories = new Inventory();
foreach($inputs as $key => $value) {
$data[] = [
'user_id' => $user_id,
'report_id' => $new_report->id,
'display' => $request->input('display')[$key] ?: 0,
'storage' => $request->input('storage')[$key] ?: 0,
'product_id' => $request->product_id[$key],
'created_at' => $now,
'updated_at' => $now,
// 'remark' => $request->remark[$key] ?? null,
];
}
Inventory::insert($data);
//End of inventory table
}
Any form of help would be appreciated. Thank you!
EDIT:
My form looks like this:
<form action="/reports/{{$report->id}}" method="post">
#csrf
{{method_field('PUT')}}
#foreach($products as $product)
<input type="number" name="product_id[]" value="{{ $product->id}}" hidden>
<input type="number" name="storage[]">
<input type="number" name="display[]">
#endforeach
</form>
You cannot update multiple rows in one shot. In this case the best way is to get one by one the items in inventory and update them. So you loop through $request->input('product_id'), get the Inventory model for that product_id and $report->id, update and save.
for($i=0; $i<count($request->input('product_id');$i++) {
$inventory = Inventory::where('report_id', $report->id)->where('product_id', $request->input('product_id')[$i])->first();
$inventory->display = $inventory->display = $request->input('display')[$i];
$inventory->storage = $inventory->storage = $request->input('storage')[$i];
$inventory->save();
}
Right now, I build my menu doing many request and loop with symfony.
I would like to be able to retrieve the same thing in a single sql request
MenuItem (Id,parent_id,level,weight)
parent_id is a reference to an other MenuItem
If I have these records
1,null,1,50
2,null,1,20
3,1,2,100
4,1,2,0
5,2,2,40
6,5,3,900
7,5,3,500
I want the results to be
2
5
7
6
1
4
3
The weigtht attribute is used to order the menu items inside their own level.
That means id 2 come before id 1 because weight 20 < weight 50 but id 2 still comes before id 4 because level 1 < level 2
I'm not even sure this is possible.
EDIT: Right now, I have to do something very ugly
$menu = $this->factory->createItem('root');
$menu->setLabel("Menu");
$item_repository = $this->getDoctrine()->getRepository(MenuItem::class);
//profondeur max defini à 5
//On récupère les items du niveau 1 trié par poids (profondeur)
$items_lv1 = $item_repository->findBy(array('menu' => $id_main_menu,'active' => 1, 'niveau' => 1),array('poids' => 'ASC'));
//On récupère les items du niveau 2 trié par parent et poids (profondeur)
$items_lv2 = $item_repository->findBy(array('menu' => $id_main_menu,'active' => 1,'niveau' => 2),array('parent' => 'ASC','poids' => 'ASC'));
$items_lv3 = $item_repository->findBy(array('menu' => $id_main_menu,'active' => 1,'niveau' => 3),array('parent' => 'ASC','poids' => 'ASC'));
$items_lv4 = $item_repository->findBy(array('menu' => $id_main_menu,'active' => 1,'niveau' => 4),array('parent' => 'ASC','poids' => 'ASC'));
$items_lv5 = $item_repository->findBy(array('menu' => $id_main_menu,'active' => 1,'niveau' => 5),array('parent' => 'ASC','poids' => 'ASC'));
$cptLv1 = 1;
foreach ($items_lv1 as $item_lv1){
$lv1 = $menu->addChild($cptLv1,
['uri' => $item_lv1->getUrl(),'label' => $item_lv1->getLabel(),
'attributes' => array('title' => $item_lv1->getTitle(),
'id_css' => $item_lv1->getIdCss(), 'class_css' => $item_lv1->getClassCss(), 'target' => $item_lv1->getTarget())]);
$cptLv2 = 1;
foreach ($items_lv2 as $item_lv2){
if($item_lv2->getParent()->getUrl() == $item_lv1->getUrl()){
$lv2 = $lv1->addChild($cptLv1.$cptLv2,
['uri' => $item_lv2->getUrl(),'label' => $item_lv2->getLabel(),
'attributes' => array('title' => $item_lv2->getTitle(),
'id_css' => $item_lv2->getIdCss(), 'class_css' => $item_lv2->getClassCss(), 'target' => $item_lv2->getTarget())]);
$cptLv3 = 1;
foreach ($items_lv3 as $item_lv3){
if($item_lv3->getParent()->getUrl() == $item_lv2->getUrl()) {
$lv3 = $lv2->addChild($cptLv1.$cptLv2.$cptLv3,
['uri' => $item_lv3->getUrl(), 'label' => $item_lv3->getLabel(),
'attributes' => array('title' => $item_lv3->getTitle(),
'id_css' => $item_lv3->getIdCss(), 'class_css' => $item_lv3->getClassCss(), 'target' => $item_lv3->getTarget())]);
$cptLv4 = 1;
foreach ($items_lv4 as $item_lv4){
if($item_lv4->getParent()->getUrl() == $item_lv3->getUrl()) {
$lv4 = $lv3->addChild($cptLv1.$cptLv2.$cptLv3.$cptLv4,
['uri' => $item_lv4->getUrl(), 'label' => $item_lv4->getLabel(),
'attributes' => array('title' => $item_lv4->getTitle(),
'id_css' => $item_lv4->getIdCss(), 'class_css' => $item_lv4->getClassCss(), 'target' => $item_lv4->getTarget())]);
$cptLv5 = 1;
foreach ($items_lv5 as $item_lv5){
if($item_lv5->getParent()->getUrl() == $item_lv4->getUrl()) {
$lv4->addChild($cptLv1.$cptLv2.$cptLv3.$cptLv4.$cptLv5,
['uri' => $item_lv5->getUrl(), 'label' => $item_lv5->getLabel(),
'attributes' => array('title' => $item_lv5->getTitle(),
'id_css' => $item_lv5->getIdCss(), 'class_css' => $item_lv5->getClassCss(), 'target' => $item_lv5->getTarget())]);
}
$cptLv4 += 1;
}
}
$cptLv4 += 1;
}
}
$cptLv3 += 1;
}
}
$cptLv2 +=1;
}
$cptLv1+=1;
}
Consider the following:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,parent_id INT NULL
,level INT NOT NULL
,weight INT NOT NULL
);
INSERT INTO my_table VALUES
(1,null,1,50),
(2,null,1,20),
(3,1,2,100),
(4,1,2,0);
SELECT *,COALESCE(y.level,x.level) my_level,COALESCE(y.weight,x.weight) my_weight FROM my_table x LEFT JOIN my_table y ON y.id = x.parent_id;
+----+-----------+-------+--------+------+-----------+-------+--------+----------+-----------+
| id | parent_id | level | weight | id | parent_id | level | weight | my_level | my_weight |
+----+-----------+-------+--------+------+-----------+-------+--------+----------+-----------+
| 1 | NULL | 1 | 50 | NULL | NULL | NULL | NULL | 1 | 50 |
| 2 | NULL | 1 | 20 | NULL | NULL | NULL | NULL | 1 | 20 |
| 3 | 1 | 2 | 100 | 1 | NULL | 1 | 50 | 1 | 50 |
| 4 | 1 | 2 | 0 | 1 | NULL | 1 | 50 | 1 | 50 |
+----+-----------+-------+--------+------+-----------+-------+--------+----------+-----------+
Extract from that whatever you like.
you do not have to use level and weight ... just use the weight ("order" would be better).
do not worry if id 2 is before or after id 4, because id4 is in id2 (parent level).
an element with parent_id = null is a id with level 0.
think like this :
select Id,parent_id,level ,weight from mytable order by level,weight
Here is a piece of code that allows you, after your select, to have a recursive table that you can browse to display the menus.
It takes into account that an element can be related to "null" (root), and that there can be non-consecutive "weights" ...
the $ result variable is constructed as if you had made a select with a sort on level + weight
<style>
ul {
list-style-type: none;
margin: 0;
padding: 0;
background-color: #D0D0D0;
}
li {
margin-left:2em;
border-left:solid 2px green;
text-align:left;
padding-left:1em;
}
</style>
<?php
/*
* warn ! this array looks like a result after a select, ordered by level+weight
*/
$result=array(
array( 'id'=>2, 'parent_id'=>null, 'level'=>1, 'weight'=>20 ),
array( 'id'=>1, 'parent_id'=>null, 'level'=>1, 'weight'=>50 ),
array( 'id'=>4, 'parent_id'=>1, 'level'=>2, 'weight'=>0 ),
array( 'id'=>3, 'parent_id'=>1, 'level'=>2, 'weight'=>100 ),
array( 'id'=>5, 'parent_id'=>2, 'level'=>2, 'weight'=>40 ),
array( 'id'=>7, 'parent_id'=>5, 'level'=>3, 'weight'=>500 ),
array( 'id'=>6, 'parent_id'=>5, 'level'=>3, 'weight'=>900 )
);
// store recursive array of menus
$menus=array();
// store link of an id in the recursive array of menus
$keysIds=array();
// start with a empty menu
$menus[0]=array('element'=>'ROOT','subMenus'=>array());
$keysIds[0]=&$menus[0];
// build a menus array
foreach ($result as $element) {
$idParent=$element['parent_id'];
$id=$element['id'];
// check for idParent=0 !!
if ($idParent==null) {
$idParent=0;
}
// check if parent exists
// don't forget, result is sorted by level+weight,
// so the idParent is always in array
if (!isset($keysIds[$idParent])) {
echo "<H2>PARENT = $idParent , not exists while INSERTING Id {$id} </H2>";
continue;
}
// JSON index sorting prevention
$max=sizeof($keysIds[$idParent]['subMenus']);
// create a entry
$keysIds[$idParent]['subMenus'][$max]=array('element'=>$element,'subMenus'=>array());
// for next entries, keep this id accessible quickly..it can be a parent.
$keysIds[$id]=&$keysIds[$idParent]['subMenus'][$max];
}
// show the HTML LISTE
htmlMenus($menus,0);
// Recursive iteration on $menus
function htmlMenus($root,$listeLevel) {
echo str_repeat(" ", $listeLevel*2); // beautifull source...
echo "<ul>\n";
foreach ($root as $datas) {
echo str_repeat(" ", $listeLevel*2+2);
echo "<li>\n ";
if ($datas['element']!='ROOT') {
echo str_repeat(" ", $listeLevel*2+2);
echo "ID : {$datas['element']['id']} weight:{$datas['element']['weight']}\n";
}
htmlMenus($datas['subMenus'],$listeLevel+1);
echo str_repeat(" ", $listeLevel*2+2);
echo '</li>';
}
echo str_repeat(" ", $listeLevel*2);
echo "</ul>\n";
}
I'm trying to get data for each date in range from 3 tables: Main table which is connected with 2 other tables using hasMany method.
Data stored as in example:
Main Table:
id | article title | url | created_at | updated_at |
----------------------------------------------------------------
14 | Some Title | www.example.com | TIMESTAMP | TIMESTAMP |
Views table (there is written count of views for each hour):
id | article_id | views | created_at | updated_at |
---------------------------------------------------
1 | 14 | 317 | TIMESTAMP | TIMESTAMP | (01:00:00)
2 | 14 | 186 | TIMESTAMP | TIMESTAMP | (02:00:00)
Clicks Table (there is written every click on this article):
id | article_id | ip_adress | created_at | updated_at |
---------------------------------------------------------
1 | 14 | 192.168.1.1 | TIMESTAMP | TIMESTAMP |
For example:
I need to get Articles from 01-02-2016 to 01-03-2016.
For every article I need to sum views and clicks for each day.
So in result i need to get something like this:
ID: 14, Title: Some Title, Views: 503, Clicks: 27
First, I wrote this code, but it makes lots of requests to database:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
foreach ($dates as $i => $date) {
$articles = Articles::with(['views' => function ($query) use ($date) {
$query->where('created_at', $date);
}, 'clicks' => function ($query) use ($date) {
$query->where('created_at', $date);
}])->get();
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) {
// Code
}
foreach ($clicks->countOfClicks as $i) {
// Code
}
// Code
}
}
Then I found solution, to get exact same result as I'm getting in first example, but making only three requests:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
$articles = Articles::with(['views' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop));
}, 'clicks' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop));
}])->get();
foreach ($dates as $date) {
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
foreach ($clicks->countOfClicks as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
// Code
}
}
It solves problem with too many queries, but it takes to much time. Is it possible to do same thing faster?
Final Code:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
$articles = Articles::with(['views' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop))
->groupBy('campaign_id', DB::raw('DATE(created_at)'))
->selectRaw('*, sum(views) as views');
}, 'clicks' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop))
->groupBy('campaign_id', DB::raw('DATE(created_at)'))
->selectRaw('*, sum(clicks) as clicks');
}])->get();
foreach ($dates as $date) {
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
foreach ($clicks->countOfClicks as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
// Code
}
}
I am trying to generate nested json data like in this example from one mysql table.
var data = {
"62" : {
"section" : "bodyImage",
"img" : "imageurl/image62.png",
"label" : "blue",
"price" : "100"
},
"63" : {
"section" : "bodyImage",
"img" : "imageurl/image63.png",
"label" : "red",
"price" : "120"
}
}
62 and 63 are from the row data_id in the following table:
+-----------+------------+-------------------------+-------+---------+
| data_id | section | img | label | price |
+-----------+------------+-------------------------+-------+----------
| 62 | bodyImage | imagpath/image62.png | blue | 100 |
| 63 | bodyImage | imagpath/image62.png | red | 120 |
+-----------+------------+-------------------------+-------+---------
+
This is the php file with the query:
$result = mysql_query("SELECT data_id, section, img, label, price FROM table WHERE active != 'no'");
$data = array();
while($row=#mysql_fetch_object($result)) {
$data[] = array (
'section' => $row['sub_section'],
'img' => $row['big_image'],
'label' => $row['label_client_en'],
'price' => $row['price']
);
}
echo json_encode( $data );
I cannot get it working. Please help me with the right syntax for the multi-dimensional array.
You cannot json_encode "sub arrays" directly on the main array
You must do json_encode for each array in your while:
$data[] = json_encode(array (
'section' => $row['sub_section'],
'img' => $row['big_image'],
'label' => $row['label_client_en'],
'price' => $row['price']
));
And then you must also encode the main array as you already do.
Consider the following table:
mysql> select * from vCountryStatus;
+-------------+------------+------+---------+--------+-----------------+
| CountryName | CountryISO | Code | Status | Symbol | CurrencyName |
+-------------+------------+------+---------+--------+-----------------+
| Brazil | BR | 55 | LIVE | BRL | Brazilian Real |
| France | FR | 33 | offline | EUR | Euro |
| Philippines | PH | 63 | LIVE | PHP | Philippino Peso |
+-------------+------------+------+---------+--------+-----------------+
3 rows in set (0.00 sec)
I am trying to construct a hash based on this table. For this I do the following:
#!/usr/bin/perl
use DBI;
use Data::Dumper;
my $dbh = DBI->connect("dbi:mysql:database=db", "user", "password", {RaiseError => 1, AutoCommit => 0, FetchHashKeyName => "NAME_lc"}) || die "DB open error: $DBI::errstr";
my $sth = $dbh->prepare("select * from vCountryStatus");
$sth->execute;
my $hash = $sth->fetchall_hashref('countryiso');
print Dumper($hash);
Here is the output this generates:
$VAR1 = {
'PH' => {
'symbol' => 'PHP',
'status' => 'LIVE',
'countryname' => 'Philippines',
'countryiso' => 'PH',
'currencyname' => 'Philippino Peso',
'code' => '63'
},
'BR' => {
'symbol' => 'BRL',
'status' => 'LIVE',
'countryname' => 'Brazil',
'countryiso' => 'BR',
'currencyname' => 'Brazilian Real',
'code' => '55'
},
'FR' => {
'symbol' => 'EUR',
'status' => 'offline',
'countryname' => 'France',
'countryiso' => 'FR',
'currencyname' => 'Euro',
'code' => '33'
}
};
The question is: why is the key of the hash (countryiso) repeated in the values inside the hash?
What I would prefer is the following output:
$VAR1 = {
'PH' => {
'symbol' => 'PHP',
'status' => 'LIVE',
'countryname' => 'Philippines',
'currencyname' => 'Philippino Peso',
'code' => '63'
},
'BR' => {
'symbol' => 'BRL',
'status' => 'LIVE',
'countryname' => 'Brazil',
'currencyname' => 'Brazilian Real',
'code' => '55'
},
'FR' => {
'symbol' => 'EUR',
'status' => 'offline',
'countryname' => 'France',
'currencyname' => 'Euro',
'code' => '33'
}
};
Is it possible using fetchall_hashref DBI method? Or do I have to go the traditional way, looping through each row and constructing the hash on the fly?
No, it cannot be done using fetchall_hashref. But you can iterate over the hash values and delete the key:
delete $_->{countryiso} for values %$hash;
I had this same problem but was using multiple keys on fetchall_hashref, so I had to go deeper in the hash references. Not exactly rocket science, but here it is:
(...)
my #keys=('key1','key2','key3');
my $result_ref=$sth->fetchall_hashref(\#keys);
remove_key_values($result_ref,\#keys);
(...)
sub remove_key_values {
my ($href_values,$aref_keys) = (#_);
foreach my $hk (keys %$href_values) {
foreach my $ak (#$aref_keys) {
if ($ak eq $hk) {
delete $href_values->{$hk};
}
}
if (exists $href_values->{$hk} and ref($href_values->{$hk}) eq 'HASH') {
remove_key_values($href_values->{$hk},$aref_keys);
}
}
}