I asked this question before, but it got no responses, so I deleted the last question, and simplified/clarified the question, and am reposting
I have a comics site... I'm working on a SO-style tagging system. A user can select 1 or many tags which will display all associated comics.
To remember the user selections, I'm storing them in a $_SESSION array.
The issue I'm having is that one of the strings, 'business', being stored in the $_SESSION['tagnames'] array after it is chosen by the user is not being found in the array...
The way it's supposed to work is a user selects a tag, and to deselect it, they click it again... so I check if the string is in the $_SESSION... if it is, unset it... here is a snippet:
//var_dump shows these are both set fine when a user clicks on the tag they want
$tagid = (isset($_GET['tagid']) ? ($_GET['tagid']) : null);
$tagname = (isset($_GET['tagname']) ? ($_GET['tagname']) : null);
...
//Tag IDS are added and removed without issue:
//if tag id exists in $_SESSION['tags'] array, remove it
if ($key = array_search($tagid, $_SESSION['tagids'])) {
unset($_SESSION['tagids'][$key]);
}
else {
$_SESSION['tagids'][] = $tagid;
}
...
//but one of the tag names, 'business', is not being removed... and is actually added again even when I press F5 to refresh
if ($key = array_search($tagname, $_SESSION['tagname'])) {
unset($_SESSION['tagname'][$key]);
}
else {
$_SESSION['tagname'][] = $tagname;
}
This is a var_dump of the sql statement: It correctly shows the query being changed based on which tag id is selected.
Here's a var_dump of the $_SESSION['tagname']... you can see that it recognizes when tags 2 and 3 (differences and similarities) have already been added (I used array_search() to check), but it doesn't find tagid 1, 'business', even though it's clearly been added several times.
Here's the function that returns the selected tag names to the user:
function getSelectedTags() {
global $tagid, $tagname;
if ($key = array_search($tagname, $_SESSION['tagname'])) {
unset($_SESSION['tagname'][$key]);
}
else {
$_SESSION['tagname'][] = $tagname;
}
var_dump($_SESSION['tagname']);
foreach ($_SESSION['tagname'] as $tagname) {
echo '<span class="tags">' . $tagname . '</span>';
}
}
Any thoughts why tagname 'business' is the only thing causing an issue?
Even after I destroy the session, pressing F5 to refresh will automatically put tagname 'business' into the $_SESSION['tagname'] array.
EDIT: More comprehensive code:
Homepage.php: user clicks on returned tags from getDBTags() to add a tag to the $_SESSION array on imageDisplay.php
<h5>Tags</h5>
<?php echo getDBTags(); ?>
<br/>
<p>Your tags:</p>
<?php echo getSelectedTags(); ?>
imageDisplay.php: responsible for handling how images are filtered and displayed...
getDBTags() returns tag choices from the database so users can click on them:
function getDBTags() {
include 'dbconnect.php';
global $cat;
$sql = "SELECT tagid, tagname FROM tags";
$query = $mysqli->query($sql);
while ($row = $query->fetch_assoc()) {
echo '<span class="tags">'.$row['tagname'].'</span>';
}
mysqli_close($mysqli);
}
getFilters() decides how images should be filtered by having a dynamic query, then sends the query to pagination(), which displays filtered images on pages.
function getFilters() {
include 'dbconnect.php';
global $cat, $site, $table, $tagid;
$order = " ORDER BY date DESC";
//if tag id exists in $_SESSION['tags'] array, remove it
if ($key = array_search($tagid, $_SESSION['tagids'])) {
unset($_SESSION['tagids'][$key]);
}
//if it doesn't, add it
else {
$_SESSION['tagids'][] = $tagid;
}
//var_dump($_SESSION['tagids']);
if ($cat != null) $catquery = " AND catidFK = $cat";
else $catquery = null;
$sql =
"SELECT c.*, t.*
FROM comics c
INNER JOIN comictags ct ON (c.id = ct.comicID)
INNER JOIN tags t ON (t.tagid = ct.tagID)
WHERE ct.tagID IN ('" . implode(', ', $_SESSION['tagids']). "')
". $catquery ." " . $order;
if (!$mysqli->query($sql)) printf("<br /><b>Error:</b> %s\n", $mysqli->error);
$query = $mysqli->query($sql);
var_dump($sql);
mysqli_close($mysqli);
return $query;
}
getSelectedTags() returns the selected tag title back to the user so they can see what they've chosen. If they click on a tag again (returned from getDBTags() above), it will remove the tag from $_SESSION['tagname']. This is the problem area:
function getSelectedTags() {
global $tagid, $tagname;
if ($key = array_search($tagname, $_SESSION['tagname'])) {
unset($_SESSION['tagname'][$key]);
}
else {
$_SESSION['tagname'][] = $tagname;
}
//var_dump($key = array_search($tagname, $_SESSION['tagname']));
var_dump($_SESSION['tagname']);
foreach ($_SESSION['tagname'] as $tagname) {
echo '<span class="tags">' . $tagname . '</span>';
}
}
There are three issues which stand out (well two stand out, one is very subtle).
1. Open only one DB connection and leave it:
First, instead of opening and closing the connection in $mysqli in your functions, do it once at the start of the script and leave it open. You'll need to modify your functions to accept $mysqli as a parameter (preferred) or access it via global $mysqli; (not preferred). There is no need to call mysqli_close(), as that is done implicitly.
In your getFilters() function, you are actually closing the MySQLi resource $mysqli and then attempting to return $query; where $query is a result resource. Doing that will render the result resource useless.
// Pass $mysqli as a parameter
function getFilters($mysqli) {
//...
// Do alll your stuff...
// And do not call mysqli_close()!
return $query
}
2. Use strict comparison and test for FALSE with array_search():
When using array_search(), if your result is the first element in the array at key [0], that will be regarded by a surrounding if () condition as a falsy return rather than the positive result it should be. For that reason, a tag at position [0] will get repeatedly added rather than removed. This will need to be fixed in a couple of places...
// First get the key, which is an int or FALSE
$key = array_search($tagid, $_SESSION['tagids']);
// Unset if it is FALSE by strict comparison
if ($key !== FALSE) {
unset($_SESSION['tagids'][$key]);
}
else {
$_SESSION['tagids'][] = $tagid;
}
3. Global variable $tagname is ruined by a foreach:
You have accessed the global $tagname in getSelectedTags(). But in that function you have a foreach loop which does :
foreach ($_SESSION['tagname'] as $tagname)
When using $tagname as the loop's variable there, it is actually overwriting the global $tagname on each iteration. You need to change that to a different value, or whenever you call getSelectedTags(), $tagname the global will become whatever the last tag in $_SESSION['tagname'] had been, without exception.
// use a different varname in the loop
foreach ($_SESSION['tagname'] as $tn) {
// Also calling htmlspecialchars to escape here. Do this in any variable to HTML output...
echo '<span class="tags">' . htmlspecialchars($tn) . '</span>';
}
Related
I am trying to create a search facility for a website. the website contains text posts so the database has a table post_record with field post_id, post_title, post_href and post_content.
now the working is when a user enters any search keyword in search field, that keyword is being tested against all the post titles and post contents and where it matches it should return all those rows. Following is the code for it. the issue is its returning only the last row of the databse.
<table class="s_table">
<?php
require_once'config.php';
if(isset($_POST['search_btn']))
{
$mes = " ";
$search_key =" ";
$search_key = ($_POST['search_field']);
try
{
if(empty($search_key) && $search_key != " ")
{
echo $mes = "Enter the keyword you want to search.";
}
else
{
$result = $conn->prepare("SELECT * FROM post_record WHERE post_title LIKE '%".$search_key."%' OR post_content LIKE '%".$search_key."%'");
$result->execute();
$count=$result->rowCount();
$datas = $result->fetchAll();
if(!$count)
{
echo $mes = "No Result found. Try another keyword for search.";
}
else
{
foreach ($datas as $data)
{
echo "
<tr><td><b><u><a href='".$data['post_href']."'>".$data['post_title']."</a></b></u></td></tr><br>
<tr><td class='s_cont'>".$data['post_content']."</tr></td><br><br>";
}
}
}
}
catch(PDOException $e)
{
$mes = "Something Went Wrong! try again";
header("location:index.php");
}
}
$conn = null;
?>
Everything is working fine except the thing that it is displaying only last row of the database.
Right now database has 3 posts and for testing purpose i tried a keyword that i know is common in all the post contents but its not working. only last row the database is being fetched and tested.
if(empty($search_key) && $search_key != " ") this condition is wrong
Both are different things which wont get true ever.
you are testing if its empty and then u r testing if its not empty
Both in same if condition
I usually use procedual functions but I encounter a problem like this with the mysqi_fetch_array(), so your problem might be that fetchAll() is moving the cursor to the last record. Try to remove that function or use mysql_data_seek($datas , 0) before the foreach() .
I have a HTML form that i am using to search a database, the form method is GET:
<form method="get" action="">
Then my SQL Query selects from a database using the $_GET values
i run the SQL, then have this function - (http://pastebin.com/J3RL3MxC) - that i run underneath the query
So in total, it looks like:
$sql="SELECT * FROM customer ORDER BY company";
$i=0;
$array = ShowRecords($sql, "customer");
foreach($array["results"] as $ret) {
}
and i echo my results in the foreach loop
this is working fine, however when navigating through the pages that the function creates i loose the $_GET values so i also loose what i have searched for
how can i keep the $_GET values but keep in mind that the $_GET["pagenum"] needs to be removed/changed so the page will change
I have already added this code:
$query_string = '?';
foreach($_GET as $q => $k) {
if($q == 'id' or $q == 'pagenum') {
//
} else {
if($q != '') {
$query_string = '&'.$q.'='.$k;
}
}
}
at the top of my function to try to do what is needed ($_GET["id"] also needs to be removed) but its not keeping the values
What is the best way to keep all $_GET values except the specified ones?
P.S: I know i should be using PDO which i will be as soon as i have this issue sorted, i will then change my code to use PDO
Use http_build_query to build your query string instead of doing it manually:
http://php.net/manual/en/function.http-build-query.php
I would just store all of the request vars in an array and updated it as needed. When ready to output any links just use http_build_query to generate the query string
$request = $_GET;
unset($request['id']);
$request['pagenum'] = 2;
//etc
$query_string = '?' . http_build_query($request);
$id = isset($_GET["id"]) ? $_GET["id"] : NULL;
$pagenum = isset($_GET["pagenum"]) ? $_GET["pagenum"] : NULL;
if($id) {
}
if($pagenum) {
}
I have this table: TABLE_ELEMENTS, with the name ELEMENTS, i have a multiple values inside, see image.
This is the php and return the results from autocomplete request.
$("#autocomplete").autocomplete({
source: "http://localhost/include/autocomplete.php?type=mauto_complete",
First i call this..
if(isset($_GET['type']) && in_array($_GET['type'], $arr_action)) $type=$_GET['type'];
if($type == "mauto_complete") {
require_once $config_abs_path."/autocomplete/autocomplete.php";
if(isset($_GET['term'])) {
$term = escape($_GET['term']);
$response = mauto_complete::getAutocomplete($term);
echo json_encode($response);
}
}
And this is the secondauto.php file
function getAutocomplete($term) {
global $db;
global $config_table_prefix;
global $crt_lang;
$elements=$db->fetchRow("select Distinct `elements` from TABLE_ELEMENTS where `elements` like '$term%' limit 10");
$elements_array = explode("|", $elements);
return $elements_array;
}
I have write this after select
$elements_array = explode("|", $elements);
Ok the request is working fine, but in autocomplete results when i type the word Building i take no words.
But when i type the first word of the elements ( Apartment ) i take all words.
The words is not uniqe
A common approach to this is to add a | to the left of the field, then search that. This ensures that an element containing the search doesn't get matched.
select
Distinct `elements`
from
TABLE_ELEMENTS
where
lower(CONCAT('|', `elements`)) LIKE lower('%|$term%')
However, you're probably looking for something else. Below is how I'd approach it. I couldn't figure out what library you were using for your connection, so you may have to change a little bit for it to work for you.
function getAutocomplete($name, $term)
{
// make sure you escape the string to avoid SQL injection
$name = mysqli_real_escape_string($db, $name);
// make the searches case-insensitive
$term = strtolower($term);
// fetch the valid elements for the field and split them using explode
$elements = $db->fetchRow("SELECT `elements` FROM `TABLE_ELEMENTS` WHERE `name` = '$name'");
$elements_array = explode('|', $elements);
// make an array to save the matching elements
$filtered = array();
// iterate over each element to check for a match
foreach($elements_array as $element)
{
// check to see if the beginning of the element starts with the search term
if(strpos(strtolower($element), $term) === 0)
{
// add it to the filtered array
$filtered[] = $element;
}
}
// return the matching results
return $filtered;
}
Then to use it, specify what field you want to autocomplete for:
print_r(getAutocomplete('Property Type', 'B'));
// Outputs: Array
// (
// [0] => Building
// [1] => Bungalow
// [2] => Business
// )
To make your existing code to use it, change your JavaScript to match the following. You'll need to change name depending on what field you're autocompleting.
$("#autocomplete").autocomplete({
source: "http://localhost/include/autocomplete.php?type=mauto_complete&name=Property%20Type"
});
Then update the file where you call the getAutocomplete function:
if(isset($_GET['type']) && in_array($_GET['type'], $arr_action)) $type=$_GET['type'];
if($type == "mauto_complete") {
require_once $config_abs_path."/autocomplete/autocomplete.php";
if(isset($_GET['term']) && isset($_GET['name'])) {
$name = $_GET['name'];
$term = $_GET['term'];
$response = mauto_complete::getAutocomplete($name, $term);
echo json_encode($response);
}
}
Try use like this to get all possible results
$elements=$db->fetchRow("select distinct `elements` from TABLE_ELEMENTS where lower(`elements`) like lower('%$term%') limit 10");
Thank you for reading this and for your help in advance.
I got a simple Book-Catalogue /procedure code/, every visitor can see the book catalogue and click through out the books and check their authors BUT I've got an idea to add a feature that allows the users to write a comment to each book ONLY if they're logged. So I added a session variable $_SESSION['isLogged']. But there is a lot of code blocks that duplicates. What I need is an advice, what to do with this duplicated blocks of code. What the good practice says? And my code below is from one of the 6 files that I got. In everyfile I got this repeating of code.
So here's my code:
if (!isset($_SESSION['isLogged'])) {
echo '<div class="user-navigation">
<a href="register.php" class="user-nav" >Register now</a>
Log in
</div>
<div class="navigation1">
Add book
Add author
</div>';
$booksAndAuthors = mysqli_query($connection, 'SELECT DISTINCT * FROM books LEFT JOIN books_authors ON books.book_id=books_authors.book_id LEFT JOIN authors ON authors.author_id=books_authors.author_id');
$result = array();
while ($resultArr = mysqli_fetch_assoc($booksAndAuthors)) {
$result[$resultArr['book_id']] ['book_name'] = $resultArr['book_title']; // Reodering array
$result[$resultArr['book_id']] ['author'][$resultArr['author_id']] = $resultArr['author_name']; // Reordering array
}
echo '<table class="table"><tr><th>Book name</th><th>Author</th></tr>'; // Open table html table tags
foreach ($result as $k=>$b) { // Foreach the result array to get the book_name
echo '<tr><td>' . $b['book_name'] . '</td><td>';
$data = array(); // Create an empty array in order to fill the data inside
foreach ($b['author'] as $k => $a) { // Foreach the nested array with the authors to get the author_name displayed
$_GET['author_name'] = $a;
$data[] = '' . $a . ''; // Link is chosen by the author_id
}
echo implode(', ', $data); // Add a comma after every record
echo '</td></tr>'; // Close table cell and row
}
exit;
echo '</table>'; // Close html table tag
}
else {
echo '<div class="user-navigation">
My Profile
Log out
</div>
<div class="navigation2">
Add book
Add author
</div>';
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$booksAndAuthors = mysqli_query($connection, 'SELECT DISTINCT * FROM books LEFT JOIN books_authors ON books.book_id=books_authors.book_id LEFT JOIN authors ON authors.author_id=books_authors.author_id');
$result = array();
while ($resultArr = mysqli_fetch_assoc($booksAndAuthors)) {
$result[$resultArr['book_id']] ['book_name'] = $resultArr['book_title']; // Reodering array
$result[$resultArr['book_id']] ['author'][$resultArr['author_id']] = $resultArr['author_name']; // Reordering array
}
echo '<table class="table"><tr><th>Book name</th><th>Author</th></tr>'; // Open table html table tags
foreach ($result as $k=>$b) { // Foreach the result array to get the book_name
echo '<tr><td>' . $b['book_name'] . '</td><td>';
$data = array(); // Create an empty array in order to fill the data inside
foreach ($b['author'] as $k => $a) { // Foreach the nested array with the authors to get the author_name displayed
$_GET['author_name'] = $a;
$data[] = '' . $a . ''; // Link is chosen by the author_id
}
echo implode(', ', $data); // Add a comma after every record
echo '</td></tr>'; // Close table cell and row
}
echo '</table>'; // Close html table tag
}
*If my question is not properly asked or you have some notices.Please let me know!
* (=
Put this code in a file then include that file at the bottom of all of the pages.
For example put the code in isLogged.php
Then on each page where the code is duplicated replace it with:
include "isLogged.php";
include is what you're looking for.
You should create a file that contains your repeated code. functions.php for example.
Then call it with one of these methods.
include "functions.php"
http://php.net/manual/en/function.include.php
include_once("functions.php")
http://php.net/manual/en/function.include-once.php
require_once("functions.php")
http://php.net/manual/en/function.require-once.php
create a function of repeated block and call when it required or create a file with the set of repeated code and use any of include,include_once,require_once to add the file.for more detail see php manual for these functions.
So I have a switch statement that I want to display one form or another based on if the id from one table has the matching foreign key in another table.
So far what I have tried is nesting one while statement into another which isn't working.
$subresult = mysqli_query($con,"SELECT * FROM tags GROUP BY tag");
$subresult2 = mysqli_query($con,"SELECT * FROM tag_subscribe WHERE uid = $uid");
while ($row = mysqli_fetch_array($subresult)) {
$tid = $row['tid'];
while ($row2 = mysqli_fetch_array($subresult2)) {
$tid2 = $row2['tid'];
}
if ($tid2 == $tid) {
$subbedup = 'yes';
} else {
$subbedup = 'no';
}
switch ($subbedup) {
case ('yes'):
echo "alternate html form goes here because $tid2 == $tid";
break;
case ('no'):
echo "html form goes here";
break;
}
}
So when this code is run, it only returns switch "no" except it will return one switch "yes" which just happens to be the last record of the second table that contains the foreign key. When I think about it, that makes sense as it will just keep running through this loop until it runs out of records in the table. So I spent about six minutes getting to this point and I have spent the last 6 hours trying to get it to work correctly without any luck.
So once again, fine people at SO, save me! Please and Thank you :)
So my question is: How would this be done correctly?
I'm not exactly sure of your database structure, so I'll improvise.
Given these sample tables and columns:
tags
id name
tag_subscriptions
user_id tag_id
The query below will loop through all tags. Each tag includes a subscribed column set to either "yes" or "no", depending on whether the current user is subscribed to that particular tag.
$sql="SELECT t.`id`, t.`name`, IF (ISNULL(ts.`tag_id`),'no','yes') AS `subscribed`
FROM `tags` t
LEFT JOIN `tag_subscriptions` ts ON (ts.`user_id`=$uid AND ts.`tag_id`=t.`id`)
WHERE 1;"
Then loop through all tags:
$q=mysql_query($sql) or die(mysql_error());
while ($row=mysql_fetch_assoc($q)) {
switch ($row['subscribed']) {
case 'yes'
// user is subscribed to this tag
break;
default:
// user is not subscribed to this tag
}
}
I think (hope) this is closer to what you're looking for.
http://sqlfiddle.com/#!2/58684/1/0
Sorry for using PDO as thats what i know, you can convert the idea to MYSQLi im sure.
$db = new PDO($hostname,$username,$password);
$arraySubTags = array();
$query = "SELECT tagID FROM tag_subscribe WHERE uid = :uid";
$statement = $db->prepare($query);
$statement->bindValue(':uid', $uid);
$statement->execute();
$subscribedTags = $statement->fetchAll(PDO::FETCH_ASSOC); //or loop with a while using fetch()
$statement->closeCursor();
foreach($subscribedTags as $sTag)
{
array_push($arraySubTags,$sTag);
}
$query = "SELECT * FROM tags GROUP BY tag";
$statement = $db->prepare($query);
$statement->execute();
$allTags = $statement->fetchAll(PDO::FETCH_ASSOC); //or loop with a while using fetch()
$statement->closeCursor();
foreach($allTags as $tag)
{
if(in_array($tag['tagID'], $arraySubTags))
{
echo "person is subscribed";
}else{ echo "person not subscribed";}
}
This code just checks whether the last tag checked is subscribed to - to check for each one you need to move the switch statement into the outer while loop, after the if..else bit that sets the $subbedup variable for the current tag.
Or you could make $subbedup an array, indexed by the tag id, if you need to keep the switch separate for some reason.