I'm new in node.js and I'm getting some data from database (actions). Every action has a number of images registered in another table so that I can get them by action id.
I'm trying to add an array of these images to the correponding action so that I can loop through them in the frontend.
In php I use to do like this:
$return = array();
$images = array();
$image = array();
$select = "SELECT id, title, description FROM actions WHERE id = ? order by id DESC";
$stmt = $mysqli->prepare($select);
$stmt->bind_param('s', $id);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($id, $title, $description);
while ($stmt->fetch()) {
$registers = array(
"id" => $id, "title" => $title, "description" => $description, "images" => $image
);
$selectImages = mysqli_query($mysqli, "SELECT id, image FROM action_images WHERE action_id = '" . $id . "' ");
while ($row = $selectImages->fetch_array(MYSQLI_BOTH)) {
$images = array("imageID" => $row['id'], "image" => $row['image']);
array_push($registers["images"], $images);
}
$return[] = $registers;
}
Is there something similar in javscript/node.js? I tried some things described in the code below but nothing worked as expected.
This is my controller:
async selectActions(req, res) {
let actionData = [];
conn.execute('SELECT * FROM actions',
function (err, results, fields) {
// console.log(err);
// console.log(results);
if (err == null) {
results.forEach(action => {
conn.execute('SELECT * FROM actions_images WHERE actionID = ?',
[action.id],
function (imagesError, imagesResults, ImagesFields) {
// I've tried some things like:
actionData = [results, imagesResults];
// and
actionData = [...results, ...imagesResults]
// and
results.push(imagesResults)
// but nothing had the expected results displayed in the image below
console.log(actionData);
}
);
});
return res.json(results);
} else {
return res.json('error fetching actions from database: ' + err);
}
});
},
Action images array must be another item in each action item:
Try this...
Promise.all(results.map((action, index) => {
return Promise(resolve => {
conn.execute('SELECT * FROM actions_images WHERE actionID = ?',
[action.id],
function(imagesError, imagesResults, ImagesFields) {
results[index].action_images = imagesResults;
resolve(true);
}
);
})
}))
console.log(results);
Related
I'm creating an app in PHP to create an API and in reactjs for the front-end.
The server-side is responsible for making calls to WHM/cPanel APIs by returning the processed result to the client-side.
Now I start the background process by using shell_exec (that is called once for “scan”) and for following requests (made by JS) I get the data from the database that I think is slower and more heavy for the server.
The background process can take up to a couple of minutes, and from the client-side I receive an update every second of the currently processed data.
The background process retrieves the list of cPanel accounts that will be stored in the database; and for each one, checks the email account(s) and stores some data in the database. This process could take more or less depending on the response time of the cPanel API, that is called once for the list of accounts, and for each account.
Now the accounts are about 120.
There is another way to run a PHP process in background and access the variables of that process (without involving PHP server extensions)?
PHP
class Api
{
public function getInfo(int $id = null)
{
//check for running complete scans if id is not provided by user
$id = $id === null ? $this->db->select('scans', ['id'], ['status' => 'running', 'type' => 'complete', 'ORDER' => ['id' => 'DESC'], 'LIMIT' => 1])[0]['id'] : $id;
if (!$id) {
$this->db->insert('scans', [
'type' => 'complete',
'status' => 'running',
'start' => date('Y-m-d H:i:s.u')
]);
$id = $this->db->id();
/**Start the getting the info in background */
shell_exec("php " . __DIR__ . DS . "load-all-info.php " . $id . " > /dev/null 2>/dev/null &");
return ege(['status' => 'success', 'process_id' => $id, 'proc_status' => 'running']);
}
//get process status and end time
$query = $this->db->select('scans', ['status', 'scan_data'], ['id' => $id])[0];
$procStatus = $query['status'];
$scanData = $query['scan_data'];
if ($scanData) {
$scanData = json_decode($scanData, true);
return ege(['status' => 'success', 'process_id' => $id, 'proc_status' => $procStatus, 'data' => $scanData]);
}
//get disk infos
$cpusers = $this->db->select('cpusers', ['user', 'domain', 'plan', 'disk_total', 'disk_used', 'disk_perc'], ['scan_id' => $id]);
//get emails infos
$emails =
$this->db->select('emails', ['login', 'user', 'email_total', 'email_used', 'email_perc', 'cpuser'], ['scan_id' => $id, 'ORDER' => 'cpuser']);
//define array with disk infos and emails inside them
$data = array();
foreach ($cpusers as $cpuser) {
$arrEmails = [];
if (!empty($emails)) {
foreach ($emails as $email) {
if ($cpuser['user'] !== $email['cpuser']) {
continue;
}
$email += ['status' => $this->setStatus($email['email_perc'], $email['email_total'], $email['email_used'])];
array_push($arrEmails, $email);
}
}
$cpuser += ['status' => $this->setStatus($cpuser['disk_perc'], $cpuser['disk_total'], $cpuser['disk_used'])];
$cpuser += ['emails' => $arrEmails];
array_push($data, $cpuser);
}
if ($procStatus == 'complete' && !$scanData) {
$dbData = ['date' => date('Y-m-d H:i:s.u'), 'data' => $data];
$this->db->update('scans', [
'scan_data[JSON]' => $dbData
], ['id' => $id]);
}
return ege(['status' => 'success', 'process_id' => $id, 'proc_status' => $procStatus, 'data' => $data]);
}
JS
do {
startTime()
//genarate fetch request
await api
.get({ func: 'info', args: { scanId: scanId }, structure: '/{scanId}' })
.then(myJson => {
...
}
})
.catch(err => {})
diffTime()
//sleep for x ms
await new Promise(r => setTimeout(r, timeDiff))
} while (scanCompleted === false)
In Java land I would do something like
#Transactional
FormData update(FormData updatedFormData) {
var result = dsl
.query(
"select id, formData from formStuff where formId = ?",
updatedFormData.formId
);
var result2 = dsl
.query(
"select reference from referenceStuff where formStuffId = ?",
result.get("id")
);
var mergedFormData = merge(
result.get("formData"),
result2.get("reference"),
updatedFormData
);
var updateResult = dsl
.executeUpdate(
"update formStuff set formData = ? where id = ?",
mergedFormData,
result.get("id")
);
return mergedFormData;
}
I am trying to do something similar on Expo SQLite but it started to appear like callback hell
async function update(db, updatedFormData) {
return
new Promise((resolve, reject) => {
db.transaction(
(tx) => {
tx.executeSql(
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId],
(tx1, resultSet1) => {
tx1.executeSql(
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id],
(tx2, resultSet2) => {
const mergedFormData = merge(
resultSet1.rows.item(0).formData,
resultSet2.rows.item(0).reference,
updatedFormData
);
tx2.executeSql(
"update formStuff set formData = ? where id = ?",
[mergedFormData, resultSet1.rows.item(0).id],
(tx3) => {
resolve(mergedFormData)
},
(tx3, error) => {
console.log(error);
reject(error);
return true;
}
)
}
(tx2, error) => {
console.log(error);
reject(error);
return true;
}
)
},
(tx1, error) => {
console.log(error);
reject(error);
return true;
}
);
},
(tx, error) => {
console.error(error);
reject(error);
},
() => {
resolve();
}
);
Wrap each call to executeSql in its own promise.
Generally it is better to then wrap each promise in its own function (which you can give a sensible name and arguments).
Then await the return value of each function in turn (which lets you assign the resolved value to a variable and pass it to the next function).
Maybe you can chain queries using recursion on success/error, something like:
function executeBatch(queries, ready) {
if (queries.length === 0) {
console.log('all done!');
ready();
return;
}
const queryId = `q${queries.length}`;
const query = queries.shift();
console.log(`starting ${query}`);
console.time(queryId);
const continueExecution = () => {
console.timeEnd(queryId);
executeBatch(queries);
};
db.transaction(tx =>
tx.executeSql(
query,
[],
() => {
console.log('ok');
continueExecution();
},
() => {
console.error('fail');
continueExecution();
}
)
);
}
executeBatch(['query1','query2',...], doneCallback);
UPDATE this does not work due to https://github.com/nolanlawson/node-websql/issues/46
I just did a quick hack of a module to do this for me. Likely there are better ways of doing this with extending classes and what not (plus I am limited to JavaScript though I use VSCode's TS check with JSDoc)
// #ts-check
/**
* This module provides an async/await interface to Expo SQLite. For now this provides a functional interface rather than a class based interface.
* #module
*/
/**
* #typedef {import("expo-sqlite").SQLTransaction} SQLTransaction
* #typedef {import("expo-sqlite").SQLError} SQLError
* #typedef {import("expo-sqlite").SQLResultSet} SQLResultSet
* #typedef {(tx: SQLTransaction)=>Promise<any>} AsyncTransactionCallback
*/
import * as SQLite from "expo-sqlite";
/**
*
* #param {string} name
* #param {string} [version]
* #returns {Promise<SQLite.WebSQLDatabase>}
*/
export async function openDatabaseAsync(name, version) {
return new Promise((resolve) => {
SQLite.openDatabase(name, version, "", 0, (db) => {
resolve(db);
});
});
}
/**
*
* #param {SQLTransaction} tx transaction
* #param {string} sqlStatement
* #param {any[]} [args]
* #return {Promise<SQLResultSet>}
*/
export async function executeSqlAsync(tx, sqlStatement, args = []) {
return new Promise((resolve, reject) => {
tx.executeSql(
sqlStatement,
args,
(txObj, resultSet) => {
resolve(resultSet);
},
(error) => {
console.log(error);
reject(error);
return true;
}
);
});
}
/**
*
* #param {SQLite.WebSQLDatabase} db
* #return {(fn: AsyncTransactionCallback)=>Promise<any>}
*/
export function txn(db) {
return async (f) => {
new Promise((resolve, reject) => {
db.transaction(
(tx) => {
f(tx)
.then((result) => resolve(result))
.catch(reject);
},
/**
*
* #param {SQLError} error error
*/
(error) => {
reject(error);
},
() => {
resolve();
}
);
});
};
}
For my scenario it is used like this
async function update(db, updatedFormData) {
return await txn(db)(async (tx) => {
// there's probably a less retarded way of writing this using bind or something
const resultSet1 = await executeSqlAsync(tx,
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId]);
const resultSet2 = await executeSqlAsync(tx,
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id]);
const mergedFormData = merge(
resultSet1.rows.item(0).formData,
resultSet2.rows.item(0).reference,
updatedFormData
);
await executeSqlAsync(tx,
"update formStuff set formData = ? where id = ?",
[mergedFormData, resultSet1.rows.item(0).id],
);
return mergedFormData;
});
};
Maybe I'll figure out how to tweak it so it looks like this in the future, but for now what I have does what I need.
async function update(db: AsyncSQLiteDatabase, updatedFormData: FormData) {
return await db.asyncTransaction<FormData>(async (tx) => {
// there's probably a less retarded way of writing this using bind or something
const resultSet1 = await tx.executeSqlAsync(
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId]);
const resultSet2 = await tx.executeSqlAsync(
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id]);
const mergedFormData = merge(
resultSet1.rows.item(0).formData,
resultSet2.rows.item(0).reference,
updatedFormData
);
await tx.executeSqlAsync(
"update formStuff set formData = ? where id = ?",
[mergedFormData, resultSet1.rows.item(0).id],
);
return mergedFormData;
});
};
Use async/await to make things feel synchronous. Assuming your sqlite lib is promise ready.
async function update(db, updatedFormData) {
const tx = await db.transaction();
try {
const resultSet1 = await tx.executeSql(
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId]
);
const resultSet2 = await tx.executSql(
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id]
);
// ... etc
tx.commit();
}
catch (e) {
tx.rollback();
}
}
I'm making a time card app.
but const updatesql = "UPDATE users SET ? WHERE ?"; does not work well...
How to use MySQL's PlaceHolder and Node.js ?
Especially, double placeholder SET ? WHERE ? .
const checksql = "SELECT * FROM users WHERE name = ?";
const checkname = "SELECT status FROM users WHERE name = ";
const updatesql = "UPDATE users SET ? WHERE ?";
if (req.body.begin_button) {
var name_checked = await query(checksql,{ name:
req.body.input_name });
var status_checked = await query(checkname,{ name: req.body.input_name});
if (name_checked == 0) {
var results = await query(sql, { id: id, name:
req.body.input_name, email: req.body.input_mail, start: now, status: 1 });
} else {
console.log("error");
};
} else if (req.body.finish_button){
if (name_checked != 0){
var results = await query(updatesql, { end: now, status: 0});
} else {
console.log("error");
}
} else if (req.body.start_button) {
if (status_checked == 1) {
var results = await query(updatesql, {restbegin: now,status: 2 });
} else {
console.log("error");
};
} else if (req.body.end_button) {
if (status_checked == 2) {
var results = await query(updatesql, { restend: now, status: 3 });
} else {
console.log("error");
};
};
You can do something like this
var sql = 'SELECT * FROM customers WHERE name = ? OR address = ?';
con.query(sql, [name, adr], function (err, result) {
if (err) throw err;
console.log(result);
});
this is the json error i am getting
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
if (e.which == 9) {
here is my controller method
// To add fabric and colour data in order entry form of 'Combos' product type..
function add_ajax_combos($newFabricName = '', $fabricSavedCallback = '', $product_type_id = '', $field = '') {
if (!empty($this->data)) {
$this->autoRender = false;
$this->layout = 'ajax';
$name = $this->data['FabricRange']['name'];
$product_id = $this->data['FabricRange']['product_id'];
$chk_range_exists = $this->FabricRange->find('count', array('conditions' => array('FabricRange.name' => $name, 'FabricRange.product_id' => $product_id)));
if ($chk_range_exists == 0) {
$chk_same_name_already_exist = $this->FabricRange->find('first', array('conditions' => array('FabricRange.name' => $name), 'fields' => array('range_id')));
$update_flag = false;
if ($chk_same_name_already_exist) {
$this->data['FabricRange']['range_id'] = $chk_same_name_already_exist['FabricRange']['range_id'];
} else {
$update_flag = true;
}
$name = $this->data['FabricRange']['colour'];
$usable_width = $this->data['FabricRange']['usable_width'];
$range_id= $this->data['FabricRange']['range_id'];
$chk_range_color_exists = $this->FabricRangeColour->find('count', array('conditions' => array('FabricRangeColour.name' => $name, 'FabricRangeColour.usable_width' => $usable_width)));
}
if ($chk_range_color_exists == 0) {
$usable_width = $this->data['FabricRange']['usable_width'];
$purchase_wizard_code = $this->data['FabricRange']['purchase_wizard_code'];
$can_turn = $this->data['FabricRange']['can_turn'];
$pattern_repeat = $this->data['FabricRange']['pattern_repeat'];
$deleted = $this->data['FabricRange']['deleted'];
$discontinued = $this->data['FabricRange']['discontinued'];
$ds = $this->FabricRange->getDataSource();
$ds->begin($this->FabricRange);
$this->FabricRange->create();
if ($this->FabricRange->save($this->data)) {
if ($update_flag) {
$id = $this->FabricRange->id;
$this->FabricRange->saveField('range_id', $id);
$fabric_ranges_id = $id;
} else {
$fabric_ranges_id = $this->data['FabricRange']['range_id'];
}
$fabricColourId = 0;
if (isset($this->data['FabricRange']['colour_id']) == false || strlen(trim($this->data['FabricRange']['colour_id'])) == 0) {
// new colour
$this->FabricRangeColour->create();
if ($this->FabricRangeColour->save(array('FabricRangeColour' => array('name' => $name, 'fabric_ranges_id' => $fabric_ranges_id, 'usable_width' => $usable_width, 'purchase_wizard_code' => $purchase_wizard_code
, 'can_turn' => $can_turn, 'pattern_repeat' => $pattern_repeat, 'deleted' => $deleted, 'discontinued' => $discontinued))) == false) {
$ds->rollback($this->FabricRange);
return json_encode(array("status" => "FAILURE"));
}
$fabricColourId = $this->FabricRangeColour->id;
} else {
$fabricColourId = $this->data['FabricRange']['colour_id'];
}
$ds->commit($this->FabricRange);
return json_encode(array("status" => "SUCCESS", 'fabric' => $this->data['FabricRange']['name'], 'id' => $this->FabricRange->id, 'colour_id' => $fabricColourId, 'colourname' => $this->data['FabricRange']['colour'], 'fabric_category_id' => $this->data['FabricRange']['fabric_categories_id']));
} else {
$ds->rollback($this->FabricRange);
return json_encode(array("status" => "FAILURE"));
}
} else {
return json_encode(array("status" => "FAILURE"));
}
}
$this->layout = 'ajax';
$fabricCategories = $this->FabricCategory->getFabricCategoryList('list');
$fabricColours = $this->FabricRangeColour->getAllAsList();
$this->set('fabric_name', $newFabricName);
$result_pr = $this->ProductType->find('first', array(
'fields' => array('product_id'), 'conditions' => array('id' => $product_type_id)
));
$product_id_title = $result_pr['ProductType']['product_id'];
$this->set('product_id', $product_id_title);
$this->set('field', $field);
$this->set(compact('fabricCategories', 'fabricColours', 'fabricSavedCallback'));
}
here is my ctp
function ajaxSubmit(field) {
var requestUrl = '<?php echo $this->Html->url(array('controller'=>'fabric_ranges','action'=>'add_ajax_combos'),true);?>';
if (validateForm()) {
$.post(requestUrl, $('#FabricRangeAddAjaxCombosForm').serialize(),
function(data) {
data = JSON.parse(data);
console.log(data);
if (data.status == 'SUCCESS') {
if (typeof <?php echo $fabricSavedCallback;?> != 'undefined') {
<?php echo $fabricSavedCallback;?>(data.id, data.fabric, data.colour_id, data.colourname, data.fabric_category_id);
if (field == 1) {
$("#fabricsAutoComplete").val(data.fabric);
$("#OrderEntryItemRollerBlindComboFabricFrontBlind").val(data.id);
$("#OrderEntryItemRollerBlindComboColours").val(data.colourname);
$("#OrderEntryItemRollerBlindComboFabricColourFrontBlind").val(data.colour_id);
$("#OrderEntryItemRollerBlindComboFabricCategoriesId").val(data.fabric_category_id);
}
else {
$("#fabricsAutoCompleteBack").val(data.fabric);
$("#OrderEntryItemRollerBlindComboFabricBackBlind").val(data.id);
$("#OrderEntryItemRollerBlindComboColoursBack").val(data.colourname);
$("#OrderEntryItemRollerBlindComboFabricColourBackBlind").val(data.colour_id);
$("#OrderEntryItemRollerBlindComboFabricCategoriesId2").val(data.fabric_category_id);
}
}
} else {
alert('An error occured while adding the fabric. The new fabric wasn\'t saved.');
}
}
);
}
}
here is the method that calls for closeandupdatefabrics
function closeAndUpdateFabrics(fabricId, fabric,color, colourId) {
$('#addFabricDialog').dialog('close');
$("#fabricsAutoComplete").val(fabric);
$("#OrderEntryItemRollerBlindComboFabricFrontBlind").val(fabricId);
$("#OrderEntryItemRollerBlindComboColours").val(color);
var fabricsIdBox = document.getElementById('OrderEntryItemRollerBlindComboFabricFrontBlind');
setFabriCategory(fabricsIdBox);
loadFabricColours(fabricsIdBox, 'OrderEntryItemRollerBlindComboFabricColourFrontBlind', function() {
$("#OrderEntryItemRollerBlindComboFabricColourFrontBlind").val(colourId);
});
}
My problem is the ajax box is not getting closed and give me a json error in console.
I want to return the $validator->errors() and include another element "message" to hold the status of the project.
for example:
if ($validator->fails()) {
$response = array('data' => $validator->errors());
$status = 'failed';
// I tried this but it didn't work
// $response = array('data' => $response, 'status' => 'failed')
} else {
$status = (Phone::create($post_data)) ? "success" : 'failed';
$response = array('status' => $status);
}
return Response::json($response);
So in javascript side I would load something like:
if (data.status == 'success') { console.log('success'); }
else {
$.each(data, function(index, value) {
message += '<div class="text-warning"> <span class="glyphicon glyphicon-exclamation-sign"></span> ' + (data[index]) + '</div>';
});
}
The method errors() returns a MessageBag instance, you need to retrieve an array:
if ($validator->fails())
{
$response = array(
'data' => $validator->errors()->all(),
'status' => 'failed'
);
}
else
{
$status = (Phone::create($post_data)) ? "success" : 'failed';
$response = array('status' => $status);
}
return Response::json($response);