Creating dynamic tables with MySQLi securely - php

I want to be able to create dynamic tables, for custom user surveys... like survey monkey... how would I go about create something like that?
Because I want to give the ability to the user to create the survey, with different amount of text fields, and different a option fields... I would need to create a custom table for each survey.
Would something like this be possible?
<?php
$table_name = 'survey_'.$_POST['surveyid'];
$query = 'CREATE TABLE ? (
`responseid` INT NOT NULL AUTO_INCREMENT,
`textarea1` TEXT NULL,
`textarea2` TEXT NULL,
`textarea3` VARCHAR(255) NULL,
`drop_down1` VARCHAR(255) NULL,
`drop_down2` VARCHAR(255) NULL,
`bool1` BIT NULL,
`bool2` BIT NULL,
PRIMARY KEY (`responseid`))';
if($stmt = $mysqli->prepare($query)){
$stmt->bind_param('s', $table_name);
$stmt->execute();
$stmt->close();
}else die("Failed to prepare");
?>
The above example comes back with "Failed to prepare", because I don't think I can prepare a table name... is there another work around using mysqli?
if(ctype_digit($_POST['surveyid']) && $_POST['surveyid']>0){
$table_name = 'survey_'.$_POST['surveyid'];
$query = 'CREATE TABLE '.$table_name.' (
`responseid` INT NOT NULL AUTO_INCREMENT,
`textarea1` TEXT NULL,
`textarea2` TEXT NULL,
`textarea3` VARCHAR(255) NULL,
`drop_down1` VARCHAR(255) NULL,
`drop_down2` VARCHAR(255) NULL,
`bool1` BIT NULL,
`bool2` BIT NULL,
PRIMARY KEY (`responseid`))';
I know I can just try to sanitize the $_POST['surveyid'] (like I did above) but I prefer to prepare it if possible.

$table_name = 'survey_'.$_POST['surveyid'];
Do not do the above. It is easy for a hacker to exploit your site if you include $_GET or $_POST data directly in any SQL string.
But you can't use parameters for a table name. A parameter takes the place of a single scalar value only. You can prepare CREATE TABLE but you can't use parameters for identifiers (e.g. table names).
The best practice is to make sure your table name conforms to a rule, for example only the leading portion of a string of numeric digits, up to the maximum length of a MySQL table name:
$table_name = 'survey_' . strspn(trim($_POST['surveyid']), '0123456789', 0, 56);
If you have other rules for a surveyid, then you could use preg_replace():
$table_name = 'survey_' . preg_replace('^(\w+)', '$1', trim($_POST['surveyid']));

It is not possible to prepare a data definition language statement like "CREATE TABLE". I can't find the reference in the MySQL docs that explains this, but I did find a good explanation on the PHP documentation site.

Related

I have a variable in php called as id. I want to use it as a table name.This code is not creating the table in php myadmin

I have to use the $id variable as table name but it is not working.
$sql1="CREATE TABLE $id(
amt_to_be_paid INT(6),
no_of_days_req INT(2),
proposal TEXT NOT NULL,
channel_link VARCHAR(100) NOT NULL,
)";
Be sure that you have the full control on the $id variable and is not coming from user input.
You need to concatenate your $id variable to the query string, as following:
$sql1="CREATE TABLE " . $id . "(
amt_to_be_paid INT(6),
no_of_days_req INT(2),
proposal TEXT NOT NULL,
channel_link VARCHAR(100) NOT NULL,
)";
Take a look at this answer for a detailed review of how to achieve this : How to include a PHP variable inside a MySQL statement

How to properly use Wildcard with CONCAT

