Create multidimensional array from one mysql table - json

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.

Related

Updating table with multiple rows

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();
}

Sql request to get an ordered menu

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";
}

laravel REST api post method

im making an api in laravel but when i send from a post request it display nothing it work only when i send the values in the url what im i doing wrong here is my code !
$user = new userInscription;
$user->nom = Request::get('name');
$user->pseudo = Request::get('pseudo');
$user->userId = Request::get('userId');
$user->hasFiat = Request::get('hasFiat');
$user->optin = Request::get('optin');
$user->mail = Request::get('mail');
$pseudo = Input::get('pseudo');
$userId = Input::get('userId');
$hasFiat = Input::get('hasFiat');
if($pseudo == '' || $hasFiat == '' )
{
return Response::json( array(
'status' => 'ko',
'message' => 'missing mandatory parameters')
);
}
else if($userId == '')
{
if( $user->save() )
{
$id = DB::table('user')
->where('pseudo','LIKE',$pseudo)
->pluck('userId');
return Response::json(array(
'status' => 'ok',
'message' => 'success',
'userId' => $id
));
}
else
{
return Response::json(array(
'message' => 'error while saving this user !!',
));
}
}
Laravel REST-ful (Resourceful) controlllers has pre-configured routes (can be re-configured):
According to : http://laravel.com/docs/controllers#resource-controllers
+-----------+---------------------------+---------+------------------+
| Verb | Path | Action | Route Name |
+-----------+---------------------------+---------+------------------+
| GET | /resource | index | resource.index |
| GET | /resource/create | create | resource.create |
| POST | /resource | store | resource.store |
| GET | /resource/{resource} | show | resource.show |
| GET | /resource/{resource}/edit | edit | resource.edit |
| PUT/PATCH | /resource/{resource} | update | resource.update |
| DELETE | /resource/{resource} | destroy | resource.destroy |
+-----------+---------------------------+---------+------------------+
Referencing the table each of the Verb must correspond to the action method in the controller.
For example if your Resourceful Route is registered as:
Route::resource('user', 'userInscriptionController');
Then to POST to user resource, you need to have userInscriptionController#store action (i.e. method called store() in your userInscriptionController.
To avoid manually creating each of these actions, you can use Laravel's artisan controller:make
php artisan controller:make userInscriptionController
which will generate all these actions for you, then you just need to fill in your logic to complete the resource.
From your comment, you are using
Route::resource('user', 'userInscriptionController');
which will generate following routes
Verb | Path | Action | Route Name
------------------------------------------------------------------------
GET | /resource | index | resource.index
GET | /resource/create | create | resource.create
POST | /resource | store | resource.store
GET | /resource/{resource} | show | resource.show
GET | /resource/{resource}/edit | edit | resource.edit
PUT/PATCH | /resource/{resource} | update | resource.update
DELETE | /resource/{resource} | destroy | resource.destroy
And as you can see, the only action allowing post is store. So you should use this one or add post route for an other method like this :
Route::post('your_url', array('as' => 'your_route_name', 'uses' => 'YourController#yourMethod'));
I hope it's clear now
if request is GET then:
if (Request::isMethod('get'))
{
$user = new userInscription;
$user->nom = Request::get('name');
$user->pseudo = Request::get('pseudo');
$user->userId = Request::get('userId');
$user->hasFiat = Request::get('hasFiat');
$user->optin = Request::get('optin');
$user->mail = Request::get('mail');
$pseudo = Input::get('pseudo');
$userId = Input::get('userId');
$hasFiat = Input::get('hasFiat');
if($pseudo == '' || $hasFiat == '' )
{
return Response::json( array(
'status' => 'ko',
'message' => 'missing mandatory parameters')
);
}
else if($userId == '')
{
if( $user->save() )
{
$id = DB::table('user')
->where('pseudo','LIKE',$pseudo)
->pluck('userId');
return Response::json(array(
'status' => 'ok',
'message' => 'success',
'userId' => $id
));
}
else
{
return Response::json(array(
'message' => 'error while saving this user !!',
));
}
}
}
===================
if request is POST then:
if (Request::isMethod('post'))
{
$user = new userInscription;
$user->nom = Request::post('name');
$user->pseudo = Request::post('pseudo');
$user->userId = Request::post('userId');
$user->hasFiat = Request::post('hasFiat');
$user->optin = Request::post('optin');
$user->mail = Request::post('mail');
$pseudo = Input::post('pseudo');
$userId = Input::post('userId');
$hasFiat = Input::post('hasFiat');
if($pseudo == '' || $hasFiat == '' )
{
return Response::json( array(
'status' => 'ko',
'message' => 'missing mandatory parameters')
);
}
else if($userId == '')
{
if( $user->save() )
{
$id = DB::table('user')
->where('pseudo','LIKE',$pseudo)
->pluck('userId');
return Response::json(array(
'status' => 'ok',
'message' => 'success',
'userId' => $id
));
}
else
{
return Response::json(array(
'message' => 'error while saving this user !!',
));
}
}
}

