PHP - How to order data by ascending or descending - php

I have a form that allows a user to retrieve information from a database.
Currently users are able to retrieve the information from the database with the code I have provided (probably terrible coding but it works).
Now I would like the data to display in an asc or dsc order depending on what the user selects on the form. But im not too sure how to go about doing that, any help in the right direction would be much appreciated!
The PHP that retrieves the information:
$sql = "SELECT RunnerID, EventID, Date, FinishTime, Position, CategoryID, AgeGrade, PB FROM Results";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
echo "<table><tr><th>RunnerID</th><th>EventID</th><th>Date</th><th>FinishTime</th><th>Position</th><th>CategoryID</th><th>AgeGrade</th><th>PB</th></tr>";
while($row = $result->fetch_assoc()) {
echo "<tr><td>" . $row["RunnerID"]. "</td><td>" . $row["EventID"]. " </td><td>" . $row["Date"]. " </td><td>" . $row["FinishTime"]. " </td><td>" . $row["Position"]. " </td><td>" . $row["CategoryID"]. " </td><td>" . $row["AgeGrade"]. " </td><td>" . $row["PB"]. " </td></tr>";
}
echo "</table>";
}
else {
echo "Error";
}

Rather than having PHP do the work, it would be more standard to modify your SQL query to include ORDER BY xxxx ASC or ORDER BY xxxx DESC. If you have a form which you want to allow them to sort by, a decent alternative might be something like:
$userSelectedProperty = $_GET['ThePropertyIWantToSortOn'];
$userSelectedDirection = $_GET['TheDirectionIWantToSortBy'];
$sql = "SELECT RunnerID, EventID ... FROM Results ORDER BY "; // Note the extra space
switch ($userSelectedProperty)
{
case 'name': { $sql .= "RunnerName"; break; }
case 'age': { $sql .= "AgeGrade"; break; }
...
default: { $sql .= "RunnerID"; break; } // By default, let's sort by ID
}
if ($userSelectedDirection == 'desc')
{
$sql .= " DESC"; // Note the preceding space.
}
else
{
$sql .= " ASC"; // Note the preceding space.
}
This way, the sorting is already done on the database, so you're retrieving the data back in the order you want it already. The reason that I'm using switch statements and if statements even when it looks like it's the same data is that it avoids a vulnerability known as SQL injection. Basically, if you just directly use the variable to build the SQL query, there's nothing stopping some malicious user from providing code that lets them modify the query's intention. For instance, if they passed in something like MyName; DELETE FROM Results; as the value for the form input used to generate the field $userSelectedProperty, the resulting $sql string would be something like SELECT RunnerID ... FROM Results ORDER BY MyName; DELETE FROM Results;. Well, that's really bad because to SQL, it's two valid statements. This isn't the best or most comprehensive definition if this is your first time hearing about it, but if you're curious about how to avoid it, I would suggest looking into SQL injection guides.

$fields = array( "RunnerID", "EventID", "Date", "FinishTime", "Position", "CategoryID" );
$orderby = 0;
$asc = 0;
if( isset($_GET['orderby'])) $orderby = (int)$_GET['orderby'];
if( isset($_GET['asc'])) $asc = (int)$_GET['asc'];
$sql = "SELECT RunnerID, EventID, Date, FinishTime, Position, CategoryID, AgeGrade, PB FROM Results";
$sql .= " ORDER BY " . $fields[$orderby];
$sql .= " " . $asc ? "ASC" : "DESC";

