I have made a "live search bar" with php and javascript. if you enter a word in the search bar it searches the database (mysql).
index.php:
<input type="text" onkeyup="getMovie(this.value)"/>
<div id="movie"></div>
javascript.js:
function getMovie(value) {
$.post("getmovie.php",{partialMovie:value},function(data) {
$("#movie").html(data);
});
}
getmovie.php:
include_once "connection.php";
if($_POST['partialMovie']){
$partialMovie = $_POST['partialMovie'];
$sql = "SELECT title FROM movie WHERE title LIKE '%$partialMovie%'";
$stm = $db->prepare($sql);
$result = $stm->execute(array());
while($row = $stm->fetch(PDO::FETCH_ASSOC)) {
echo "<li>".$row['title']."</li>";
}
}
This works, but it is way to slow. I have only 3 "movies" in my database, but it takes like a second or two to show the results.
the titles of the 3 movies are: een twee drie.
but if i type "een" fast, after a second you see een, twee, drie. a second later you see: een twee. and another second later you see: een.
So my question is: is there a way to speed the search up or is there a way to stop searching if you type another letter?
Either lower your expectation, because 1 second for a request's round trip is not very improvable, or get the data as json at page load time and search against locally available json data. But if there are many records this might not be an option.
As you use PDO, bind your parameters to avoid sql injection.
Then use full text search in MySQL (select match against), it will be a lot faster than the like %% approach.
Doc : http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
HTML:
<input type="text" id="movie-autocomplete"/>
<div id="movie"></div>
JS:
$(document).ready(function(){
$('#movie-autocomplete').keyup(function(){
setTimeout(function(){
$.post(
"getmovie.php",
{
partialMovie: $(this).val()
},
function(data) {
$("#movie").html(data);
}
);
}, 500);
});
});
This will create a small delay and post an atual value within the field.
PHP:
include_once "connection.php";
if(!empty($_POST['partialMovie'])){
$partialMovie = $_POST['partialMovie'];
$sql = "SELECT title FROM movie WHERE title LIKE :movie ORDER BY title LIMIT 0, 10";
$stm = $db->prepare($sql);
$result = $stm->execute(array(':movie' => "%{$partialMovie}%"));
while($row = $stm->fetch(PDO::FETCH_ASSOC)) {
echo "<li>".$row['title']."</li>";
}
}
We have to bind the parameter to secure the SQL query and prevent SQL injection. Also You should ensure You have set the PDO to use real prepared statements.
To further speed all the process You should return only JSON data from the PHP and fill in the <ul> within Your JS code...
Related
I have a single page responsive HTML page. One section of the page has a product search. User can enter search criteria in a form and get back the results. The results are paged.
<form id="filterform" name="filterform" method="post" action="./loaddata.php">
...
</form>
The form is submitted by Ajax and the results are returned as an HTML fragment that gets dynamically inserted into the DOM to refresh the results.
That's all working OK, but sometimes the results from loaddata.php are very slow, usually the first time called from the page.
In loaddata.php I'm using a Sqlite3 database. It is read only. Something like the following:
$filename = "../datafile.sqlite3";
$db = new SQLite3($filename);
$q = "SELECT distinct productId, title, price, name FROM datatable LIMIT 16";
$results = $db->query($q);
while ($row = $results->fetchArray()) {
echo "<h1>Results</h1>";
}
$db->close();
Is there a way to make loaddata.php load and stay in memory to respond to the form submit? It seems like it will reload every submit.
Depending on the size of the datatable you can save it on SESSION, use functions like shmop_open/shmop_write/shmop_read or (better yet) use some cache service like redis, but in one way or another the data will be stored and processed every time you pass by that place. I would tere the page in pieces, create one webservice to deal with the form post and another to show the form.
The easiest (not necessary safest or best way to do) would be ...
PS: I assume you are working with PDO and (obviously) the code bellow is an elaboration, it will not actually work
if (isset($_SESSION['db_datatable'])) {
foreach ($_SESSION['db_datatable'] AS $item) {
echo "<h1>".$item['some_row']."</h1>";
}
} else {
$filename = "../datafile.sqlite3";
$db = new SQLite3($filename);
$q = "SELECT distinct productId, title, price, name FROM datatable LIMIT 16";
$results = $db->query($q);
while ($row = $results->fetchArray()) {
$_SESSION['db_datatable'][] = $row;
echo "<h1>Results</h1>";
}
$db->close();
}
Hope I have been of some help. Cheers!
I have a table called Stats. Stats has 4 columns: id, ip, count, and date. count is the number of clicks the ip has clicked on the ads on my site. Each time the user clicks on an ad, their count number will increase by 1. How do I increase their count number by 1 and update that in the database? Here is my code and it's not working for some reason. When I click on the div, it doesn't refresh...so the whole block of code isn't executing. Note that I've already captured the user's ip when they entered my site, this is the part where if they clicked my ad, the count is incremented and updated in the database.
<script>
$(document).ready(function()
{
$("#ad").click(function()
{
<?php
$queryTot = "SELECT `count` AS total FROM Stats WHERE ip = '$ip'";
$resultTot = $db->query($queryTot);
$data = $resultTot->fetch_assoc();
$count = $data["total"] + 1;
$insert = $db->prepare("UPDATE Stats(count) WHERE ip = '$ip' VALUES(?)");
$insert->bind_param('i', $count);
$insert->execute();
$insert->close();
?>
location.reload();
})
})
</script>
There is a lot of points to consider in your answer.
But very possibly you should use an AJAX solution to do it, and avoid every SQL queries in your HTML pages, because keeping SQL queries there definitely is not a good pratice, according all basic security and code maintenance POVs.
It is not possible to re-write your code here rightly without knowing your project structure, or even your idea, and you must take this answer as an important start point.
Basically, you must define in your server-side application a method which returns pure data, in a JSON format for example, and then use AJAX to access this method according an event (a click, for example), receive its response and modify your client-side, probably with jQuery and JS.
I hope this answer can help you.
I've written a short example for you that you could continue to build on to accomplish what you need. Here's the basic idea.
HTML
<input type="hidden" id="ip" value="<?php echo $_SERVER['REMOTE_ADDR'];?>"/>
jQuery
var ip = $('#ip').val();
$(document).ready(function(){
$('#ad').on('click',function(){
$.ajax({
type: 'POST',
url: 'ajaxUpdateDatabase.php',
data: 'ip='+ ip,
success: function(response){
console.log(response)
//send the user to the ad's page here for example
//you could use location.href='url-to-add-here';
//or if you really want to reload the page for a reason I fail to understand, use location.reload();
}
});
});
});
PHP (ajaxUpdateDatabase.php)
//please note that using UPDATE requires that there is previously an entry with this IP address.
//example using PDO...
$sql = 'SELECT * FROM stats WHERE ip = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute(array($_POST['ip']));
if($stmt -> rowCount() > 0){
$sql = 'UPDATE stats SET count = count + 1 WHERE ip = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute(array($_POST['ip']));
}
else{
//ip-address was not found in the database
//insert query goes here
}
I am using MySQLi multi_query to work with several select statemets at a time.
What i would like to know is how to handle results, so i will be able to use them later in code.
Example:
<?php
//connection stuff
$query = "SELECT name, surname FROM database1;";
$query.= "SELECT car, year, type FROM database2 WHERE carID='1';";
$query.= "SELECT product, price FROM database3;";
if ($mysqli->multi_query($query)) {
if($result = $mysqli->store_result()) {
while($row = $result->fetch_row()) {
--> what to do here?
}
}
}
?>
<html>
<div id='persona'>
<?php
foreach() {
--> print name + surname
}
?>
</div>
<div id='cars'>
<?php
foreach() {
--> print car + year + type
}
?>
</div>
<div id='product'>
<?php
foreach() {
--> print product + price
}
?>
</div>
</html>
One more thing, prepared statements are not possible when using multiple_query, right?
There really is no benefit in putting unrelated queries together in one multi query call. In fact, the risk of getting hit by a SQL injection is way bigger! The regular query function does only allow one query per call, so it is impossible to inject something into a SELECT statement, ending it prematurely and then add a DELETE.
With multi_query, this is possible.
Additionally, you have to fetch and end each query, and then it's gone. You you cannot change between the query results at will, they have to be fetched in exactly the order they were issued.
The better way is to just execute independent simple queries. This would allow you to use prepared statements as well, and as long as you are not getting HUGE amounts of data back, it will probably use the same amount of memory and not annoy anyone.
Context
I am adding autocomplete function to the search engine at motherpipe.co.uk.
I want it to only suggest terms that begin with the letters typed in by the user. For example, if the user types "lon" the function should return ten suggestions that begin with "lon".
I have about 50,000 terms in a local database
I have managed to get the autocomplete up and running, working with a separate php script that calls the database (sql).
Problem
My problem is that to begin with the top ten listings in the database (based on id) are shown regardless of what the user is typing in. It is only after the user types a further letter that suggestions appear correctly.
Question
How can I modify either jquery or the php code to make sure that ONLY terms that begin with what is typed in are returned (and then only the top ten terms in that subset based on the id.)?
HTML
<script type="text/javascript" src="/scripts/autocomplete/autocomplete/jquery.js"></script>
<script type="text/javascript" src="/scripts/autocomplete/autocomplete/jquery.autocomplete.js"></script>
<script>
$(document).ready(function(){
$("#tag").autocomplete("/scripts/autocomplete/autocomplete/autocomplete.php", {
selectFirst: false,
minChars: 2
});
});
</script>
PHP
<?php
$q=$_GET['q'];
$mydata=mysql_real_escape_string($q);
$mysqli=mysqli_connect('localhost','username','password','languages') or die("Database Error");
$sql="SELECT searchterms FROM topterms WHERE searchterms LIKE '$mydata%' ORDER by id";
$result = mysqli_query($mysqli,$sql) or die(mysqli_error());
if($result)
{
while($row=mysqli_fetch_array($result))
{
echo $row['searchterms']."\n";
}
}
?>
With the new version of jQuery autocomplete when you use a URL as a source (like you do):
You must return JSON data:
String: When a string is used, the Autocomplete plugin expects that
string to point to a URL resource that will return JSON data. It can
be on the same host or on a different one (must provide JSONP).
The GET argument you receive is 'term', not 'q'
The Autocomplete plugin does not filter the results, instead a query
string is added with a term field, which the server-side script should
use for filtering the results.
The proper way to use real escape with mySQL in PHP is to use it after the connection has been established, in your case:
$q = $_GET['term'];
$mysqli = mysqli_connect('localhost','username','password','languages');
if (!$mysqli or $mysqli->connect_error) die("Database Error");
$mydata = $mysqli->real_escape_string($q);
$sql = "SELECT searchterms FROM topterms WHERE searchterms LIKE '$mydata%' ORDER by id";
$result = mysqli_query($mysqli,$sql);
$json = array();
if ($result and $result->num_rows > 0) {
while($row=mysqli_fetch_array($result)) {
$json[] = $row['searchterms'];
}
}
header('Content-Type: application/json'); // You can skip that because IE doesn't really like it
echo json_encode($json);
I am using a jQuery/JS/Ajax script to dynamically fill SELECT boxes from my database, with each subsequent SELECT box being populated based on the the previous SELECT.
Eg. Someone selects 'England' from my first SELECT, this then populates the next SELECT with towns in England etc.
The trouble I'm having is getting the second SELECT function to recognise a variable. It's a lot of code so I don't want to paste it all but... this is the first function which populates the first SELECT box on page load:
function getTierOne()
{
$tblname = $_SESSION['country'];
$result = mysql_query("SELECT DISTINCT county FROM $tblname ORDER BY county")
or die(mysql_error());
while($tier = mysql_fetch_array( $result ))
{
echo '<option value="'.$tier['county'].'">'.$tier['county'].'</option>';
}
}
This is fine, $tblname is a posted $_SESSION variable containing the users country, obvs.
The problem is 'onBlur' of this first SELECT box, a second SELECT box is brought up using this JS function and PHP call:
$(document).ready(function()
{
$('#wait_1').hide();
$('#drop_1').change(function()
{
$('#wait_1').show();
$('#result_1').hide();
$.get("func.php",
{
func: "drop_1",
drop_var: $('#drop_1').val()
},
function(response)
{
$('#result_1').fadeOut();
setTimeout("finishAjax('result_1', '"+escape(response)+"')", 400);
});
return false;
});
});
...
if (isset($_GET['func'])&& $_GET['func'] == "drop_1") {
drop_1($_GET['drop_var']);
}
Which calls this function:
function drop_1($drop_var)
{
include_once('functions.php');
$result = mysql_query("SELECT DISTINCT town FROM $tblname WHERE county='$drop_var'")
or die(mysql_error());
echo '<select name="drop_2" id="drop_2">
<option value=" " disabled="disabled" selected="selected">Select Your Town/Nearest Town</option>';
while($drop_2 = mysql_fetch_array( $result ))
{
echo '<option value="'.$drop_2['town'].'">'.$drop_2['town'].'</option>';
}
echo '</select>';
echo '<input type="submit" name="submit" value="Continue" />';
}
But this function will not recognise my $tblname variable, even if i use Global! The only variable it recognises is $drop_var, which is the result of the first SELECT box.
Does anyone have any idea how i can pass the $tblname variable into
function drop_1($drop_var) { ...
If you're using PHP sessions to store the user's country (and from line1 of getTierOne() it looks like you are), you should be able to add the line to the start of your drop1() PHP function:
function drop_1($drop_var)
{
$tblname = $_SESSION['country'];
include_once('functions.php');
...
Jquery should be sending your user's cookie along with the GET parameters (func and dropvar), which allows PHP to know which session belongs to the user, and give them their session variables back (You can check this with the "Network" tab of Firebug / Chrome inspector- look for the call to func.php after onBlur has fired and look for the "Cookie" request header).
Alternatively, if you're not using sessions, but you have the country stored on the client-side (eg. as the first "drop_0" select box?), you could have jquery pass the selections from tier1 (county) select boxes in the get call. ie.
$.get("func.php",
{
func: "drop_1",
country: "VALUE OF COUNTRY HERE",
drop_var: $('#drop_1').val()
},
function(response)
{ ...
And modify your server-side PHP code to accept 2 arguments instead of one.
From a maintainability point of view, consider not using multiple tables (one per country) and instead use a single table with an additional "country" column. It's rarely a good idea to have multiple identically structured tables, MySQL generally handles millions of rows much better than hundreds of tables, and it makes joins and making changes to your tables' structure more difficult.
You also should be sanitizing the inputs, not using $GET['drop_var'] directly in a SQL query, or it'll break on quotes and opens you up to SQL injection attacks. (Apologies if you have a good reason for doing either of these: Obviously you know more about your specific requirements than I do!)