Having issues with incrementing a field in the database - php

I've established a database full of movies which is connected to a HTML form of which the information is ported with PHP POST (Acts as a movie database).
When the user enters a search term (Either title, genre, year or classification) the PHP script search.php is handed the information.
This goes fine, however, i also have a column in my DB which is titled times_searched which has a default value of zero.
I feel like one of the big problems is i've just been staring at this file for too long and something simple is not apparent to me right now.
I'm very new to PHP and WebDev in general.
I have tried issuing a second statement to execute inside the foreach loop of the TableRow class and have tried different syntaxes and attempting to bind previous statements to new statements.
class TableRows extends RecursiveIteratorIterator
{
function __construct($it)
{
parent::__construct($it, self::LEAVES_ONLY);
}
function current()
{
return "<td style='width:150px;border:1px solid black;'>" .
parent::current()."</td>";
}
function beginChildren()
{
echo "<tr>";
}
function endChildren()
{
echo "</tr>" . "\n";
}
}
function printAssocArray($stmt)
{
foreach (new TableRows(new RecursiveArrayIterator($stmt->fetchAll()))
as $k=>$v) {
echo $v;
}
}
require 'Templates/header.php';
//DB variables
$serverName = "localhost";
$userName = "root";
$password = "";
//Post variables
$title = ($_POST['title']);
$year = ($_POST['year']);
$genre = ($_POST['genre']);
$rating = ($_POST['rating']);
if (isset($_POST['submit'])) {
try {
//Create PDO object using DB variables
$conn = new PDO(
"mysql:host=$serverName;dbname=project_db", $userName, $password
);
//Set PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "<h3 class='center'>Connection Successful!</h3>";
if (!empty($_POST['title']) && empty($_POST['genre'])
&& empty($_POST['rating']) && empty($_POST['year'])
) {
$stmt = $conn->prepare(
"UPDATE movies SET
times_searched = times_searched + 1 WHERE id = :id;
IN (SELECT * FROM movies WHERE title LIKE '%$title%'"
);
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->bindValue(':id', $result['id']);
$stmt->execute();
//Use function call to print information to table using TableRows
printAssocArray($stmt);
}elseif (!empty($_POST['genre']) && empty($_POST['rating'])
&& empty($_POST['year']) && empty($_POST['title'])
) {
$stmt = $conn->prepare(
"SELECT * FROM movies WHERE genre LIKE '%$genre%'"
);
$stmt->execute();
//Collect Result in associative array
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
//Use function call to print information to table using TableRows
printAssocArray($stmt);
}
//New Block
if (!empty($_POST['title']) && empty($_POST['genre'])
&& empty($_POST['rating']) && empty($_POST['year'])
) {
$updateQuery = "UPDATE movies SET times_searched = times_searched + 1
WHERE id IN (SELECT id FROM movies WHERE title LIKE :title)";
$stmt = $conn->prepare($updateQuery);
$stmt->bindValue(':title', "%{$title}%");
$stmt->execute();
$outputQuery = "SELECT * FROM movies WHERE title LIKE :title";
$stmt2 = $conn->prepare($outputQuery);
$stmt2->bindValue(':title', "%{$title}%");
$data = $stmt2->fetchAll(PDO::FETCH_ASSOC);
foreach ($data as $row) {
foreach ($row as $v) {
echo $v, " ";
}
echo "<br>",\n;
}
The aim is to try and return the requested query from the database then increment the times_searched column before printing it using the TableRow Class, this should happen each time the query is run (if the page is refreshed the times_searched column should increment)
This is being used for completion of a homework project, and security is not nessacarily a factor (though use of PDO was requested).

Your query should either use WHERE id = :id (single movie item) or WHERE id IN (SELECT id FROM movies WHERE title LIKE '%$title%') (multiple movie items), not both, not separated by a ;.
I assume you want to use the latter (update multiple movie items), change
$stmt = $conn->prepare(
"UPDATE movies SET
times_searched = times_searched + 1 WHERE id = :id;
IN (SELECT * FROM movies WHERE title LIKE '%$title%'"
);
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->bindValue(':id', $result['id']);
$stmt->execute();
to
$stmt = $conn->prepare("UPDATE movies SET times_searched = times_searched + 1 WHERE id IN (SELECT id FROM movies WHERE title LIKE :title)");
$stmt->bindValue(':title',"%{$title}%");
$stmt->execute();
For the // New Block part:
Replace
$outputQuery = "SELECT * FROM movies WHERE title LIKE '%title%'";
$stmt2 = $conn->prepare($outputQuery);
$stmt2->bindValue('title', $title);
with
$outputQuery = "SELECT * FROM movies WHERE title LIKE :title";
$stmt2 = $conn->prepare($outputQuery);
$stmt2->bindValue(':title', "%{$title}%");

Related

How to generate a student ID in PHP? And how to avoid goto?

I want to generate a unique student ID. The format that I want is the last two digits of the current year plus 5 digits after that. For example: 2000001, 2000002, 2000003.. etc.. This is the extract of my code right now.
$pre = substr(strval(date("Y")),2);
$num = 1;
include "dbh.inc.php"; // this file merely creates the $conn variable that connects to mysql database.
$sql_cmd = "SELECT id FROM students WHERE id=?;";
$stmt = $conn->stmt_init();
if ($stmt) {
$prepare = $stmt->prepare($sql_cmd);
if ($prepare) {
bind:
$studentid = $pre . str_repeat('0', (4 - strlen($num)) ) . strval($num);
$bind = $stmt->bind_param('s', $studentid);
if ($bind) {
$result = $stmt->execute();
if ($result) {
$num++;
goto bind;
}
else {
// insert student here using $studentid
}
}
}
}
But I need to improve this because:
It uses a goto (I want to avoid it)
It seems overkill to prepare, bind, execute, etc everytime and query the database every loop.
It is obviously slow.
Please let me know if there is a better way of doing this.
You can generate new id on the MySQL side:
include "dbh.inc.php"; // this file merely creates the $conn variable that connects to mysql database.
$sql =
"SELECT CONCAT("
. "DATE_FORMAT(CURDATE(), '%y'), "
. "LPAD(COALESCE(MAX(RIGHT(id, 5)) + 1, 1), 5, '0')"
. ") AS new_id "
. "FROM students";
$result = $conn->query($sql);
if ($result) {
if ($row = $result->fetch_assoc()) {
// insert student here using $row['new_id']
}
}
Or another option is to create an trigger on insert:
DELIMITER //
CREATE TRIGGER students_tr_bi BEFORE INSERT ON students
FOR EACH ROW
BEGIN
SET NEW.id = (
SELECT CONCAT(
DATE_FORMAT(CURDATE(), '%y'),
LPAD(COALESCE(MAX(RIGHT(id, 5)) + 1, 1), 5, '0')
) FROM students
);
END//
DELIMITER ;
-- Usage:
INSERT INTO students (name) VALUES ('John');

Get data from different table based on Id

I'm building a small e-commerce website and I want to display the reviews, and I want to create screen names for people (first letter of first name concatenated onto the last name), but I can't figure out how to get the information from my clients table. Let me show you the code I've done so far:
$invId = filter_input(INPUT_GET, 'invId', FILTER_SANITIZE_NUMBER_INT); // this is taken from a name value pair from the view
// Gets the raw data from the database
function getProRev($invId){
$db = acmeConnect();
$sql = "SELECT * FROM reviews WHERE invId = :invId ORDER BY reviewId DESC";
$stmt = $db->prepare($sql);
$stmt->bindValue(':invId', $invId, PDO::PARAM_STR);
$stmt->execute();
$tn = $stmt->fetchAll();
$stmt->closeCursor();
return $tn;
}
// Builds the simple review display
function buildReviewDisplay($reviews){
$rd = "<div id='reviewView'>";
foreach ($reviews as $review){
$rd .= "<h2>$review[clientId]</h2>";
$rd .= "<h3>$review[reviewDate]</h3>";
$rd .= "<p>$review[reviewText]</p>";
$rd .= "<hr>";
}
$rd .= "</div>";
return $rd;
}
As you can see, I'm displaying the clientId (a number) which is not what I want, and now this is where I'm stuck. I have the relationship between the two tables (clients and reviews) set up, but I can't figure out how to get the data. Here is the function I tried to write, but it didn't work:
// Trying to get the dang client info
function getUsername($clientId){
$db = acmeConnect();
$sql = "SELECT * FROM clients WHERE clientId = :clientId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':clientId', $clientId, PDO::PARAM_STR);
$stmt->execute();
$cd = $stmt->fetchAll();
$stmt->closeCursor();
$fletter = substr($cd['clientFirstname'], 0, 1);
$scrnam = $fletter . $cd['clientLastname'];
return $scrnam;
}
And I understand that this didn't work because there was nothing passing the $clientId parameter to the function, but the is contained in the $tn[] array, so there has to be a way that I can take the $clientId from the $tn[] array and query the database for the first and last name, but I can't figure out how.
You can join the clients table to the reviews table in your first query.
SELECT * FROM reviews
LEFT JOIN clients ON reviews.clientId = clients.clientId
WHERE invId = :invId ORDER BY reviewId DESC
Then you'll have access to the client name columns without needing to execute an additional query for each review you display.
That way you can use your code from getUsername in buildReviewDisplay.
foreach ($reviews as $review) {
$fletter = substr($review['clientFirstname'], 0, 1);
$scrnam = $fletter . $review['clientLastname'];
$rd .= "<h2>$scrnam</h2>";
$rd .= "<h3>$review[reviewDate]</h3>";
$rd .= "<p>$review[reviewText]</p>";
$rd .= "<hr>";
}

Why is my update statement updating my columns with the wrong data?

Why is my update statement updating my columns card_id with the wrong data?
My database structure.
$deckCardIds Holds a array of different two digit numbers.
What I expect the code to: Update each column based on the the current row I am looping through and updating card_id with the current index of $deckCardIds.
What is actually happening: The columns are looped through and data is set with $deckCardIds but then set to 0 when the loops moves to the next iteration.
<?php
$playerId=$_GET['playerid'];
$playerDeckCardIds=$_GET['deckcardids'];
$deckCardIds = explode(" ", $playerDeckCardIds);
array_pop($deckCardIds);
try
{
#Connect to the db
$conn = new PDO('mysql:host=localhost;dbname=tcg', 'root', '');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare('SELECT row_id FROM player_deck WHERE player_id = :player_id');
$stmt->execute(array('player_id' => $playerId));
$r = $stmt->fetchAll(PDO::FETCH_OBJ);
$i=0;
foreach($r as $row)
{
$stmt = $conn->prepare('UPDATE player_deck SET card_id = :card_id WHERE row_id = :row_id');
$stmt->bindParam(':card_id', $deckCardIds[$i]);
$stmt->bindParam(':row_id', $row->row_id);
$stmt->execute();
$i++;
}
}
catch(PDOException $e)
{
echo 'ERROR: ' . $e->getMessage();
}
?>

Searching multiple tables

I'm trying to create a search for my site and I'm having lots of trouble doing it. In my movies database I have two tables: movieinfo and movie_genres. Under movieinfo, there is Movie_Name, actor1, actor2, actor3, actor4. Under the movie_genres table, there is Genre_Id. (There are more columns in each of the tables, but I only want to search those columns.)
My current code is:
<?php
if ($_GET) {
$search = $_GET['search'];
$connect = mysql_connect("localhost","root","spencer");
if($connect) {
mysql_select_db("movies", $connect);
$query = "SELECT Movie_Name, actor1, actor1, actor1, actor1, Genre_Id FROM movieinfo, movie_genres WHERE * LIKE '%:search%' OR actor1 LIKE '%:search%' OR actor2 LIKE '%:search%' OR actor3 LIKE '%:search%' OR actor4 LIKE '%:search%' OR Genre_Id = ? ";
$results = mysql_query($query);
while($row = mysql_fetch_array($results)) {
echo $row['Poster'] . "<br/>" . $row['Movie_Name'];
}
} else {
die(mysql_error());
}
}
?>
Everything in movieinfo I'm searching for is VARCHAR, and movie_genres is an INT (incase that makes much of a difference). I think I'm close to what the code should be, but I don't know.
As of right now, I get the following error while trying to search:
Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in C:\xampp\htdocs\movies\get_search.php on line 12
New code:
<?php
// Connection data (server_address, database, name, poassword)
$hostdb = 'localhost';
$namedb = 'movies';
$userdb = 'root';
$passdb = 'spencer';
try {
// Connect and create the PDO object
$conn = new PDO("mysql:host=$hostdb; dbname=$namedb", $userdb, $passdb);
$conn->exec("SET CHARACTER SET utf8"); // Sets encoding UTF-8
}
catch (PDOException $e) {
$v_errormsg = $e->getMessage();
$error_str = <<<END_HTML
<h2> Error connecting to database: Message: $v_errormsg <h2>
END_HTML;
echo $error_str;
die;
}
$sql = <<<END_SQL
SELECT Movie_Name FROM movieinfo WHERE Movie_Name LIKE '%":search"%' ";
END_SQL;
try {
$sth = $conn->prepare($sql);
$sth->execute();
}
catch (PDOException $e) {
$v_errormsg = $e->getMessage();
$error_str = <<<END_HTML
<h2> Error selecting data: Message: $v_errormsg <h2>
END_HTML;
echo $error_str;
die;
}
$num_rows = 0;
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
$Movie_Name = $row['Movie_Name'];
}
echo $row['Movie_Name'];
?>
You can't use part of a string as a placeholder; placeholders can only take the place of a single entire value. Use LIKE :search and bind '%' . $search . '%' as the value.
You aren't binding any values. :)
You are getting an invalid query because you're using bind paramter syntax from PDO
start with something very simple like
$search= mysql_escape_string($search);
$query = "SELECT Movie_Name FROM movie_info WHERE name LIKE '%".$search."%';
Once you get that working, move on to the more complex logic...
Better yet, use PDO to do the query:
<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
example from: http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers

PHP - (PDO) Execute with binding variables

I just started playing around with PDO and I am trying to create a function that will display all the data for a given table name. After reading a few posts here I found a solution that I can get working (shown below with a hard-coded select statement). However, I can't get my execute statements to work when I bind my field names (I get an exception similar to: Undefined index: person_id). I should mention my class extends PDO:
/*********************************************************************
*Function showTable
*Purpose Display all information for a given table.
*Params $sTable -> Table name
********************************************************************/
public function showTable($sTable)
{
$result;
try
{
if(isset($sTable))
{
//create a result in a table format
$result = "<table>";
//$stmt = $this->prepare('DESCRIBE :sTable');
$stmt = $this->prepare('DESCRIBE ' . $sTable);
//$stmt->bindParam(':sTable', $sTable);
$stmt->execute();
//array version of the column names
$aCols = $stmt->fetchAll(PDO::FETCH_COLUMN);
//string version of the column names
$sCols = implode (", ", $aCols);
//$stmt = $this->prepare('SELECT :fields FROM :sTable');
//$stmt = $this->prepare('SELECT :fields FROM person');
$stmt = $this->prepare('SELECT person_id, first_name, last_name FROM person');
//$stmt->execute(array(':fields'=>$sCols, 'stable'=>$sTable));
//$stmt->execute(array(':fields'=>$sCols));
$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
var_dump($row);
$result = $result . "<tr>";
foreach($aCols as $col)
{
//var_dump($row);
$result = $result . " <td>" . $row[$col]. "</td>";
}
$result = $result . "</tr>";
}
$result = $result . "</table>";
}
return $result;
}
catch(PDOException $e)
{
if($this->bDebug)
{
echo $e->getMessage();
}
}
}
Like I said the hard coded select string works but when i comment out the hard coded and uncomment the execute with a bind it throws exceptions.
You cannot insert identifiers or keywords this way.
PDOStatement::execute() will put the value in escaped form inside single quotes. Your query would look like:
SELECT 'col1, col2' FROM person
What is invalid MySQL syntax.
A valid example:
$stmt = $this->prepare('SELECT col FROM person WHERE name = :name');
$stmt->execute(array(':name' => $name));
It works, because it's a value you insert here; and not an keyword or identifier.

Categories