Rather than having the server do the work, consider having the browser do it instead. Offloading work to the browser is an extremely useful skill, especially with very busy websites.
Put the data into a table without regard for sort order. Then use JavaScript to implement sorting. There are plenty of ways of doing this, for instance:
var tbl = document.getElementById('mytable'),
trs = tbl.rows, l = trs.length, i, tmp = [];
for( i=0; i<l; i++) tmp.push(trs[i]);
tmp.sort(function(a,b) {
// compare the rows how you want.
// return -1 if a comes before b
// return 1 if b comes before a
// return 0 if they are equal
});
for( i=0; i<l; i++) tbl.appendChild(tmp[i]);
You can also look into one of the many plug-ins out there on the internet to do this, such as Footable.
Of course, you can always support non-JS users (how outdated are they?) with:
<noscript>Sort ascending</noscript>
When that parameter is present, do the sorting server-side for them, as per the other answers. The overwhelming majority of the time, though, it will be done with JavaScript.

Related

SQL query not working but works in PHPMyAdmin

I have a web application and I'm trying to modify one of the queries. The query fetches information (from a table named voyage_list) and returns various fields.
I want to modify the query so that it is based on certain filters the user applies (which will be placed in the URL).
I can't get the query to work in the web application, but if I copy the query and execute it directly within PHPMyAdmin, it works fine.
$vesselFilter = $_GET['vesselFilter'];
$vesselArray = explode(',', $vesselFilter);
$arrayCount = count($vesselArray);
$sqlExtend = ' status = 1 AND';
foreach ($vesselArray as $value) {
$i = $i + 1;
$sqlExtend .= " vesselID = '$value'";
if ($i < $arrayCount){
$sqlExtend .= " OR";
}
}
$newQuery = "SELECT * FROM voyage_list WHERE" . $sqlExtend;
echo $newQuery;
$query = $db->query($newQuery)->fetchAll();
I appreciate the above is pretty messy, but it's just so I can try and figure out how to get the query to work.
Any help would be greatly appreciated!
Thanks
That query probably doesn't return what you think it does. AND takes precedence over OR, so it will return the first vessel in the list if the status is 1, and also any other vessel in the list, regardless of status.
You'd do better to create a query with an IN clause like this:
SELECT * FROM voyage_list WHERE status = 1 AND vesselID IN(8,9,10)
Here's some code to do just that:
$vesselFilter = $_GET['vesselFilter'];
// Validate data. Since we're expecting a string containing only integers and commas, reject anything else
// This throws out bad data and also protects against SQL injection.
if (preg_match('/[^0-9,]/', $vesselFilter)) {
echo "Bad data in input";
exit;
}
// filter out any empty entries.
$vesselArray = array_filter(explode(',', $vesselFilter));
// Now create the WHERE clause using IN
$sqlExtend = 'status = 1 AND vesselID IN ('.join(',', $vesselArray).')';
$newQuery = "SELECT * FROM voyage_list WHERE " . $sqlExtend;
echo $newQuery;
$query = $db->query($newQuery)->fetchAll();
var_dump($query);

Dynamic select query in mysql?

