appending data from loop php - javascript

I'm having an issue appending data from loop. I am trying to append the result that is clicked on, but when theres multiple results every result is appended on click. I am using ajax to retrieve search results. Below is my php section. I think the issue is that every result has the same class so it appends every one, but I can't figure out how to identify only one
if(isset($_POST['inviteSearch'])) {
include_once "connect.php";
$con = getConnection();
$search = "%{$_POST['inviteSearch']}%";
$query = "SELECT FirstName,LastName FROM `Profiles` WHERE FirstName LIKE ? OR LastName LIKE ? ";
$stmt = $con->prepare($query);
$stmt->bind_param("ss", $search ,$search);
if(!($stmt->execute())) {
die(mysql_error());
} else {
$result = $stmt->get_result();
$output = '<ol>';
if(mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)){
$name = $row['FirstName'] . " " . $row['LastName'];
$output .= "
<li id='invitetoken'>
<p>$name</p>
</li>
<script>
$(document).ready(function(){
$('#invitetoken').click(function(){
$('.invitedSection').show();
$('.invitedList').append('<li><p>$name</p><button>×</button></li>');
});
});
</script>
";
}
} else {
$output .= "<h3>We could not find $search</h3>";
}
$output .= '</ol>';
echo "$output";
}
}

To start with the obvious, please use parameterized queries you are currently vulnerable to SQL injection attacks.
You are appending the same js repeatedly in your while loop, that is generally bad practice. I'm going to break this up a bit, since you have both php and JS issues
PHP
Here just generally cleaning up and using parameterized queries and class based mysqli (in line with the above suggestion comments). I also moved the javascript out of the loop as repeating it over and over doesn't achieve anything. Obviously haven't tested my changes but they are fairly boilerplate (see the docs)
if (!isset($_POST[''inviteSearch']) {
return;
}
$connection = new mysqli('host', 'user', 'password', 'schema');
$param = filter_input(INPUT_POST, 'inviteSearch', FILTER_SANITIZE_STRING);
// setup query
$query = "SELECT CONCAT(FirstName, ' ', LastName) AS name
FROM Profiles
WHERE (FirstName LIKE ? OR LastName LIKE ?)";
// prepare statement
$stmt = $connection->prepare($query);
// need variable bind for each tokenized parameter
$stmt->bind_param('ss', $param, $param);
$stmt->execute();
$results = $stmt->get_result();
$output = makeOutput($results);
// Siloing your presentational elements for clarity.
function makeOutput($results) {
if ($results->num_rows === 0) {
return "<h3>We could not find $search</h3>";
}
$output = '<ol>';
while ($row = $results->fetch_assoc()) {
$output .= "<li class='invitetoken'><p>{$row['name']}</p></li>";
}
$output .= '</ol>';
return $output;
}
JavaScript
A few things here, enclosed the function into an IIFE to keep it namespace contained, generally a good practice. The changes to the click handler let the callback handle the update dynamically for any matching class that is clicked. I reference this (in this case it is helpful to think of this as event.target) and use it to find the name to be appended. from there it is pretty similar to what you already had. Of note, I'm using let for variable definitions and template literal syntax for the string data, but you should look up their availability and decide whether you need to worry about old browser support :).
(function($) {
$(document).ready(function(){
$('.invitetoken').on('click', function() {
let name = $(this).children('p').text()
, string = `<li><p>${name}</p><button>×</button></li>`;
$('.invitedSection').show();
$('.invitedList').append(string);
});
});
})(jQuery);

Related

How to create an optimized sql query for multiple dropdown filtering in php?

Am new to php and working on a project. At the beginning there was 2 filters for which I didn't worry much in writing the sql query with 4 conditions. But now I've 8 different dropdowns and I need to write an optimized sql for filter functionality. As per conditions to be written counted, I need to write 256 conditions which is the worst idea at all.
Here the problem is, there is no mandatory filed to choose for filtering. This giving me the more problem in applying different approaches.
Is there any other alternative to achieve this issue? what would be the best idea for optimized query.
Example Code
if($_REQUEST['action']=='action_report'){
$v1 = $_POST['v1'];
$v2 = $_POST['v2'];
if(!empty($v1) && !empty ($v2)){
$sql = "SELECT * FROM TABLE WHERE v1=$v1 AND v2=$v2 AND action='action_report'";
}elseif(!empty($v1) && empty($v2)){
$sql = "SELECT * FROM TABLE WHERE v1=$v1 AND action='action_report'";
}elseif(empty($v1) && !empty($v2)){
$sql = "SELECT * FROM TABLE WHERE v2=$v2 AND action='action_report'";
}elseif(empty($v1) && empty($v2)){
$sql = "SELECT * FROM TABLE WHEREAND action='action_report'";
}
}
The code would look like this:
$sql = 'SELECT * FROM TABLE WHERE ';
$first = true;
foreach($_POST as $paramName => $value) {
if ($first) {
$sql .= "{$paramName}='{$value}'";
$first = false;
continue;
}
$sql .= " AND {$paramName}='{$value}'";
}
Since the $_POST is an array of the incoming variables you can go through on it with a simple foreach cycle and attach it to the SQL query string. The first possible parameter wont need an AND operator, so you have to handle it differently, that's why the $first variable is for.
However this code is has SQL injection vulnerabilities, so it's better to attach the parameter name and the value to the SQL string like this:
$sql .= ' AND ' . mysqli_real_escape_string($connection, htmlspecialchars($paramName)) . "='" . mysqli_real_escape_string($connection, htmlspecialchars($value)) . "'";
You will also receive empty values, you wouldn't like to attach to the SQL query string. So the final code needs to handle that too, and after a bit of formatting it would look like this:
$sql = 'SELECT * FROM TABLE WHERE ';
$first = true;
foreach($_POST as $paramName => $value) {
$protectedParamName = mysqli_real_escape_string($connection, htmlspecialchars($paramName));
$protectedValue = mysqli_real_escape_string($connection, htmlspecialchars($value));
if (empty($value)) {
continue;
}
if ($first) {
$sql .= "{$protectedParamName}='{$protectedValue}'";
$first = false;
continue;
}
$sql .= " AND {$protectedParamName}='{$protectedValue}'";
}
In the example the $connection variable is a mysqli object:
$connection = new mysqli(
$dbConfig['host'],
$dbConfig['user'],
$dbConfig['password'],
$dbConfig['databaseName'],
$dbConfig['port']
);
The foreach($_POST as $paramName => $value) goes through on each $_POST array values, so if you would you don't want some fields to be used in the SQL query, then you can use blacklist filtering, where you specify if the $paramName is in the blacklist, then you wont attach it to the SQL query.
For example:
$blackList = [
'action'
];
foreach($_POST as $paramName => $value) {
if (in_array($paramName, $blackList)) {
continue;
}
}

Handling special characters in and out of mysql

I'm building a leaflet web app which stores messages assigned to geolocations.
I add data one line at a time by sending it from javascript to PHP using:
$name = mysqli_real_escape_string($conn, $_POST['NAME']);
$latitude = mysqli_real_escape_string($conn, $_POST['LATITUDE']);
$longitude = mysqli_real_escape_string($conn, $_POST['LONGITUDE']);
$message = mysqli_real_escape_string($conn, $_POST['MESSAGE']);
$sql = "INSERT INTO geoData (NAME,LATITUDE,LONGITUDE,MESSAGE)
VALUES ('$name', '$latitude', '$longitude', '$message')";
I get the data back out using PHP to echo the data back to javascript using:
$conn = mysqli_connect($dbServername,$dbUsername, $dbPassword, $dbName);
if(! $conn ){
die('Could not connect: ' . mysqli_error());
}
$sql = 'SELECT * FROM geoData';
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)) {
$rows[] = $row;
}
} else {
echo "0 results";
}
mysqli_close($conn);
<script type="text/javascript">
var data = JSON.parse( '<?php echo json_encode($rows); ?> ' );
</script>
This works fine UNLESS the message has special characters such as apostrophes for example 'Dave's dogs's bone'. This creates an error
What is the best practise for such an application which uses PHP and javascript. I think I need some way to encode the special characters which javascript can then decode and display.
The error comes as:
Uncaught SyntaxError: missing ) after argument list
<script type="text/javascript">
var data = JSON.parse( '[{"NAME":"The Kennel","LATITUDE":"50.7599143982","LONGITUDE":"-1.3100980520","MESSAGE","Dave's Dog's Bone"}] ' );
</script>
Many thanks
The issue is your JSON.parse() which isn't needed at all in this case.
Change:
var data = JSON.parse( '<?php echo json_encode($rows); ?> ' );
to
var data = <?= json_encode($rows); ?>;
JSON.parse() is for parsing stringified json. Echoing the result from json_encode() will give you the correct result straight away.
Side note
I would recommend adding $rows = []; before your if (mysqli_num_rows($result) > 0) or json_encode($rows) will throw an "undefined variable" if the query doesn't return any results (since that variable currently is created inside the loop when you're looping through the results).
Side note 2
When making database queries, it's recommended to use parameterized Prepared Statements instead of using mysqli_real_escape_string() for manually escaping and building your queries. Prepared statements are currently the recommended way to protect yourself against SQL injections and makes sure you don't forget or miss to escape some value.
You produce that error yourself by adding ' in json. If you want check that use this:
JSON.parse( '[{"NAME":"The Kennel","LATITUDE":"50.7599143982","LONGDITUTE":"-1.3100980520","type":"bad","reason":"Dave\'s Dog\'s Bone","improvement":"","reviewed":"0"}] ' );
And if you want correct that in main code use str.replace(/'/g, '"') for your var data, before parse it to json.

Accessing JSON results from jQuery Ajax

I'm working on a web application to maintain the administration for a restaurant kind of type. The idea is to make new orders, put order items in that, check finance overviews etc...
I've also got a function to see all the orders in a list, when you select one of them, the order data (such as the name, emailadress, location of the customer) shows up in a another element inside the document.
I'm doing that with this function, every tr inside the has been given a custom attribute; the order_id. When selecting that, a class is given, called selectedRow.
function select_order(order) {
var item = $(order);
if (!item.hasClass("selectedRow")) {
if (!selectedOrderInformation.is(":visible")) {
switchScreen(selectedOrderInformation, financeOverview);
}
item.parent().find(".selectedRow").removeClass("selectedRow");
item.addClass("selectedRow");
selectedOrderInformation.html("loading......");
$.ajax({
url: "includes/functions/select-order.php",
type: "get",
data: {order_id: item.attr("data-order-index")},
success: function (data) {
selectedOrderInformation.html(data);
$("#delete-order-btn").prop("disabled", false);
}
});
} else {
console.log("DEBUG: Row is already selected");
}
}
The usage of that function is by doing this:
$("#list tbody tr").click(function () {
select_order(this);
});
At the first place, i was deploying all the HTML data via PHP. This took a pretty long time, it could take from 500ms to about 1 second. In my opinion thats pretty long.
I was doing that like this (select-order.php):
if (!empty($_GET['order_id'])) {
$order_id = $_GET['order_id'];
$order_data = Database::getInstance()->get_all_data_by_order_id($order_id);
$order_items = Database::getInstance()->get_order_items_by_order_id($order_id);
while ($row = mysqli_fetch_array($order_data)) {
echo "<h1>Klant informatie</h1>";
echo "<p>Voornaam: " . $row['first_name'] . "</p>";
echo "<p>Achternaam: " . $row['last_name'] . "</p>";
echo "<p>Emailadres: " . $row['email_adress'] . "</p>";
echo "<p>Klant informatie: " . $row['customer_info'] . "</p>";
echo "<br>";
echo "<h1>Bestellingsinformatie</h1>";
echo "<p>Order informatie: " . $row['order_info'] . "</p>";
echo "<p>Locatie: " . $row['location'] . "</p>";
echo "<p>Gemaakt op: " . $row['created'] . "</p>";
}
echo "<br>";
echo "<table>";
echo "<thead>";
echo "<tr>";
echo "<th>Product naam</th>";
echo "<th>Hoeveelheid</th>";
echo "</tr>";
echo "</thead>";
while ($row = mysqli_fetch_array($order_items)) {
echo "<tr>";
echo "<td>" . $row['name'] . "</td>";
echo "<td>" . $row['quantity'] . "</td>";
echo "</tr>";
}
echo "</table>";
exit;
}
This goes together with the Database class with all the functions:
class Database extends mysqli
{
// single instance of self shared among all instances
private static $instance = null;
private $databaseHost = "";
private $databaseUser = "";
private $databasePassword = "";
private $databaseName = "";
public static function getInstance() {
if (!self::$instance instanceof self) {
self::$instance = new self;
}
return self::$instance;
}
public function __clone() {
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public function __wakeup() {
trigger_error('Deserializing is not allowed.', E_USER_ERROR);
}
function __construct() {
parent::__construct($this->databaseHost, $this->databaseUser, $this->databasePassword, $this->databaseName);
if (mysqli_connect_error()) {
exit('Connect Error (' . mysqli_connect_errno() . ') ' . mysqli_connect_error());
}
parent::set_charset('utf-8');
}
function get_all_data_by_order_id($order_id) {
$query = "SELECT customers.first_name,
customers.last_name,
customers.email_adress,
customers.customer_info,
orders.order_info,
orders.total_price,
orders.location,
orders.created
FROM customers
INNER JOIN orders ON customers.id = orders.customer_id
WHERE orders.id = {$order_id}";
return $this->query($query);
}
function get_order_items_by_order_id($order_id) {
$query = "SELECT `products`.`name`, `orders-items`.`quantity` FROM `orders-items`\n" . "INNER JOIN `products`ON `orders-items`.`products_id` = `products`.`id`\n" . "WHERE order_id=" . $order_id;
return $this->query($query);
}
}
Now someone told me i could better translate the data into json and return that, so i did this:
if (!empty($_GET['order_id'])) {
$order_id = $_GET['order_id'];
$order_data = Database::getInstance()->get_all_data_by_order_id($order_id);
$order_items = Database::getInstance()->get_order_items_by_order_id($order_id);
$rows = array();
while ($row = mysqli_fetch_array($order_data)) {
$rows[] = $row;
}
return json_encode($rows);
exit;
}
But as expected, nothing really happened. So i tried changing the javascript to this (trying it as a array because i'm returning it that way?), to deploy one piece of data:
$.ajax({
url: "includes/functions/select-order.php",
type: "get",
data: {order_id: item.attr("data-order-index")},
success: function (data) {
selectedOrderInformation.html(data['first_name']);
}
});
But that didn't work aswell.
Problems
The previous PHP code was to slow, so i had to find another way.
When trying to deploy HTML into the other screen, it doesnt do anything. It stays on the 'loading...' screen, so the success function was'nt reached.
Question
How can my piece of code be changed so it will actually deploy parts of the data from the mysql database?
In your $.ajax() call you should define what type your response data is expected to be, by adding the following parameter to the call:
dataType: 'json'
Also, you should try echo json_encode($rows); your data instead of returning it.
**Edit: you are receiving an array of arrays, so your original referencing in the success callback won't suffice. Having another look at your MySQL part, If you are only expecting one row to be returned by your query, then you can change your PHP to:
$row = mysqli_fetch_array($order_data);
echo json_encode($row); // instead of $rows
instead of the while loop. That way your selectedOrderInformation.html(data['first_name']); will most likely work.
To clean your query up a bit:
$query = "SELECT p.name, ot.quantity FROM orders-items AS ot
LEFT JOIN products AS p ON ot.products_id = p.id
WHERE ot.order_id = " . $order_id;
You could also switch your INNER JOIN to a LEFT JOIN in your "get order data" function. An inner join is absolutely useless here, as you'll have all your data paired based on the foreign keys anyways.
I would try secluding some of the codebase: try commenting out the Database::getInstance() calls, and supplementing some testdata into the processes. To put it short, fake a returned response, by declaring a $row = array('first_name' => 'Joe', 'order_date' => '2014-08-29 11:11:52', ...); and returning that. If its way faster, then your database server might be the bottleneck. If its still slow, then 500ms - 1000ms is actually argueably code related, it might be other hardware aspects that cause the problem. Or for example, do you have your jQuery library loaded from a CDN, or locally?
**Edit: As #Debflav pointed out (and I've also touched upon the matter), that your queries could benefit from not being executed as simple queries, but transforming them into prepared statements. For the full story you could start checking out PHP.net : Prepared Statements, or to keep it short:
Prepared statements look almost just like your everyday query, however variables are not just concatenated into the query string, rather bindings are used.
You use the database handler's prepare function instead of query - with this method, you are requesting the MySQL server to inspect your query and optimize it for later use (which will come handy if you're doing the same query over and over again, just with a few varying values).
For more detailed insights on the mechanics of prepared statements and how to get the hang of it for efficiently utilizing it in your projects I recommend you research the topic a bit, but as a quick conversion for your example at hand, it would look like this:
function get_all_data_by_order_id($order_id) {
$query = "SELECT c.first_name, c.last_name, c.email_adress, c.customer_info,
o.order_info, o.total_price, o.location, o.created
FROM customers AS c
LEFT JOIN orders AS o ON c.id = o.customer_id
WHERE o.id = :order_id";
$query_params = array(
':order_id' => $order_id
);
$preparedStatement = $this->prepare($query);
return $preparedStatement->execute($query_params);
}
and
function get_order_items_by_order_id($order_id) {
$query = "SELECT p.name, ot.quantity FROM orders-items AS ot
LEFT JOIN products AS p ON ot.products_id = p.id
WHERE ot.order_id = :order_id;";
$query_params = array(
':order_id' => $order_id
);
$preparedStatement = $this->prepare($query);
return $preparedStatement->execute($query_params);
}
And to reflect on how you would build up your JSON response with data including the order headers and the connected order-items would be:
if (!empty($_GET['order_id'])) {
$order_id = $_GET['order_id'];
$order_data = Database::getInstance()->get_all_data_by_order_id($order_id);
$order_items = Database::getInstance()->get_order_items_by_order_id($order_id);
$orderObject = array();
$orderObject['header'] = mysqli_fetch_array($order_data);
$orderObject['items'] = array();
while ($orderedItem = mysqli_fetch_array($order_items)){
$orderObject['items'][] = $orderedItem;
}
echo json_encode($orderObject);
}
This way your jQuery could look something as follows:
....
success: function (data) {
selectedOrderInformation.html('<h3>' + data['header']['first_name'] + '</h3><ul>');
$.each(data['items'], function(i, item) {
selectedOrderInformation.append('<li>' + item['name'] + ' x ' + item['quantity'] + '</li>');
});
selectedOrderInformation.append('</ul>');
}
....

PHP validation to check if record exists

I'm trying to create a validation for a form. When a user fills out the form, it is supposed to run a set of queries. The first is to check if a records already exists in the table. If it does exist, then it doesn't need to run the the next 2 queries, which are to INSERT and UPDATE.
I'm not sure what I am doing wrong, but the table already has an existing record. After checking the table, it still runs the INSERT and UPDATE queries. They should not fire. It should not do anything.
Here is my code: * I'm starting my code from the for loop, which is just taking an array of BOL numbers and CONTAINER numbers that the user manually selected. I exploded the array, but I will not show that code as I do not think it is necessary to show in this case *
<?php
for($i = 0; $i < $count; $i++)
{
$bolService = $bolArray[$i];
$conService = $conArray[$i];
$checkService = "SELECT * FROM import_service WHERE bol = '" . $bolService . "' AND container = '" . $conService . "'";
$checkSerRes = mysql_query($checkService);
$checkSerNum = mysql_num_rows($checkSerRes);
if($checkSerNum > 0)
{
$successService = false;
}
elseif($checkSerNum = 0)
{
$sql_query_string = mysql_query
("INSERT INTO import_service (bol, container) VALUES ('$bolService','$conService')");
$updateService = mysql_query ("UPDATE import_dispatch_details SET SERVICE = 'Y'
WHERE BOL_NUMBER = '$bolService' AND CONTAINER = '$conService')");
$successService = true;
}
}
// javascript fires an ALERT message in this next set of code
if($successService = true)
{
echo ("<script language='javascript'>
window.alert('Record has been saved')
window.location.href=''
</script>");
}
// if checkSerNum > 0, then it should skip the INSERT and UPDATE and fire the code below
elseif($successService = false)
{
echo ("<script language='javascript'>
window.alert('There was an error saving the record')
window.location.href=''
</script>");
}
?>
I'm not sure why this is not working correctly. I need this validation to work. I'm sure there is an alternative method, but this is what I got.
Please help me make this work.
Thank you in advance.
This elseif($checkSerNum = 0) needs to be elseif($checkSerNum == 0)
You're presently doing an assignment instead of a comparison.
Including if($successService = true) and elseif($successService = false) so add another = sign.
Add error reporting to the top of your file(s) which will help during production testing.
error_reporting(E_ALL);
ini_set('display_errors', 1);
http://www.php.net/manual/en/function.mysql-error.php
Footnotes:
mysql_* functions deprecation notice:
http://www.php.net/manual/en/intro.mysql.php
This extension is deprecated as of PHP 5.5.0, and is not recommended for writing new code as it will be removed in the future. Instead, either the mysqli or PDO_MySQL extension should be used. See also the MySQL API Overview for further help while choosing a MySQL API.
These functions allow you to access MySQL database servers. More information about MySQL can be found at » http://www.mysql.com/.
Documentation for MySQL can be found at » http://dev.mysql.com/doc/.
This isn't quite efficient (you are selecting * from your table, which you aren't using - waste of memory?). Why don't you do something like this:
for ($i = 0; $i < $count; $i++)
{
$bolService = $bolArray[$i];
$conService = $conArray[$i];
$recordExists = false;
$result = mysql_query("SELECT COUNT(*) AS recordCount FROM import_service WHERE bol = '" . $bolService . "' AND container = '" . $conService . "'");
if ($result) {
$row = mysql_fetch_assoc($result);
$recordExists = ($row['recordCount'] >= 1);
}
if ($recordExists)
{
$successService = false;
}
else
{
$sql_query_string = mysql_query
("INSERT INTO import_service (bol, container) VALUES ('$bolService','$conService')");
$updateService = mysql_query
("UPDATE import_dispatch_details SET SERVICE = 'Y'
WHERE BOL_NUMBER = '$bolService' AND CONTAINER = '$conService')");
$successService = true;
}
}
P.S. mysql_* is officially deprecated. Please use PDO or MySQLi. Also, your code is potentially open to SQL Injection.

json_encode a MySQL table and urldecode Each Row

Here's what I got. I have a MySQL table, in this case 'businesses' which has a many rows of urlencode() data. I am trying to use a php script (getTable.php) to grab the table urldecode() each row and then use a json_encode to send the data as an array back to javascript. Here's what i have so far.
$table = $_GET['table'];
$query = "SELECT * FROM $table";
$jsonOut = array();
while($result = mysql_fetch_array(mysql_query($query)))
{
foreach($result as &$value)
{
$value = urldecode($value);
}
$jsonOut[] = $result;
}
echo (json_encode($jsonOut));
Obviously, I'm doing something wrong because I'm causing an infinite loop and nothing ends up working. Any help wold be greatly appreciated.
Try this code:
$table = $_GET['table'];
$query = "SELECT * FROM $table";
$jsonOut = array();
$result = mysql_query($query);
while($result = mysql_fetch_array($result))
{
$jsonOut[] = $result;
}
echo (json_encode($jsonOut));
In fact the query has to be executed only once, not once for each iteration of the while loop, or else you'll end up with an infinite loop!
Also, as a side note, you should avoid using mysql_* functions as they're now deprecated, try to switch to PDO if you can.
EDIT:
If you need to urldecode each row, replace
$jsonOut[] = $result;
with
$jsonOut[] = array_map('urldecode', $result);
inside the while loop.
This is the infinite loop you're talking about
while($result = mysql_fetch_array(mysql_query($query)))
Try
$recordset=mysql_query($query);
while($result = mysql_fetch_array($recordset)){
}
Note: MySQL API is long deprecated, better move away from it before you are forced to
You should do like this:
$query = mysql_query($query);
while($result = mysql_fetch_array($query)) {
You could retrieve and decode your parameters with the following code. (Based on #Matteo Tassinari answer)
$table = $_GET['table'];
//This could be a SQL injection
$query = "SELECT * FROM " . mysql_real_escape_string($table);
$jsonOut = array();
$result = mysql_query($query);
while($result = mysql_fetch_array($result))
{
$newArray = array();
foreach ($result as $k => $v) {
$newArray[$k] = urldecode($v);
}
$jsonOut[] = $newArray;
}
echo (json_encode($jsonOut));

Categories