(Spoiler: The Title has nothing to do with what is wrong with the code.)
I'm creating a live-search system just to show the user possible event types already listed on my website. During my speculations I may have an error with Wildcard binding which I'm unable to see.
I tried using different types of "WHERE LIKE" statements, and most of them didn't work at all. Such as I tried using placeholder query (question mark) and that did not work at all. If I ran this query manually on my database I will get results which I'm expecting.
This is how my code looks, the variable $q is obtained using $_GET method.
$query = $pdo->prepare('SELECT DISTINCT EventCategory FROM Events
WHERE EventCategory LIKE CONCAT(\'%\',:q,\'%\')');
$query->bindParam(":q", $q);
$query->execute();
$row = $query->fetch(PDO::FETCH_ASSOC);
while ($row = $query->fetchObject()) {
echo "<div> $row->EventCategory </div>";
}
The expected results would be: If the $q is equal to n, Meeting and Nightlife is returned. When $q is equal to ni, then Nightlife is only returned.
The search is NOT CASE SENSITIVE, N and n is treated equally.
The SHOW CREATE TABLE Events query returned the following:
CREATE TABLE `Events` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(100) NOT NULL,
`Image` varchar(600) NOT NULL,
`Date` date NOT NULL,
`Description` varchar(1200) NOT NULL,
`SpacesAvailable` int(11) NOT NULL,
`EventCategory` varchar(50) NOT NULL,
`Trending` varchar(30) DEFAULT NULL,
`TrendingID` int(255) NOT NULL,
`Sale` int(255) NOT NULL,
PRIMARY KEY (`ID`)
)DEFAULT CHARSET=latin1
Images to show the operation of the website: https://imgur.com/a/yP0hTm3
Please if you are viewing the images the view from bottom to top. Thanks
I suspect the default collation in your EventCategory column is case-sensitive. That's why Ni and ni don't match in Nightlife.
Try this query instead.
'SELECT DISTINCT EventCategory FROM Events WHERE EventCategory COLLATE utf8_general_ci LIKE CONCAT(\'%\',:q,\'%\')'
Or, if your column's character set is not unicode but rather iso8859-1, try this:
'SELECT DISTINCT EventCategory FROM Events WHERE EventCategory COLLATE latin1_general_ci LIKE CONCAT(\'%\',:q,\'%\')'
This explains how to look up the available character sets and collations on MySQL.
How to change collation of database, table, column? explains how to alter the default collation of a table or a column. It's generally a good idea because collations are baked into indexes.
The problem is not in LIKE, but in PHP and PDO. Stare at the 3 conflicting uses of $row in your code:
$row = $query->fetch(PDO::FETCH_ASSOC);
while ($row = $query->fetchObject()) {
echo "<div> $row->EventCategory </div>"; }
Then review the documentation and examples. (Sorry, I'm not going to feed you the answer; you need to study to understand it.)
In complement to the comprehensive answer by O.Jones, another, simpler solution would be to just perform a case-insensitive search, like :
'SELECT DISTINCT EventCategory
FROM Events
WHERE UPPER(EventCategory) LIKE CONCAT(\'%\',UPPER(:q),\'%\')'

I'm trying to create a table on my database using PHP so far i have managed to create a database using PHP but but fail to create table/tables

<?PHP
try{
$handler = new PDO($servername, $username, $password);
$handler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "CREATE TABLE user_details (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(30) NOT NULL,
fullname VARCHAR(30) NOT NULL,
username VARCHAR(30) NOT NULL,
password VARCHAR(30) NOT NULL,
gender ENUM('male','female','other','') NOT NULL , PRIMARY KEY (id))
ENGINE = InnoDB
)";
echo "Sucessfullll";
$handler->exc($sql);
} catch (PDOException $msg){
echo $msg->getMessage();
die();
}
?>
I'm not sure what value you have stored in $servername variable. If you are storing just the IP address or hostname, then it's wrong.
The first argument to the PDO constructor is a Data Source Name (dsn). An example of a MySQL DSN is:
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$handler = new PDO($dsn, $username, $password);
See http://php.net/manual/en/pdo.construct.php
You have an extra unmatched parentheses )
$sql = "CREATE TABLE user_details (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(30) NOT NULL,
fullname VARCHAR(30) NOT NULL,
username VARCHAR(30) NOT NULL,
password VARCHAR(30) NOT NULL,
gender ENUM('male','female','other','') NOT NULL ,
PRIMARY KEY (id)
) ENGINE = InnoDB )";
Should be
$sql = "CREATE TABLE user_details (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(30) NOT NULL,
fullname VARCHAR(30) NOT NULL,
username VARCHAR(30) NOT NULL,
password VARCHAR(30) NOT NULL,
gender ENUM('male','female','other','') NOT NULL ,
PRIMARY KEY (id)
) ENGINE = InnoDB";
You also might need the length for the INT, I forget.
INDEXES
I always do my id's like this id INT(10) UNSINGED NOT NULL AUTO_INCREMENT because well no one like having a negative ID. And I would make UNIQUE(username) And UNIQUE(email) and maybe an INDEX(fullname).
Indexes depend on what you plan on searching, the more indexes you have the long it takes to insert and update records. But for your average website it's pretty unnoticeable.
If you plan on inserting 10's of thousands of rows at a time, you would want to be more careful with indexes, because for each index it takes a bit extra time to compile them when modifying the data.
NAMING CONVENTIONS
Your field names look good, no plurals no casing. The only thing I would change here is name the table just users, I usually pluralize tables, and use _ as a way to mark a junction table, or a table that ties 2 tables together with a Many To Many relationship. So for example I would have users and addresses and then a table that joined them users_addresses.
If it's a table with a One to many, I will do a non-plural for the one and a plural for the many. So if each user could have many uploads. One to Many, I would name it like user_uploads. That way I can see what kind of relationship it is without opening up PHPmyAdmin or trying to remember how I set it up.
I just wanted to mention that, because the name you have going by my naming conventions, it would be a table with details in it with a user_id foreign key.
I should say everyone does naming conventions a bit differently the main thing is to name things for a reason, and be consistent.
last thing
This is just my Opinion, but I find it better to connect this way:
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
];
$handler = new PDO($servername, $username, $password, $options);
By sending it as options, it will throw an exception when you fail to connect. Otherwise you haven't set the mode until after the initial connection. And probably about 90% of the time I use associative arrays for my return, so you can set that too.
cheers!