I am using mysql as my database and php as server side language.
As we know that we can select data from database using select query.
Below example is important!!
select * from table
select name from table
select name,salary from table where salary > 10000
etc..........
now, for different select query of a table we need different select method. because every time select * is not good because it takes a huge time.
Now, My Question is how write dynamic single get method of a single table by which we can achieve our requirement (shown in example...)?
I will pass the array of parameters in the argument of the function.. for ex. in php
public get($arr)
{
//code goes here
}
I want to fetch the $arr and want to change the sql dynamically..
Don't want any join query just simple select as shown in above..
Depending on how you want to do it, you can do something like this:
public get($arrfield, $arrtable, $arrwhere)
{
$str = "SELECT " . $arrfield . " FROM " . $arrtable . " WHERE " . $arrwhere;
return $str;
// You can return the query string or run the query and return the results
}
Trust me, to write all three queries is not that too hard a job that have to be avoided at any cost.
Please, do not obfuscate a precious SQL language into unreadable gibberish. Not to mention innumerable security breaches of your approach.
What you should think of is a function that lets you to use parameters. Thus, better make our function like this
function mysqli($mysqli, $query, $params, $types = NULL)
{
$statement = $mysqli->prepare($select);
$types = $types ?: str_repeat('s', count($params));
$statement->bind_param($types, ...$params);
$statement->execute();
return $statement;
}
and run your every query as is, only providing placeholders instead of variables
select * from table:
you'll never need a query like this
select name from table
$names = mysqli($db, "select name from table")->get_result->fetch_all();
`select name,salary from table:
$stmt = mysqli($db, "select name from table where salary > ?", [10000]);
$names = $stmt->get_result->fetch_all();
See - the query itself is the least code portion. Your concern should be not SQL but useless reprtitive parts of PHP code used to run a query and to fetch the data.
Here is the structure of the dynamic query.Please add required validation.You can add 'Or' clause also.On the basis of parameter or data type you can do it. Like
public SelectTable($arrfield, $table, $arrwhere, $arrgroup)
{
if(!empty($arrfield))
{
$fields = implode('`,`',$arrfield);
}
else
{
$fields = '*';
}
if(!empty($arrwhere))
{
foreach($arrwhere as $fieldName=>$fieldValue)
{
if(is_array($fieldValue))
{
$cond .= "`fieldName` in (". implode(',',$fieldValue) );
}
else
$cond .= "`fieldName` = '" . addslashes($fieldValue)."'";
}
}
else
{
$cond = '1';
}
if(!empty($arrgroup))
{
$groupBy .= " group by ";
foreach($arrgroup as $field=>$value)
{
$groupBy .= $field . " " . $vale;
}
}
}
$str = "SELECT " . $fields . " FROM " . $table . " WHERE " . $cond . $groupBy;
return $str;
// You can return the query string or run the query and return the results
}

PHP Query response to string

I'm running a PHP query which returns several rows / columns. (Im returning the columns: name, quantity, unit, producer, notes) * X rows (depending on how many rows were found in the database).
$sql = "SELECT products.name, products.unit, lists.quantity, lists.producer, lists.notes FROM lists,products WHERE lists.familyid ='$familyid' AND lists.productid = products.id ";
$sqlmessage=mysql_query($sql);
Now i would like to arrange this response into a STRING, in order to email it using mail($to,$subject,$message,$headers).
Im trying to use the following function, however im not getting the correct list but rather alot of " fetchColumn(name) "
The Broken function:
for ($i=0; $i<mysql_num_rows($sqlmessage); ++$i){
while ($row = mysql_fetch_array($sqlmessage)){
$name = $row->fetchColumn($i);
$message .= "$name";
$message .= ", ";
}
}
What do i need to change to get the correct information out ? Been searching for a day now and trying different things without any success.'
You are using two loops (i dont know why) and object to mysql_fetch_array() .Do you mean something like this?:
while($row = mysql_fetch_array($sqlmessage))
{
$name = $row['name'];
$message .= $name;
$message .= ", ";
}

Building an SQL query using multiple (optional) search fields

I have a form that is going to be used to search through a table of support tickets.
the user can search from a few difficult optional fields.
Date (to/from)
Ticket Status
Engineer
Ticket Contact
I'm wondering what is the best way to deal with optional search filters. So I have a query that takes in parameters from the user. So if the user searches using both the from and to dates then the query would want to include BETWEEN. So do I have to write a different query for if the user searches for only from. or another query when the user has not added any date parameters? Then what if the status dropdown is blank? Is that another query?
Any help to clear this up would be great!
Jonesy
Build your query in parts. Start with whatever is constant in your query, and add on more SQL depending on what extra conditions:
$query = "SELECT ...
FROM ...
WHERE [where conditions that are always going to be present]";
if (isset($_POST['date_from']) && isset($_POST['date_to']))
{
$query .= ... // query code for dealing with dates
}
if (isset($_POST['status']))
{
$query .= ... // deal with status
}
// etc.
// Once you have your query fully built, execute it
$result_set = mysql_query($query);
This code is obviously just a skeleton, but that's how I would construct my query.
Hard to say without knowing what sort of DB abstraction you're using, but assuming you're hand-writing the SQL, it's fairly simple, just build up sections of your where clause individually for each variable. (Assuming here that your vars are already escaped/quoted.)
$where_clause = array();
if (!empty($date_from)) {
$where_clause[] = "table.date >= $date_from";
}
if (!empty($date_to)) {
$where_clause[] = "table.date <= $date_to";
}
if (!empty($status)) {
$where_clause[] = "status = $status";
}
$query = 'select * from table where ' . join(' and ', $where_clause);
This is an elegant way that I use alot and wish will help you too:
$q = 'SELECT * FROM Users';
$buildQ = array();
if (empty($idOrName) === false) {
$buildQ[] = '(userid = "' . $idOrName . '" OR username LIKE "%' . $idOrName. '%")';
}
if (empty($nickname) === false) {
$buildQ[] = 'nickname="' . $nickname . '"';
}
if (empty($salary) === false) {
$buildQ[] = 'salary="' . $salary . '"';
}
// ... any other criterias like above if statements
if (count($buildQ) === 1) {
$q .= ' WHERE ' . $buildQ[0];
} else if (count($buildQ) > 1) {
$count = 0;
foreach ($buildQ as $query) {
if ($count === 0) {
$q .= ' WHERE ' . $query;
} else {
$q .= ' AND ' . $query;
}
$count++;
}
}
I think it would be better if You generate query dynamically at runtime based on which fields are filled. So You could make some helper which appends specific query fragments if only one date is passed and the other one is null, or when both are passed and so on.

Sql query only printing first row

I am coding in php and the code takes data from an array to fetch additional data from a mysql db. Because I need data from two different tables, I use nested while loops. But the code below always only prints out (echo "a: " . $data3[2]; or echo "b: " . $data3[2];) one time:
foreach($stuff as $key)
{
$query3 = "SELECT * FROM foobar WHERE id='$key'";
$result3 = MySQL_query($query3, $link_id);
while ($data3 = mysql_fetch_array($result3))
{
$query4 = "SELECT * FROM foobar_img WHERE id='$data3[0]'";
$result4 = MySQL_query($query4, $link_id);
while ($data4 = mysql_fetch_array($result4))
{
$x += 1;
if ($x % 3 == 0)
{
echo "a: " . $data3[2];
}
else
{
echo "b: " . $data3[2];
}
}
}
}
First and foremost, improve your SQL:
SELECT
img.*
FROM
foobar foo
INNER JOIN foobar_img img ON
foo.id = img.id
WHERE
foo.id = $key
You will only have to iterate through one array.
Also, it appears that you're actually only selecting one row, so spitting out one row is expected behavior.
Additionally, please prevent yourself from SQL injection by using mysql_real_escape_string():
$query3 = "SELECT * FROM foobar WHERE id='" .
mysql_real_escape_string($key) . "'";
Update: As Dan as intimated, please run this query in your MySQL console to get the result set back, so you know what you're playing with. When you limit the query to one ID, you're probably only pulling back one row. That being said, I have no idea how many $keys are in $stuff, but if it spins over once, then it will be one.
You may be better off iterating through $stuff and building out an IN clause for your SQL:
$key_array = "";
foreach($stuff as $key)
{
$key_array .= ",'" . mysql_real_escape_string($key) . "'";
}
$key_array = substr($key_array, 1);
...
WHERE foo.id IN ($key_array)
This will give you a result set with your complete list back, instead of sending a bunch of SELECT queries to the DB. Be kind to your DB and please use set-based operations when possible. MySQL will appreciate it.
I will also point out that it appears as if you're using text primary keys. Integer, incremental keys work best as PK's, and I highly suggest you use them!
You should use a JOIN between these two tables. It the correct way to use SQL, and it will work much faster. Doing an extra query inside the loop is bad practice, like putting loop-invariant code inside a loop.

Categories