Joining table data

I have a table of:
id title type expl bubble_content onfocus req dorder label mirror
1 Fullname 1 1 Your fullname Yes 0 0 0 NULL
Then another table of:
id fieldid relid dorder
4 1 2 0
5 1 1 0
How would I join the two tables so that the result would be something like:
0 => array(
'id' => 1,
'title' => 'Fullname',
.... etc ....
'relid' => 2,
'relid' => 1),
1 => array(
.... etc ....
))
I've tried using INNER JOIN / LEFT JOIN but this produces two rows/arrays for each relid, I would really like all the data for the particular fieldid to exist within the same array, as illustrated above.
You can't have 2 keys with the same name in an array. On your example you have 2 'relid', the second one will overwrite the first.
You can code PHP so that it merges those rows into one:
$output = array();
while ($row = mysql_fetch_assoc($result))
{
// Capture all values for this row first.
// If this is the new row from the first table, store.
if (!isset($output[$row['id']]))
{
$output[$row['id']] = $row;
// Make a new array for relids.
$output[$row['id']]['relids'] = array();
}
$output[$row['id']]['relids'][] = $row['relid'];
}
Your output array will look like this:
0 => array(
'id' => 1,
'title' => 'Fullname',
.... etc ....
'relids' => array(2, 1),
1 => array(
.... etc ....
))

Showing JSON data from database to view

