Do a function while echo-ing if a certain string matches - php

I have a variable that contains a subpage's content. I want to do some MySQL query, if the variable's content contains this pattern {gallery:somerandomIDnumber}, but I don't want to lose the other stuff, and it's also important that the query should run where the pattern belongs.
For example, this is my variable's content:
<h1>Title of the page</h1>
{gallery:10}
<p>Other informations...</p>
I tried with preg_match, but unfortunately I can't figure it out, how I can save the other content.
foreach($matches[0] as $value) {
$int = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
$query = 'SELECT * ';
$query .= 'FROM gallery_images ';
$query .= 'WHERE gid = '.$int;
$gallery = ms_query($query);
while($gimage = mysqli_fetch_assoc($gallery)) {
echo '<img src="admin/uploaded/'.$gimage['imagepath'].'" width ="100">';
}
}
Summarazing, in this situation, I want to echo the title of the page, then some pictures from my database, and after that some other text.

Related

How to search multiple words?

I was trying to make a search engine so far I had done with it and the feature I lack in it was, when user queries a long sentence or two or three words with on not in line-wise in my DB is not going to show and it shows 0 results.
So far it is working fine with the one-word search it is doing a great job with that.
public function getResultsHtml($page, $pageSize, $term){
$fromLimit = ($page - 1) * $pageSize;
//page 1: (1-1) * 20
//page 2:(2-1) * 20
$query = $this->con->prepare("SELECT *
FROM sites WHERE title LIKE :term
OR url LIKE :term
OR keywords LIKE :term
OR description LIKE :term
ORDER BY clicks DESC
LIMIT :fromLimit,:pageSize");
$searchTerm = "%" . $term ."%";
$query->bindParam(":term",$searchTerm);
$query->bindParam(":fromLimit",$fromLimit,PDO::PARAM_INT);
$query->bindParam(":pageSize",$pageSize,PDO::PARAM_INT);
$query->execute();
$resultsHtml ="<div class='siteResults'>";
while($row = $query->fetch(PDO::FETCH_ASSOC)){
$id = $row["id"];
$url = $row["url"];
$title = $row["title"];
$description = $row["description"];
$title = $this->trimField($title,55);
$description = $this->trimField($description,230);
$resultsHtml .="<div class='resultContainer'>
<h3 class='title'>
<a class='result' href='$url' data-linkId='$id'>
$title
</a>
</h3>
<span class='url'>$url</span>
<span class='description'>$description</span>
</div>";
}
$resultsHtml .= "</div>";
return $resultsHtml;
}
So when user searches apple it is retrieving the data and we can see the search result in "apple store search" in the first image.But in second image when we search "apple search" it should be able to show us the "apple store search".
first image
Second image
First you need to breakdown your $term into separated words. With European languages, you can simply use explode:
<?php
$terms = explode(' ', $term);
// lets say your $term is 'apple orange lemon banana'
// $terms would be ['apple', 'orange', 'lemon', 'banana']
Prepare Terms List for Binding
Then, I'd build a key-value array for the terms binding:
<?php
// build a term list with term key (:term{number}) and associated term
// for binding
$term_params = [];
foreach ($terms as $key => $term) {
$term_params[":term{$key}"] = "%{$term}%";
}
// $term_params would be:
// [
// ':term0' => '%apple%',
// ':term1' => '%orange%',
// ':term2' => '%lemon%',
// ':term3' => '%banana%',
// ]
Prepare the Where Clause in SQL for Binding
Now, supposed the logics is all the terms need to show up in either of the target fields:
<?php
// a list of fields to search from
$fields = ['title', 'url', 'keywords', 'description'];
// build a where clause SQL based on the terms and fields
$or_where_clauses = [];
foreach (array_keys($term_params) as $term_key) {
$or_where_clauses[] = implode(' OR ', array_map(function ($field) use ($term_key) {
return "{$field} LIKE {$term_key}";
}, $fields));
}
$where_clauses_sql = implode(' AND ', array_map(function ($term_clause) {
// quote each term clause with bracket for proper OR logic to work
return '(' . $term_clause . ')';
}, $or_where_clauses);
The resulting where clauses sql would be:
(title like :term0 OR url like :term0 OR keywords like :term0 OR description like :term0)
AND
(title like :term1 OR url like :term1 OR keywords like :term1 OR description like :term1)
AND
...
...
(title like :termN OR url like :termN OR keywords like :termN OR description like :termN)
Putting Everything Together
I can then build the SQL string and bind the terms to the query accordingly:
<?php
// prepare the select statement
$query = $this->con->prepare("SELECT *
FROM sites WHERE {$where_clauses_sql}
ORDER BY clicks DESC
LIMIT :fromLimit,:pageSize");
// bind the terms
foreach ($term_params as $key => $value) {
$query->bindParam($key, $value);
}
$query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT);
$query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT);
$query->execute();
// ...
Shortcomings and Potential Solution
Please note that this approach does not understand the number of occurrence of terms in the field. And the way to separate words is far from perfect. For example:
It didn't handle punctuation marks.
It couldn't search for plural terms with the singular, or visa verse.
It could be matching part of a word by mistake (e.g. search "sing" and match "dressing").
It couldn't deal with CJK or languages without space).
You can use software like Elastic Search for better feature. With plugin and proper config, you can give even sort results with relevance, or give different field a different importance in the search process etc.
But that is an entirely different software than SQL server to use. You'd need to learn and plan how to index your contents there as well as just saving your data to SQL.
we dont know how u get search words from user, but as i guess u get them as an array. so you can try below code:
<?php
...
$textSearch = "";
for($i = 0 ; $i< count($userInputs);$i++){
if($i !== count($userInputs) -1){
$userInputData = $userInputs[$i];
$textSearch .= "'%$userInputData%' OR";
}else{
$textSearch .= "'%$userInputData%'";
}
}
and put $textSearch into your query.
remember , $userInputs is an array that u had before.
UPDATE
as your images shown, you can add $userInput = explode(" ",$textFromUser) in very begin of given code.

Removing empty spaces and BR from string in echo

I got the below code but at the moment it generates a string of results but with about 40+ empty spaces.
$user_ = JFactory::getUser();
$db = JFactory::getDBO();
$levels = JAccess::getAuthorisedViewLevels($user->id);
foreach($levels as $key => $level)
{
$query = 'SELECT title FROM #__pf_projects';
$query .= ' WHERE access = ' . $level . " AND TRIM(title) != ''";
$db->setQuery($query);
$projectlist = $db->loadResult($query).'<br>';
echo $projectlist;
}
At first I thought that array_filter() would be good here but as PatrickQ points out it is a string so the array filter won't work.
Then I adapted the code according to the answer from Don't Panic. This adapted code is what you can see above.
It returns now a list like this.
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
http://www.domain1.com
<br>
<br>
<br>
<br>
http://www.domain5.com
http://www.domain23.com
http://www.domain65.com
http://www.domain213.com
<br>
<br>
<br>
<br>
<br>
<br>
So how to adapt the code to just get a list like this:
http://www.domain1.com
http://www.domain5.com
http://www.domain23.com
http://www.domain65.com
http://www.domain213.com
When you change the <br> into a , then the list becomes ,,,,,,,,,,,http,,,,,,httphttphttphttp,,,,,,, <= I wrote it down a bit shorter.
First thing, array_filter, if no callback was passed, will remove only falsy elements. String with empty spaces is evaluated to true and therefore will not be remove from array. You can do something like:
$filteredArray = array_filter($projectList, function($val) {
return trim($val);
});
print_r($filteredArray);
Also, you can't echo an array. You can use print_r or var_dump.
If array_filter isn't filtering out empty values, then they probably aren't really empty. Assuming there is some sort of whitespace there rather than nulls or empty strings, you can probably modify your query to trim the title and only return results where there's still something there.
SELECT title FROM #__pf_projects
WHERE access = ? AND title IS NOT NULL AND TRIM (title) != ''
Or, in terms of your original PHP code:
$query = 'SELECT title FROM #__pf_projects';
$query .= ' WHERE access = ' . $level . " AND TITLE IS NOT NULL AND TRIM(title) != ''";
It is best to avoid concatenating variables into your SQL like this, though. If the framework you're using has some way to utilize prepared statements, you should go that route instead.
If this still doesn't work, I don't really know what else to try with the query, but you should be able to just check for an empty result in PHP and only echo if there's something to show.
$projectlist = $db->loadResult($query);
if (trim($projectlist)) echo $projectlist.'<br>';
Thanks to everyone's answers I finally figured out a way to get the results. My way is not necessarily the right way for everyone. And someone with more knowledge then me in this area would probably do it different.
Essentially my answer is outputting the entire string with each result inside a div. This will create a lot of empty divs but those are not generated by HTML. They do however show up in the Inspector.
The final code that I use in my script.
$user_ = JFactory::getUser();
$db = JFactory::getDBO();
$levels = JAccess::getAuthorisedViewLevels($user->id);
foreach($levels as $key => $level)
{
$query = 'SELECT title FROM #__pf_projects';
$query .= ' WHERE access = ' . $level;
$db->setQuery($query);
$projectlist = '<div class="project">'.$db->loadResult($query).'</div>';
echo $projectlist;
}
This is now giving me a list like this:
http://www.domain1.com
http://www.domain5.com
http://www.domain23.com
http://www.domain65.com
http://www.domain213.com

Get Popular words in PHP+MySQL

How do I go about getting the most popular words from multiple content tables in PHP/MySQL.
For example, I have a table forum_post with forum post; this contains a subject and content.
Besides these I have multiple other tables with different fields which could also contain content to be analysed.
I would probably myself go fetch all the content, strip (possible) html explode the string on spaces. remove quotes and comma's etc. and just count the words which are not common by saving an array whilst running through all the words.
My main question is if someone knows of a method which might be easier or faster.
I couldn't seem to find any helpful answers about this it might be the wrong search patterns.
Somebody's already done it.
The magic you're looking for is a php function called str_word_count().
In my example code below, if you get a lot of extraneous words from this you'll need to write custom stripping to remove them. Additionally you'll want to strip all of the html tags from the words and other characters as well.
I use something similar to this for keyword generation (obviously that code is proprietary). In short we're taking provided text, we're checking the word frequency and if the words come up in order we're sorting them in an array based on priority. So the most frequent words will be first in the output. We're not counting words that only occur once.
<?php
$text = "your text.";
//Setup the array for storing word counts
$freqData = array();
foreach( str_word_count( $text, 1 ) as $words ){
// For each word found in the frequency table, increment its value by one
array_key_exists( $words, $freqData ) ? $freqData[ $words ]++ : $freqData[ $words ] = 1;
}
$list = '';
arsort($freqData);
foreach ($freqData as $word=>$count){
if ($count > 2){
$list .= "$word ";
}
}
if (empty($list)){
$list = "Not enough duplicate words for popularity contest.";
}
echo $list;
?>
I see you've accepted an answer, but I want to give you an alternative that might be more flexible in a sense: (Decide for yourself :-)) I've not tested the code, but I think you get the picture. $dbh is a PDO connection object. It's then up to you what you want to do with the resulting $words array.
<?php
$words = array();
$tableName = 'party'; //The name of the table
countWordsFromTable($words, $tableName)
$tableName = 'party2'; //The name of the table
countWordsFromTable($words, $tableName)
//Example output array:
/*
$words['word'][0] = 'happy'; //Happy from table party
$words['wordcount'][0] = 5;
$words['word'][1] = 'bulldog'; //Bulldog from table party2
$words['wordcount'][1] = 15;
$words['word'][2] = 'pokerface'; //Pokerface from table party2
$words['wordcount'][2] = 2;
*/
$maxValues = array_keys($words, max($words)); //Get all keys with indexes of max values of $words-array
$popularIndex = $maxValues[0]; //Get only one value...
$mostPopularWord = $words[$popularIndex];
function countWordsFromTable(&$words, $tableName) {
//Get all fields from specific table
$q = $dbh->prepare("DESCRIBE :tableName");
$q->execute(array(':tableName' = > $tableName));
$tableFields = $q->fetchAll(PDO::FETCH_COLUMN);
//Go through all fields and store count of words and their content in array $words
foreach($tableFields as $dbCol) {
$wordCountQuery = "SELECT :dbCol as word, LENGTH(:dbCol) - LENGTH(REPLACE(:dbCol, ' ', ''))+1 AS wordcount FROM :tableName"; //Get count and the content of words from every column in db
$q = $dbh->prepare($wordCountQuery);
$q->execute(array(':dbCol' = > $dbCol));
$wrds = $q->fetchAll(PDO::FETCH_ASSOC);
//Add result to array $words
foreach($wrds as $w) {
$words['word'][] = $w['word'];
$words['wordcount'][] = $w['wordcount'];
}
}
}
?>

Advance Searching, PHP & MySQL

I'm trying to create an Advanced Searching form that sort of look like this ;
http://img805.imageshack.us/img805/7162/30989114.jpg
but what should I write for the query?
I know how to do it if there is only two text box but three, there's too many probability that user will do.
$query = "SELECT * FROM server WHERE ???";
What should I write for the "???"
I know how to use AND OR in the query but lets say if the user only fill two of the textbox and one empty. If I write something like this ;
$query = "SELECT * FROM server WHERE model='".$model."' and brand='".$brand."' and SN='".$SN.'" ";
The result will return as empty set. I want the user can choose whether to fill one,two or three of the criteria. If I use OR, the result will not be accurate because if Model have two data with the same name (For example :M4000) but different brand (For example : IBM and SUN). If I use OR and the user wants to search M4000 and SUN, it will display both of the M4000. That's why it is not accurate.
If the user can decide how many criteria he wants to enter for your search and you want to combine those criteria (only those actually filled by the user), then you must dynamically create your SQL query to include only those fields in the search that are filled by the user. I'll give you an example.
The code for a simple search form could look like this:
$search_fields = Array(
// field name => label
'model' => 'Model',
'serialNum' => 'Serial Number',
'brand' => 'Brand Name'
);
echo "<form method=\"POST\">";
foreach ($search_fields as $field => $label) {
echo "$label: <input name=\"search[$field]\"><br>";
}
echo "<input type=\"submit\">";
echo "</form>";
And the code for an actual search like this:
if (isset($_POST['search']) && is_array($_POST['search'])) {
// escape against SQL injection
$search = array_filter($_POST['search'], 'mysql_real_escape_string');
// build SQL
$search_parts = array();
foreach ($search as $field => $value) {
if ($value) {
$search_parts[] = "$field LIKE '%$value%'";
}
}
$sql = "SELECT * FROM table WHERE " . implode(' AND ', $search_parts);
// do query here
}
else {
echo "please enter some search criteria!";
}
In the above code we dynamically build the SQL string to do a search ("AND") for only the criteria entered.
Try this code
<?php
$model="";
$brand="";
$serialNum="";
$model=mysql_real_escape_string($_POST['model']);
$brand=mysql_real_escape_string($_POST['brand']);
$serialNum=mysql_real_escape_string($_POST['serialNum']);
$query=" select * from server";
$where_str=" where ";
if($model == "" && $brand == "" && $serialNum == "")
{
rtrim($where_str, " whrere ");
}
else
{
if($model != "")
{
$where_str.= " model like '%$model%' AND ";
}
if($brand != "")
{
$where_str.= " brand like '%$brand%' AND ";
}
if($serialNum != "")
{
$where_str.= " serialNum like '%$serialNum%' AND ";
}
rtrim($where_str, " AND ");
}
$query.= $where_str;
$records=mysql_query($query);
?>
For those framiliar with mysql, it offers the ability to search by regular expressions (posix style). I needed an advanced way of searching in php, and my backend was mysql, so this was the logical choice. Problem is, how do I build a whole mysql query based on the input? Here's the type of queries I wanted to be able to process:
exact word matches
sub-string matches (I was doing this with like "%WORD%")
exclude via sub-string match
exclude via exact word match
A simple regexp query looks like:
select * from TABLE where ROW regexp '[[:<:]]bla[[:>:]]' and ROW
regexp 'foo';
This will look for an exact match of the string "bla", meaning not as a sub-string, and then match the sub-string "foo" somewhere.
So first off, items 1 and 4 are exact word matches and I want to be able to do this by surrounding the word with quotes. Let's set our necessary variables and then do a match on quotes:
$newq = $query; # $query is the raw query string
$qlevel = 0;
$curquery = "select * from TABLE where "; # the beginning of the query
$doneg = 0;
preg_match_all("/\"([^\"]*)\"/i", $query, $m);
$c = count($m[0]);
for ($i = 0; $i < $c; $i++) {
$temp = $m[1][$i]; # $temp is whats inside the quotes
Then I want to be able to exclude words, and the user should be able to do this by starting the word with a dash (-), and for exact word matches this has to be inside the quotes. The second match is to get rid of the - in front of the query.
if (ereg("^-", $temp)) {
$pc = preg_match("/-([^-]*)/i", $m[1][$i], $dm);
if ($pc) {
$temp = $dm[1];
}
$doneg++;
}
Now we will set $temp to the posix compliant exact match, then build this part of the mysql query.
$temp = "[[:<:]]".$temp."[[:>:]]";
if ($qlevel) $curquery .= "and "; # are we nested?
$curquery .= "ROW "; # the mysql row we are searching in
if ($doneg) $curquery .= "not "; # if dash in front, do not
$curquery .= "regexp ".quote_smart($temp)." ";
$qlevel++;
$doneg = 0;
$newq = ereg_replace($m[0][$i], "", $newq);
}
The variable $newq has the rest of the search string, minus everything in quotes, so whatever remains are sub-string search items falling under 2 and 3. Now we can go through what is left and basically do the same thing as above.
$s = preg_split("/\s+/", $newq, -1, PREG_SPLIT_NO_EMPTY); #whitespaces
for ($i = 0; $i < count($s); $i++) {
if (ereg("^-", $s[$i])) { # exclude
sscanf($s[$i], "-%s", $temp); # this is poor
$s[$i] = $temp;
$doneg++;
}
if ($qlevel) $curquery .= "and ";
$curquery .= "ROW "; # the mysql row we are searching in
if ($doneg) $curquery .= "not ";
$curquery .= "regexp ".quote_smart($s[$i])." ";
$qlevel++;
$doneg = 0;
}
# use $curquery here in database
The variable $curquery now contains our built mysql query. You will notice the use of quote_smart in here, this is a mysql best practice from php.net. It's the only mention of security anywhere in this code. You will need to run your own checking against the input to make sure there are no bad characters, mine only allows alpha-numerics and a few others. DO NOT use this code as is without first fixing that.
You have to provide $model, $brand, $serial which come from your search-form.
$query = "SELECT * FROM `TABLE` WHERE `model` LIKE '%$model%' AND `brand` LIKE '%$brand%' AND `serial` LIKE '%$serial%'";
Also take a look at the mysql doc
http://dev.mysql.com/doc/refman/5.1/en/string-comparison-functions.html
A basic search would work like this:
"SELECT * FROM server WHERE column_name1 LIKE '%keyword1%' AND column_name2 LIKE '%keyword2%' .....";
This would be case for matching all parameters.For matching any one of the criteria, change ANDs to ORs

array_unique only on one field?

I have following code for an autocomplete box, I'm adding an image for choice clarification but want to make sure the title returned is unique, but, when I get to the code which makes the array unique, i've added other code which makes it non unique in other areas. Is there a way around this?
$query = "SELECT $title, imageURL FROM PRprodINFO2 WHERE ((prodcatID = '$cat_id')
AND ($title LIKE \"%" . $_GET["q"] . "%\")) group by $title LIMIT 8";
}
$result = mysql_query($query);
// set the array
$output_items = array();
while($row = mysql_fetch_array($result)) {
// clean after first non letter/number
$row[$title] = preg_replace('/[^\w\s].*$/', "", $row[$title]);
//trim spaces
$row[$title] = trim($row[$title]);
// add image src
$output_items[] = '<img src='.$row[imageURL].' style=max-width:50px;>'
.$row[$title];
} // while
// here i need just $row[title] to be unique,
// it is made non unique after regex strips off some characters
$output = array_unique($output_items);
print(implode("\n", $output));
mysql_close();
I am possibly confused by what you are asking. It seems like you would already have unique titles since you are grouping by title in your sql. But, maybe you have extra non alpha-numeric characters you are stripping out with your regex that makes some unique titles the same.
In that case, instead of building up your $output_items like you are, try:
$output_items[$row['title']] = $row['imageURL'];
This will ensure that each title is unique. You will have the imageURL of the last row that matched that title. If you want, instead, the first title that matched, then just check isset before overwriting it like:
if (!isset($output_items[$row['title']])) $output_items[$row['title']] = $row['imageURL'];
Then, outside of the loop, build up your output string.
$output = '';
foreach ($output_items as $title => $image) {
$output .= '<img src='.$image.' ...>'.$title."\n";
}
echo $output;

Categories