Insert value if recieved value is null

so, i'm trying to show a certain value in a table row, if the value recieved by post is null.
So far i've got this as my small database (it's in spanish):
DROP DATABASE IF EXISTS fantasmas;
CREATE DATABASE fantasmas;
USE fantasmas;
CREATE TABLE tipos(
ID tinyint(2) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
TIPO VARCHAR(45) UNIQUE
);
CREATE TABLE datos(
ID tinyint(2) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
NOMBRE VARCHAR(50) NOT NULL,
ARCHIVO VARCHAR(30),
AVISTAMIENTO VARCHAR(50) NOT NULL,
LOCALIDAD VARCHAR(50) NOT NULL,
INFO VARCHAR(200),
FKTIPOS TINYINT(2) UNSIGNED,
FOREIGN KEY (FKTIPOS) REFERENCES tipos(ID)
);
INSERT INTO tipos (TIPO) VALUES('Vapor'), ('Forma Animal'), ('Forma Humanoide'), ('Dios/Semidios'),('No Catalogado');
Those inserted values into "tipos" are then showed in options like this:
<select name="tipos">
<option value="sintipo">---------</option>
<?php
while( $row = mysqli_fetch_assoc($rta)):
$tipo = $row['TIPO'];
echo '<option value="'.$row['ID'].'">'.$tipo.'</option>';
endwhile;
?>
</select>
After, when the form is sent, the inset to the table, looks like this, it assigns null value if someone select the "------" option:
$query = "INSERT INTO datos SET NOMBRE='$nombre', AVISTAMIENTO='$lugar', LOCALIDAD='$localidad', INFO='$info', ARCHIVO='$ruta', FKTIPOS = NULLIF('$tipo','sintipo')";
All the data filled in the form, is then showed in a different row of a table.
What I need now, is that, if someone selects the "------" option, the value shown in screen is "No catalogado"
So far I have not been able to do it.
Can anyone help me?
Before the insert happens you can do a null check on the PHP side. Or... you can just use SQL's built in ISNULL()
You can also use CASE statements in SQL to accomplish this behavior across multiple values.
I know this isn't the most lengthy answer, but it should work.
Is this what your asking for?
$query = "INSERT INTO datos SET NOMBRE='$nombre', AVISTAMIENTO='$lugar', LOCALIDAD='$localidad', INFO='$info', ARCHIVO='$ruta', FKTIPOS = " . ($tipo == 'sintipo' ? 'NULL' : "'$tipo'");
Be aware that you have very dirty code, you don't even filter and escape income values, as I guess. Try to google php mysql escape values.

