Let me explain fast what i want to do!
I want to show similar rows from my database by a PHP term.
I have a table called "games" and a column called "title" that titles are looks like "Rockstar - GTA V".
So i want to remove all words after dash and use new string as keyword to search in database.
My CMS use this code to show post title inside the loop:
$_smarty_tpl->tpl_vars['game']->value['title']
I just found a code to convert "Rockstar - GTA V" to "Rockstar":
<?php $mygame = strstr($_smarty_tpl->tpl_vars['game']->value['title'], '-', true); echo($mygame); ?>
When i put this code in my "Single template file", it work fine and trim the title as i want and it work good in every game's single page.
So i want to make a section in single page to display all games made by that company (i mean that trimmed word from title). I tried some codes and nothing! This is what i tried:
<?php
$connect = mysqli_connect("localhost", "dbname", "dbpass", "dbuser");
$connect->set_charset('utf8mb4');
mysqli_set_charset($link, 'utf8mb4');
$gamecompany = strstr($_smarty_tpl->tpl_vars['game']->value['title'], '-', true);
$query = 'SELECT * FROM games WHERE title = "'.$gamecompany.'" ORDER BY game_id ASC LIMIT 50';
$result = mysqli_query($connect, $query);
if(mysqli_num_rows($result) > 0)
{
$output .= '<div class="list">';
while($row = mysqli_fetch_array($result))
{
$output .= '<li class="game">'.$row["title"].'</li>';
}
$output .= '</div>';
echo $output;
}
else
{
echo 'Nothing Found';
}
?>
So i used $gamecompany to trim and get a game's company and use it as a keyword in query. But everytime it just show "Nothing Found". When i have some games with keyword "Rockstar" in my database But it won't display that and just pass the conditions statement and can't show nothing.
Tried another keywords (Directly in my code) but won't work!
And one note: My titles are in "Arabic" language and it should be UTF8. Is this my problem? or just a wrong coding?
Using LIKE you can find all occurences with 'Rockstar', but to be safe, convert it to lower case and remove any extra spaces that might occur. Also, lets protect ourselves from SQL attacks with a prepared statement.
$gamecompany = strtolower(trim(strstr($_smarty_tpl->tpl_vars['game']->value['title'], '-', true))); // put it in lower case, trim any excess white space
$query = 'SELECT * FROM games WHERE LOWER(title) LIKE ? ORDER BY game_id ASC LIMIT 50';
$stmt = $conn->prepare($query);
$value = "%$gamecompany%"; // The % allows us to find any titles that have our search string in them
$stmt->bind_param("s", $value);
$stmt->execute();
$result = $stmt->get_result();
For you requirement
title = "'.$gamecompany.'"
is not going to work. You'll need to either use likewise search or full-text search
Likewise
title like '$gamecompany'
Full-Text - For full-text to work, you'll need to have full-text index for that column
MATCH (title) AGAINST (:gamecompany IN NATURAL LANGUAGE MODE)
You can create Full-text index like this
ALTER TABLE games ADD FULLTEXT(title)
Try using the LIKE keyword inside the query , and for the Arabic part make sure both the web app and the database uses the same encoding , i once had this problem and when both of them followed the same encode it worked out.
Related
I have a legacy PHP script which creates a list of resources from information stored in a MySQL database. Users can search the list or filter by the first letter in the title (this is stored as a column in the database). You can see it in action here: http://lib.skidmore.edu/library/index.php/researchdatabases). The script works fine except for one resource, FT.com, which appears incorrectly when users filter by letter. Regardless of the letter selected, its entry will be either at the top or the bottom. Note that in the unfiltered view FT.com is in proper alphabetical order. My first thought was to look at the database entry, but everything looks fine.
My hypothesis is a variable is not being set correctly. The way the script works is the top half of it contains a web form. The PHP below then picks up the input and assigns it to the variable $searchletter.
A combination of while loops and mysqli queries then retrieves and displays the results. Interestingly when the $searchletter = !empty line is commented out, the entire list disappears for the unfiltered view except for the FT.com entry (see this test script for an example: http://lib.skidmore.edu/library/search_dbs2.php). Otherwise I can see anything in neither the script nor the database which might be causing the observed behavior. Is my suspicion correct?
Here is the code. I've included everything except the connection information so you can see how it all works.
$search=(isset($_GET['search']) ? $_GET['search'] : null);
$search = !empty($_GET['search']) ? $_GET['search'] : 'default';
$search= addslashes($search);
$searchletter=(isset($_GET['searchletter']) ? $_GET['searchletter'] : null);
$searchletter = !empty($_GET['searchletter']) ? $_GET['searchletter'] : 'default';
var_dump ($_GET['searchletter']);
$con=mysqli_connect(DB_HOST,WEBMISC_USER,WEBMISC_PASS,DB_NAME);
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
if ($search == "default" && $searchletter == "default"){
$result = mysqli_query($con,"SELECT title,summary,url,coverage,format FROM dbs");
//This while loop creates the inital A to Z list.
while($row = mysqli_fetch_array($result))
{
$url=$row['url'];
$title=$row['title'];
$summary=$row['summary'];
$coverage=$row['coverage'];
$format=$row['format'];
echo <<<HTML
<p><h6>$title</h6>
<br />$summary</p>
HTML;
}
}
else {
$result = mysqli_query($con,"SELECT title,summary,url,coverage,format,fletter FROM dbs where title like '%$search%' or summary like '%$search%' or fletter = TRIM('$searchletter')");
//This block creates the filtered and searched version of the list.
while($row = mysqli_fetch_array($result))
{
$url=$row['url'];
$title=$row['title'];
$summary=$row['summary'];
$coverage=$row['coverage'];
$format=$row['format'];
echo <<<HTML
<p><h6>$title</h6>
<br />$summary</p>
HTML;
}
mysqli_close($con);
the first serious problem with this script is that it seems to be prone to MySQL Injection, the most serious problem of them all. (but I may be wrong). Please consider switching this code to PDO and its prepared statements and bindParam method.
the second is that, in the FORM you either support search OR letter (but not both)
BUT you use both in mysql query.
you should split the result fetching from
$result = mysqli_query($con,"SELECT
title,summary,url,coverage,format,fletter
FROM dbs
where title like
'%$search%' or summary like '%$search%' or fletter = '$searchletter'");
into if/else statement:
if(!empty($search)){
$result = mysqli_query($con,"SELECT
title,summary,url,coverage,format,fletter
FROM dbs
where title like
'%$search%' or summary like '%$search%'");
} elseif(!empty($searchletter)){
$result = mysqli_query($con,"SELECT
title,summary,url,coverage,format,fletter
FROM dbs
where fletter = '$searchletter'");
}
this will not fire BOTH cases on the search and should return more reliable result based on your selection.
EDIT: after you added more code, it's clear that every "unset by user" field has value of "default". which means:
whatever letter you chose, the "seachphrase" will be set to "default" and "default" appears to be a part of FT.com summary field (you can see this word in search results). Again: splitting the query into two cases will solve this, so "default" word is never used in the search query.
I'm currently working on a live search that displays results directly from a mysql db.
The code works, but not really as i want it.
Let's start with an example so that it is easier to understand:
My database has 5 columns:
id, link, description, try, keywords
The script that runs the ajax request on key up is the following:
$("#searchid").keyup(function () {
var searchid = encodeURIComponent($.trim($(this).val()));
var dataString = 'search=' + searchid;
if (searchid != '') {
$.ajax({
type: "POST",
url: "results.php",
data: dataString,
cache: false,
success: function (html) {
$("#result").html(html).show();
}
});
}
return false;
});
});
on the results.php file looks like this:
if ($db->connect_errno > 0) {
die('Unable to connect to database [' . $db->connect_error . ']');
}
if ($_REQUEST) {
$q = $_REQUEST['search'];
$sql_res = "select link, description, resources, keyword from _db where description like '%$q%' or keyword like '%$q%'";
$result = mysqli_query($db, $sql_res) or die(mysqli_error($db));
if (mysqli_num_rows($result) == 0) {
$display = '<div id="explainMessage" class="explainMessage">Sorry, no results found</div>';
echo $display;
} else {
while ($row = $result->fetch_assoc()) {
$link = $row['link'];
$description = $row['description'];
$keyword = $row['keyword'];
$b_description = '<strong>' . $q . '</strong>';
$b_keyword = '<strong>' . $q . '</strong>';
$final_description = str_ireplace($q, $b_description, $description);
$final_keyword = str_ireplace($q, $b_keyword, $keyword);
$display = '<div class="results" id="dbResults">
<div>
<div class="center"><span class="">Description :</span><span class="displayResult">' . $final_description . '</span></div>
<div class="right"><span class="">Keyword :</span><span class="displayResult">' . $final_keyword . '</span></div>
</div>
<hr>
</div>
</div>';
echo $display;
}
}
}
now, let's say that i have this row in my DB:
id = 1
link = google.com
description = it's google
totry = 0
keywords: google, test, search
if i type in the search bar:
google, test
i have the right result, but if i type:
test, google
i have no results, as obviously the order is wrong.
So basically, what o'd like to achieve is something a bit more like "tags", so that i can search for the right keywords without having to use the right order.
Can i do it with my current code (if yes, how?) or i need to change something?
thanks in advance for any suggestion.
PS: I know this is not the best way to read from a DB as it has some security issues, i'm going to change it later as this is an old script that i wrote ages ago, i'm more interested in have this to work properly, and i'm going to change method after.
Normalize your schema
The rules of relational database are very simple (at least the first three).
keywords: google, test, search
...breaks the second rule. Each keyword should be in its own row in a related table. Then you can simply write your query as....
SELECT link, description, resources, keyword
FROM _db
INNER JOIN keywords
ON _db.id=keywords.db_id
WHERE keyword.value IN (" . atomize($q) . ")
(where atomize explodes the query string, applies mysqli_escape_paramter() to each entry to avoid breaking your code, encloses each term in single quotes and concatenates the result).
Alternatively you could use MySQL's full text indexing which does this for you transparently.
Although hurricane makes some good points in his/her answer, they do not mention that none of the solutions proposed there does not scale to handle large volumes of data with any efficiency (decomposing the field into a new table/using full text indexing does).
Untested code but modify according to your needs,
$q = $_REQUEST['search'];
$q_comma = explode(",", $q);
$where_in_set = '';
$count = count($q_comma);
foreach( $q_comma as $q)
{
$counter++;
if($counter == $count) {
$where_in_set .= "FIND_IN_SET('$q','keywords')";
}else {
$where_in_set .= "FIND_IN_SET('$q','keywords') OR ";
}
}
$sql_res = "select link, description, resources, keyword from _db where $where_in_set or description like '%$q%'";
There are 2 solutions I can think of:
Use fulltext index and search.
You can split the search string into words in php for example using explode() and serach for the words not in a single serach criteria, but in separate ones. This latter one can be very resource intensive, since you are seraching in multiple fields.
LIKE '%google, test%' will match id=1 but not '%google,test%' (no space between coma) nor '%google test%' (space delimiter) nor '%test, google%'. Put each keyword as separate table or you can split input keywords into several single keyword and use OR operator such as LIKE 'google%' OR LIKE 'test%'
Not an ideal solution, but instead of treating your search datastring as one element, you can have php treat it as an array of keywords separated by a comma (by using explode). You'd then build a query depending on how many keywords were sent.
For example, using "google, test" your query would be:
$sql_res = "select link, description, resources, keyword from _db where (description like '%$q1%' or keyword like '%$q1%') AND (description like '%$q2%' or keyword like '%$q2%')";
Where $q1 and $q2 are "google" and "test".
First of all as you say it is not a good way to do it. I think you are writing a autocompleter.
Seperators for words
"google, test" or "test, google" is a attached words. First you need to define a seperator for users. Usually it is a whitespace ' '.
When you define it you need to split words.
$words = explode(" ",$q);
// now you get two words "google," and "test"
Then you need to create a sql which gives you multiple search chance.
There are a lot example in MySQL LIKE IN()?
Now you get your result.
Text similarity
Select all result from db and in a while search a text from another text. It gives you a dobule point for similarity. Best result is your result.
Php Similarity Example
Important Info
If you ask my opinion don't use it like that bcs it is very expensive. Use autocompleters on html side. Here is an example
I'm trying to improve my MySQL query.
SELECT gamename
FROM giveaway
WHERE gamename LIKE '$query'
I got an input that consists of URL's that are formed like:
http://www.steamgifts.com/giveaway/l7Jlj/plain-sight
http://www.steamgifts.com/giveaway/okjzc/tex-murphy-martian-memorandum
http://www.steamgifts.com/giveaway/RqIqD/flyn
http://www.steamgifts.com/giveaway/FzJBC/penguins-arena-sednas-world
I take the game name from the URL and use this as input for a SQL query.
$query = "plain sight"
$query = "tex murphy martian memorandum"
$query = "flyn"
$query = "penguins arena sednas world"
Now in the database the matching name sometimes has more characters like : ' !, etc.
Example:
"Plain Sight"
"Tex Murphy: Martian Memorandum"
"Fly'N"
"Penguins Arena: Sedna's World!"
So when putting in the acquired name from the URL this doesn't produce results for the 2nd, 3rd and 4th example.
So what I did was use a % character.
$query = "plain%sight"
$query = "tex%murphy%martian%memorandum"
$query = "flyn"
$query = "penguins%arena%sednas%world"
This now gives result on the 1st and 2nd example.
.
On to my question:
My question is, how to better improve this so that also the 3rd and 4th ones work?
I'm thinking about adding extra % before and after each character:
$query = "%f%l%y%n%"
$query = "%p%e%n%g%u%i%n%s%a%r%e%n%a%s%e%d%n%a%s%w%o%r%l%d%"
But I'm not sure how that would go performance wise and if this is the best solution for it.
Is adding % a good solution?
Any other tips on how to make a good working query?
Progress:
After a bit of testing I found that adding lots of wildcards (%) is not a good idea. You will get returned unexpected results from the database, simply because you just added a lot of ways things could match.
Using the slug method seems to be the only option.
If i get your question well, you are creating a way of searching through those informations. And if that is the case then try
$query = addslashes($query);
SELECT name
FROM giveaway
WHERE gamename LIKE '%$query%'
Now if you want to enlarge your search and search for every single word that looks like the words in your string, then you can explode the text and search for each word by doing
<?php
$query = addslashes($query);
//We explode the query into a table
$tableau=explode(' ',$query);
$compter_tableau=count($tableau);
//We prepare the query
$req_search = "SELECT name FROM giveaway WHERE ";
//we add the percentage sign and the combine each query
for ($i = 0; $i < $compter_tableau; $i++)
{
$notremotchercher=$tableau["$i"];
if($i==$compter_tableau) { $liaison="AND"; } else { $liaison=""; }
if($i!=0) { $debutliaison="AND"; } else { $debutliaison=""; }
$req_search .= "$debutliaison gamename LIKE '%$notremotchercher%' $liaison ";
}
//Now you lauch your query here
$selection=mysqli_query($link, "$req_search") or die(mysqli_error($link));
?>
By so doing you would have added the % to every word in your query which will give you more result that you can choose from.
so I have a blog system, and i want to build a section for "related news", I am not making a 'Tags' system but just simply searching and storing the current title of the article (which is pulled from the database) in a string and exploding it to be able to later put all the words into a query, that later query will search all titles in the database to find any of those words, and if it does, it will return the title in a list. Here is the relevant code:
// note to stackoverflow peeps, $row_object_title is just the title that is pulled form the database
$row_object_title_lower = strtolower($row_object_title);
$keywords = explode(" ",$row_object_title_lower);
Code that is run later on the page:
$keywords_imploded = implode("','",$keywords);
$myquery = sql_query("SELECT object_title FROM table WHERE object_title IN ('$keywords_imploded')
Now i try to list the titles by printing the title out, but nothing is display.
I am sure there is matching titles in the database.
Thanks
Your array of keywords is generated with:
$keywords = explode(" ",$row_object_title_lower);
What if you have a title like "My Super Blog Post"? You're going to get:
$keywords = array( "My", "Super", "Blog", "Post" );
Later on, you query using those values imploded together:
$keywords_imploded = implode("','",$keywords);
$myquery = sql_query("SELECT object_title FROM table WHERE object_title IN ('$keywords_imploded')
The SELECT query is going to look like this:
SELECT object_title FROM table WHERE object_title IN ( 'My', 'Super', 'Blog', 'Post' );
I don't think that's going to find anything.
You need to re-evaluate how you're handling the list of titles (I think that's what you're going for, right?).
It seems as though you have misunderstood how the IN clause works.
The IN clause will look for what is on the left in the list of values on the right. For example: WHERE id IN (2,3,5) - if id is in that list it will return true. In your case it is the opposite.
Something like this should work for what your after but there are likely to be better alternatives.
$sql = '';
foreach ($keywords AS $keyword)
{
if ($sql != '')
$sql .= ' OR ';
$sql .= "object_title LIKE '%$keyword%'";
}
$query = 'SELECT object_title FROM table WHERE '.$sql;
% is a wildcard.
Just please remember to escape the values first.
Try:
$keywords_imploded = join("','", $keywords);
$myquery = sql_query("SELECT object_title FROM table WHERE object_title IN ($keywords_imploded)
I have made the following search script but can only search one table column when querying the database:
$query = "select * from explore where site_name like '%".$searchterm."%'";
I would like to know how I can search the entire table(explore). Also, I would need to fix this line of code:
echo "$num_found. ".($row['site_name'])." <br />";
One last thing that is bugging me is when I push the submit button on a different page I always displays the message "Please enter a search term." even when I enter in something?
Thanks for any help, here is the entire script if needed:
<?php
// Set variables from form.
$searchterm = $_POST['searchterm'];
trim ($searchterm);
// Check if search term was entered.
if (!$serachterm)
{
echo "Please enter a search term.";
}
// Add slashes to search term.
if (!get_magic_quotes_gpc())
{
$searchterm = addcslashes($searchterm);
}
// Connects to database.
# $dbconn = new mysqli('localhost', 'root', 'root', 'ajax_demo');
if (mysqli_connect_errno())
{
echo "Could not connect to database. Please try again later.";
exit;
}
// Query the database.
$query = "select * from explore where site_name like '%".$searchterm."%'";
$result = $dbconn->query($query);
// Number of rows found.
$num_results = $result->num_rows;
echo "Found: ".$num_results."</p>";
// Loops through results.
for ($i=0; $i <$num_results; $i++)
{
$num_found = $i + 1;
$row = $result->fetch_assoc();
echo "$num_found. ".($row['site_name'])." <br />";
}
// Escape database.
$result->free();
$dbconn->close();
?>
Contrary to other answers, I think you want to use "OR" in your query, not "AND":
$query = "select * from explore where site_name like '%".$searchterm."%' or other_column like '%".$searchterm."%'";
Replace other_column with the name of a second column. You can keep repeating the part I added for each of your columns.
Note: this is assuming that your variable $searchterm has already been escaped for the database, for example with $mysqli->real_escape_string($searchterm);. Always ensure that is the case, or better yet use parameterised queries.
Similarly when outputting your variables like $row['site_name'] always make sure you escape them for HTML, for example using htmlspecialchars($row['site_name']).
One last thing that is bugging me is when I push the submit button on a different page I always displays the message "Please enter a search term." even when I enter in something?
Make sure that both forms use the same method (post in your example). The <form> tag should have the attribute method="post".
Also, what is wrong with the line of code you mentioned? Is there an error? It should work as far as I can tell.
A UNION query will provide results in a more optimized fashion than simply using OR. Please note that utilizing LIKE in such a manner will not allow you to utilize any indexes you may have on your table. You can use the following to provide a more optimized query at the expense of losing a few possible results:
$query = "SELECT * FROM explore WHERE site_name LIKE '".$searchterm."%'
UNION
SELECT * FROM explore WHERE other_field LIKE '".$searchterm."%'
UNION
SELECT * FROM explore WHERE third_field LIKE '".$searchterm."%'";
This query is probably as fast as you're going to get without using FULLTEXT searching. The downside, however, is that you can only match strings beginning with the searchterm.
To search other columns of table you need to add conditions to your sql
$query = "select * from explore where site_name like '%".$searchterm."%' or other_column like '%".$searchterm."%'";
But if you don't know that I would strongly advise going through some sql tutorial...
Also I didn't see anything wrong with this line
echo "$num_found. ".($row['site_name'])." <br />";
What error message are you getting?
Just add 'AND column = "condition"' to the WHERE clause of your query.
Be careful with adding lots of LIKE % conditions as these can be very slow especially if using a front wild card. This causes the RDBMS to search every row. You can optimize if you use an index on the column and only a trailing wildcard.
You are searching the whole table, just limiting the results to those where the site_name like '%".$searchterm."%'. If you want to search everything from that table, you need to remove the WHERE clause
Here's the corrected line. You had a few too many quotes in it.
echo $num_found.".".($row['site_name'])." <br />";
Regarding displaying the message, you have a typo in your code:
// Check if search term was entered.
if (!$serachterm)
should be:
// Check if search term was entered.
if (!$searchterm)
In the code you have written, !$serachterm always evaluates to true because you never declared a variable $seracherm (note the typo).
your code is very bugy for sql injection first do
do this
$searchterm = htmlspecialchars($searchterm);
trim($searchterm);
next
$query = mysql_real_escape_string($query);
finaly your search looks like this
$query = "select * from explore where site_name like '%$searchterm%';