This is an example of my table product and category in database:
tbl_categories
|id_category | name........| slug........|
|1...........| Hanger .....| hanger .....|
|2...........| Lamp .......| lamp .......|
|3...........| Merchandise | merchandise |
|4...........| Storage ....| storage ....|
tbl_products
id_products | id_category | name .....| slug .....| images
1 ..........| 1 ..........| Hanger asd| hanger-asd|json
2 ..........| 1 ..........| Hanger asd| hanger-dsa|json
3 ..........| 1 ..........| Hanger asd| hanger-das|json
4 ..........| 1 ..........| Hanger asd| hanger-sad|json
where the content of images is json_encoded like this one:
id_product : 1
{
"7b8d9fbfe384b1b6e4cfb0da473df8e5": {
"alt": "jhonson hanger",
"caption": "",
"filename": "7b8d9fbfe384b1b6e4cfb0da473df8e5.jpg",
"primary": true
},
"f7d225c85590012f91bad32dd8adaa3d": {
"alt": "jhonson hanger",
"caption": "lorem ipsum lorem ipsum dolor siamet ameticioud",
"filename": "f7d225c85590012f91bad32dd8adaa3d.jpg"
}
}
etc.
First thing I want is to get every product to be shown on my ecommerce product page, so in my controller products:
function index()
{
$data = array(
"keyword" => "sadasdasd",
"description" => "asdasdasd",
"content" => "product",
"title" => "BALOK Official :: Product"
);
$products = $this->model_product->get_all_products();
$data['products'] = $products;
$this->load->view("product", $data);
}
in my model_product:
function get_all_products()
{
$this->db->select
("
tbl_product.name AS prod_name,
images,
tbl_product.slug AS prod_slug,
tbl_categories.slug AS cat_slug
");
$this->db->from("tbl_products");
$this->db->join("tbl_categories", "tbl_categories.id_category = tbl_product.id_category");
$this->db->order_by("prod_name", "ASC");
$query = $this->db->get();
if($query->num_rows() > 0)
{
return $query->result();
}
else
{
return false;
}
}
How to display product name, prod_slug, cat_slug and only one images['filename] for every product in my view, and if "primary = true" than show the primary image else show the first images. example in array maybe images[0];.
I have checked the data from fields images with this code:
foreach ($products as $prod)
{
$prod->images = json_decode($prod->images);
print_r($prod->images);
}
and that shows stdClass Object like this:
stdClass Object
(
[43f8cd2ba0fcb96453b43b36b6a4f759] => stdClass Object
(
[filename] => 43f8cd2ba0fcb96453b43b36b6a4f759.jpg
[alt] =>
[caption] =>
[primary] => 1
)
)
stdClass Object
(
[f7d225c85590012f91bad32dd8adaa3d] => stdClass Object
(
[filename] => f7d225c85590012f91bad32dd8adaa3d.jpg
[alt] => jhonson hanger
[caption] => lorem ipsum lorem ipsum dolor siamet ameticioud
[primary] => 1
)
[7b8d9fbfe384b1b6e4cfb0da473df8e5] => stdClass Object
(
[filename] => 7b8d9fbfe384b1b6e4cfb0da473df8e5.jpg
[alt] => jhonson hanger
[caption] =>
)
)
stdClass Object
(
[29c2100ff85ec538e17c6d68fafbd43d] => stdClass Object
(
[filename] => 29c2100ff85ec538e17c6d68fafbd43d.jpg
[alt] =>
[caption] =>
[primary] => 1
)
[8d4ecb9c4dc369febe70019586f3d570] => stdClass Object
(
[filename] => 8d4ecb9c4dc369febe70019586f3d570.jpg
[alt] =>
[caption] =>
)
[dc4358c470c33f20206afc180a28ae5b] => stdClass Object
(
[filename] => dc4358c470c33f20206afc180a28ae5b.jpg
[alt] =>
[caption] =>
)
)
That stdobject makes me confused.
update.
in view i write this:
foreach ($products as $prod)
{
echo $prod->prod_name.' - '.$prod->prod_slug.' - '.$prod->cat_slug.'<br>';
}
it success display what i want;
Book Cabinets Wood - book-cabinets-wood - storage
Brand New Hanger Jhonson - brand-new-hanger-jhonson - hanger
Flash Wood - flash-wood - merchandise
Gantungan baju dari kayu - gantungan-baju-dari-kayu - hanger
Shaman Lamp - shaman-lamp - lamp
Storage Wood Shelf - storage-wood-shelf - storage
Wood Lamp - wood-lamp - lamp
Wood Lamp Transcending - wood-lamp-transcending - lamp
Yoyo Kayu - boneka-kayu-lucu - merchandise
the images it still in json format, and i dont know how to manipulate it, im just thinking about array_value, to convert json_data after decoded to array, maybe?
If you want to have the first image or the one that has primaryset to true you need to do something like this:
function getPrimaryImage($images) {
//initialize the finalImage with 'null'
$finalImage = null;
//Iterate over all images
foreach( $images as $image ) {
//check if primary is set and if it is true
if( isset($image->primary) && $image->primary ) {
//if primary set the finalImage and exit the loop
$finalImage = $image;
break;
} else if( $finalImage == null ) {
//if primary is not set or false and the finalImage is not set, then set to the current image (first one)
$finalImage = $image;
}
}
return $finalImage;
}
foreach ($products as $prod)
{
$prod->images = json_decode($prod->images);
$prod->primaryImage = getPrimaryImage($prod->images);
}
EDIT
I modified your code above to show the filename:
foreach ($products as $prod)
{
$prod->images = json_decode($prod->images);
$prod->primaryImage = getPrimaryImage($prod->images);
echo $prod->prod_name.' - '.$prod->prod_slug.' - '.$prod->cat_slug.' '.$prod->primaryImage->filename.'<br>';
}
You loop over the whole list having the first image set as your final result. If you find a image that has primary set to true you replace your $finalImage and exit the loop.
You could place this in a function that accepts images as parameters and return $finalImage as result.
Do you have a compelling reason to use JSON data? If not, simply create a new table named tbl_products_images and JOIN it with the previous JOIN.