Foreign Key Failure in MySQL

I have created a database composed of three tables. This is my query in creating my tables with Foreign Key.
CREATE TABLE reporter
(
reporterid INT NOT NULL AUTO_INCREMENT,
firstname VARCHAR(1000) NOT NULL,
lastname VARCHAR(100) NOT NULL,
PRIMARY KEY (reporterid)
);
CREATE TABLE flood
(
floodid INT NOT NULL AUTO_INCREMENT,
address VARCHAR(500) NOT NULL,
description VARCHAR(1000) NOT NULL,
dateofflood DATE NOT NULL,
timeofflood INT NOT NULL,
PRIMARY KEY (floodid)
);
CREATE TABLE reports
(
reportid INT NOT NULL AUTO_INCREMENT,
timereport NODATATYPE NOT NULL,
datereport DATE NOT NULL,
rid INT NOT NULL,
fid INT NOT NULL,
PRIMARY KEY (reportid),
FOREIGN KEY (rid) REFERENCES reporter(reporterid),
FOREIGN KEY (fid) REFERENCES flood(floodid)
);
I created a system in order for me to add records/row on my database through PHP. This is my code:
<?php
mysql_connect("localhost", "root", "") or die("Connection Failed");
mysql_select_db("flooddatabase")or die("Connection Failed");
$description = $_POST['description'];
$address = $_POST['address']; // Make sure to clean the
$dateofflood=$_POST['dateofflood'];
$timeofflood=$_POST['timeofflood'];
$firstname=$_POST['firstname'];
$lastname=$_POST['lastname'];
$dateofreport=$_POST['dateofreport'];
$timeofreport=$_POST['timeofreport'];
$query = "INSERT into flood(address,description,dateofflood,timeofflood) values ('$address','$description','$dateofflood','$timeofflood')";
$query2 = "INSERT into reporter(firstname,lastname) values ('$firstname','$lastname')";
$query3 = "INSERT into reports(dateofreport,timeofreport) values ('$dateofreport','$timeofreport')";
if(mysql_query($query))
if(mysql_query($query2))
if(mysql_query($query3))
{
echo "";
} else
{
echo "fail";
}
?>
The result that I am getting is fine. It's just that, in my REPORTS table, there is no foreign key that is being generated. For example I input something on my reporter table and flood table, the foreign key 'rid' and 'fid' has no values that references to both tables. Need help thank you.
Get the just inserted Primary key value from flood table insert
query. And store it to a variable say $f_id;
Get the just inserted primary key value from reporter table insert
query and store it to a variable say $r_id;
Now Make your last insert statement like below:
"INSERT into reports(dateofreport,timeofreport,rid,fid) values ('$dateofreport','$timeofreport',$r_id,$f_id)";
I am not giving you a direct copy paste solution.
If you need to know how to get the last inserted id by executing an insert query then look at this link
there is no foreign key that is being generated
I'm not entirely sure what you even mean by that. Foreign keys aren't "generated". Primary keys can be, which you do:
reporterid INT NOT NULL AUTO_INCREMENT
(as well as for your other two tables)
the foreign key 'rid' and 'fid' has no values
Well, look at your query:
INSERT into reports(dateofreport,timeofreport) values ...
Where do you insert values for rid and fid? I'm actually pretty surprised this query works at all, since those columns don't allow NULL values:
rid INT NOT NULL,
fid INT NOT NULL,
(Though your column names also don't line up, so I find it likely that the code you're showing isn't actually the code you're using...) That point aside however, the fact still remains that if you want a value in those fields then you have to put a value in those fields:
INSERT into reports(dateofreport,timeofreport,rid,fid) values ...
After each query, you can get the last generated identifier from mysql_insert_id():
$last_id = mysql_insert_id();
Use that to then populate the values being inserted as foreign keys in subsequent queries.
Also worth noting, the mysql_* libraries are long since deprecated and have been replaced with mysqli_ and other libraries such as PDO. I highly recommend you upgrade to a current technology, since what you're using isn't supported by any vendor.
Additionally, and this is very important, your code is wide open to SQL injection attacks. This basically means that you execute any code your users send you. You should treat user input as values, not as executable code. This is a good place to start reading on the subject, as is this.

Categories