I'm just curious and was wondering how you guys handle it if you want to use the same html form and as far as possible the same php code to create and update an item.
Example:
On one page you can create a database entry with name, email address and age.
On a different(?) page you see the form fields filled with your data and you can edit and save it.
I have my ways to accomplish this using pretty much the same code - but I'm hoping to learn something here. So how would you handle this task?
Thanks & Cheers, sprain
Pretty easily - if an ID of an existing item (which the user is authorised to edit) is supplied in the query string, then it's an edit operation.
If no ID is supplied in the query string, it's a create operation.
The fields are pre-populated based on the existing values from the database if it's an edit operation, or based on default values or empty strings if it's a create operation.
The way I see it is that reusing identical markup for form between create/edit works for some cases, but not for all. I find that forms -- though they may map to the same database table -- are really defined by their context. For example, if you had a 'users' table, you might have a 'create' form with username, email, password, but after that user exists you want them to retain their identity on their site, so the username field would not appear in an 'edit' context. I'm classically a PHP developer, but I have come to appreciate the approach that Django takes, where you create a model (table) that defines the basic validation for each field and you can create as many forms as you that build off of, or modify/extend from that definition. If you're writing from scratch, you'll probably find it practical to make your validation methods very portable and/or find ways to make your form fields context-sensitive.
That's the way I always do it now. Are you using an MVC system at all? I use one controller with two different actions (urls = person/new + person/edit/xxxx_id).
the code is then something like:
function new()
errors = []
if (get)
data = blank_record()
elseif (post)
data = posted_data
if (create(data))
redirect_to_listing()
else
errors = describe_errors
show_form(data, errors)
function edit()
errors = []
if (get)
data = get_from_db(id)
elseif (post)
data = posted_data
if (save())
redirect_to_listing()
else
errors = describe_errors
show_form(data, errors)
Note that once it gets to the form there's always an object called data that the form can render, it may be blank, from the db, or posted data. Either way it should always be the same format.
The reason I split new and edit is that I find that often enough they are actually quite different in their behaviours and the load and save steps.
I guess this is not the right answer but it might be interesting for you anyway.
There is an orm project called doctrine:
http://www.doctrine-project.org/projects/orm/1.2/docs/en
// User Id might be an existing id, an wrong id, or even empty:
$user_id = 4;
$user_id = null;
// Fetch the user from the database if possible
$user = Doctrine::getTable('Model_User')->find($user_id);
// If there was no record create a new one
if ( $user === false )
$user = new Model_User();
// Change some data
$user->title = $newValue;
// Perform an update or an insert:
$user->save();
As you see you don't have to care about sql.
Doctrine does that for you and your code becomes easier to read and to debug.
Yes, that's the only acceptable solution.
Here is a little example of CRUD application which store the input form in a template:
<?
mysql_connect();
mysql_select_db("new");
$table = "test";
if($_SERVER['REQUEST_METHOD']=='POST') { //form handler part:
$name = mysql_real_escape_string($_POST['name']);
if ($id = intval($_POST['id'])) {
$query="UPDATE $table SET name='$name' WHERE id=$id";
} else {
$query="INSERT INTO $table SET name='$name'";
}
mysql_query($query) or trigger_error(mysql_error()." in ".$query);
header("Location: http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);
exit;
}
if (!isset($_GET['id'])) { //listing part:
$LIST=array();
$query="SELECT * FROM $table";
$res=mysql_query($query);
while($row=mysql_fetch_assoc($res)) $LIST[]=$row;
include 'list.php';
} else { // form displaying part:
if ($id=intval($_GET['id'])) {
$query="SELECT * FROM $table WHERE id=$id";
$res=mysql_query($query);
$row=mysql_fetch_assoc($res);
foreach ($row as $k => $v) $row[$k]=htmlspecialchars($v);
} else {
$row['name']='';
$row['id']=0;
}
include 'form.php';
}
?>
form.php
<form method="POST">
<input type="text" name="name" value="<?=$row['name']?>"><br>
<input type="hidden" name="id" value="<?=$row['id']?>">
<input type="submit"><br>
Return to the list
</form>
list.php
Add item
<? foreach ($LIST as $row): ?>
<li><?=$row['name']?>
<? endforeach ?>
Of course, some fancy form constructor, like HTML_QuickForm2 coud be used instead of plain HTML template - you know its constant programmer's hunger not to repeat himself, even in naming an HTML field, field value and error key :)
But personally I prefer plain HTML.
Related
I have a single page responsive HTML page. One section of the page has a product search. User can enter search criteria in a form and get back the results. The results are paged.
<form id="filterform" name="filterform" method="post" action="./loaddata.php">
...
</form>
The form is submitted by Ajax and the results are returned as an HTML fragment that gets dynamically inserted into the DOM to refresh the results.
That's all working OK, but sometimes the results from loaddata.php are very slow, usually the first time called from the page.
In loaddata.php I'm using a Sqlite3 database. It is read only. Something like the following:
$filename = "../datafile.sqlite3";
$db = new SQLite3($filename);
$q = "SELECT distinct productId, title, price, name FROM datatable LIMIT 16";
$results = $db->query($q);
while ($row = $results->fetchArray()) {
echo "<h1>Results</h1>";
}
$db->close();
Is there a way to make loaddata.php load and stay in memory to respond to the form submit? It seems like it will reload every submit.
Depending on the size of the datatable you can save it on SESSION, use functions like shmop_open/shmop_write/shmop_read or (better yet) use some cache service like redis, but in one way or another the data will be stored and processed every time you pass by that place. I would tere the page in pieces, create one webservice to deal with the form post and another to show the form.
The easiest (not necessary safest or best way to do) would be ...
PS: I assume you are working with PDO and (obviously) the code bellow is an elaboration, it will not actually work
if (isset($_SESSION['db_datatable'])) {
foreach ($_SESSION['db_datatable'] AS $item) {
echo "<h1>".$item['some_row']."</h1>";
}
} else {
$filename = "../datafile.sqlite3";
$db = new SQLite3($filename);
$q = "SELECT distinct productId, title, price, name FROM datatable LIMIT 16";
$results = $db->query($q);
while ($row = $results->fetchArray()) {
$_SESSION['db_datatable'][] = $row;
echo "<h1>Results</h1>";
}
$db->close();
}
Hope I have been of some help. Cheers!
So I have a backoffice called biblioteca.php where I have some requests and I can validate them trough a button called "Validar". That button redirects to a page like this: http://localhost/pap_16gpsi21/validacao.php?nproposta=87 where I can fill the form and submit.
What I want is to validate the request related to that url.
Example:
I've a request and his number is 90, I click on "Validar", then redirects me to a page like this http://localhost/pap_16gpsi21/validacao.php?nproposta=90, I fill the form and click submit. Then it updates the request number 90 in the database ($updateEstado = "UPDATE propostas SET validacao='Validado'";)
biblioteca.php
$selectProp = "SELECT nproposta, prioridade,disponibilidade,validacao,
autorizacao,aquisicao,registo,biblioteca,docente
FROM propostas
ORDER BY nproposta DESC";
$resultado = mysqli_query($ligaBD, $selectProp);
if (mysqli_num_rows($resultado) > 0) {
// output data of each row
while($row = mysqli_fetch_assoc($resultado)) {
<td><a class="btn btn-default" href="./validacao.php?nproposta=<?= $row["nproposta"] ?>">Validar</a></td>
valida.php
// gets nproposta from propostas
$npropostaLinha = "SELECT nproposta FROM propostas";
$resultado=mysqli_query($ligaBD, $npropostaLinha);
$nproposta = "";
printf($npropostaLinha);
$row = mysqli_fetch_array($resultado,MYSQLI_NUM);
printf("==> ");
print_r($row[0]);
$nproposta = $row[0];
$insertValidacao = "INSERT INTO validacao
(nproposta,nome_validacao,nif_validacao,
email_validacao,preco_validacao)
VALUES ($nproposta,'$nome_validacao','$nif_validacao',
'$email_validacao','$preco_validacao')";
$updateEstado = "UPDATE propostas SET validacao='Validado'";
$resultado = mysqli_query($ligaBD, $insertValidacao);
$resultado = mysqli_query($ligaBD, $updateEstado);
The problem is that if I have 3 requests (90,91,92) and I decide to validate just the number 91 it updates the first which is the number 90.
Also I know this isnt the safest method but this is just a test.
Hopefully, I explained explicitly. Sorry for any grammatical mistakes. Thank you
You need a couple of changes in your code as there are a few logical mistakes that I think you want to avoid.
You need to target a variable to specify from your SELECT that you desire a specific proposta;
You need, when updating, to specify which row, otherwise you will update every single record in your DB;
For such, go to your valida.php and add the following:
At the very top, check you have the variable ready
if (!isset($_POST['nproposta']) || empty($_POST['nproposta'])) {
//Do here what you desire to stop the script from running. Redirect back if you wish;
echo "No proposal id was found";
die;
}
$nPropostaID = $_POST['nproposta'];
Once you have your ID to target the row in your DB, update your query to consider it;
UPDATE propostas SET validacao='Validado' WHERE nproposta = $nPropostaID
Go to your form view and add below line within the form
<input type='hidden' value="<?php echo $_GET['nproposta']?>" name="nproposta">
NOTE: Because you mentioned you are aware of the SQL injections and this is a test I won't go with those, but always good to remember to be careful with them :) My proposal for the queries is just to get you going and in no way good for a script!
I have a edit profile page in my social media website.
When users click submit on the form. I run an update query to obviously update the users field in the database.
How can I optimize this scenario to include the logging of which particular fields are updated?
So for e.g.
One scenario could be:
Chris updated his profile picture.
Another scenario would be:
Chris updated his profile, inc:
Email
Username
Address
Address 2
Can anyone offer a solution to this?
I feel there is no need for code as all it is, is an update query.
Thanks
When writing out the form, save the current states in the $_SESSION-variable. The check the submitted forms and compare with the data in the $_SESSION-variable. Then only make an update on the forms that have changed.
if($_SESSION['name'] != $myform['name']) { $sql[] = "name = '{$myform['name']}'"; }
if($_SESSION['img'] != $myform['img']) { $sql[] = "img = '{$myform['img']}'"; }
$sqlstring = "UPDATE mytable SET " . implode(",",$sql);
// run the sql
EDIT: to implement logging:
// populate the variables (name, img) from the db/session with the highest revision number.
// ie SELECT * FROM mytable where userid = $userid ORDER BY revision DESC LIMIT 1
$revision = $_SESSION['revision'] + 1;
$sql = "INSERT INTO mytable SET img = '$img', name='$name', revision='$revision'";
Did you put all that information in a $_SESSION or something? If so, you can unset the session and declare it again, with the new info.
You can use custom setters / getters to do this. Check the code below.
You can also add additional checks to make sure the values have changed, or use magic methods to make things more dynamic.
class MyObject
{
protected $modifiedFields = array();
public function setField($value) {
if (!in_array('field', $this->modifiedFields)) {
$this->modifiedFields[] = 'field';
}
}
}
If you have the modified fields, you can just run the update query to contain only these fields.
I have a form where I am trying to implement a tag system.
It is just an:
<input type="text"/>
with values separated by commas.
e.g. "John,Mary,Ben,Steven,George"
(The list can be as long as the user wants it to be.)
I want to take that list and insert it into my database as an array (where users can add more tags later if they want). I suppose it doesn't have to be an array, that is just what seems will work best.
So, my question is how to take that list, turn it into an array, echo the array (values separated by commas), add more values later, and make the array searchable for other users. I know this question seems elementary, but no matter how much reading I do, I just can't seem to wrap my brain around how it all works. Once I think I have it figured out, something goes wrong. A simple example would be really appreciated. Thanks!
Here's what I got so far:
$DBCONNECT
$artisttags = $info['artisttags'];
$full_name = $info['full_name'];
$tel = $info['tel'];
$mainint = $info['maininst'];
if(isset($_POST['submit'])) {
$tags = $_POST['tags'];
if($artisttags == NULL) {
$artisttagsarray = array($full_name, $tel, $maininst);
array_push($artisttagsarray,$tags);
mysql_query("UPDATE users SET artisttags='$artisttagsarray' WHERE id='$id'");
print_r($artisttagsarray); //to see if I did it right
die();
} else {
array_push($artisttags,$tags);
mysql_query("UPDATE users SET artisttags='$artisttags' WHERE id='$id'");
echo $tags;
echo " <br/>";
echo $artisttags;
die();
}
}
Create a new table, let's call it "tags":
tags
- userid
- artisttag
Each user may have multiple rows in this table (with one different tag on each row). When querying you use a JOIN operation to combine the two tables. For example:
SELECT username, artisttag
FROM users, tags
WHERE users.userid = tags.userid
AND users.userid = 4711
This will give you all information about the user with id 4711.
Relational database systems are built for this type of work so it will not waste space and performance. In fact, this is the optimal way of doing it if you want to be able to search the tags.
<?php
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
$mysql = mysql_connect('corte.no-ip.org', 'hostcorte', 'xxxx');
mysql_select_db('fotosida');
if((isset($_POST['GetPersons'])))
{
if(isset($_POST['ID'])) {
$query = sprintf("SELECT * FROM persons WHERE id='%s'",
mysql_real_escape_string($_POST['ID']));
} else {
$query = "SELECT * FROM persons";
}
$res = mysql_query($query);
while ($row = mysql_fetch_assoc($res)) {
for ($i=0; $i < mysql_num_fields($res); $i++) {
$info = mysql_fetch_field($res, $i);
$type = $info->type;
if ($type == 'real')
$row[$info->name] = doubleval($row[$info->name]);
if ($type == 'int')
$row[$info->name] = intval($row[$info->name]);
}
$rows[] = $row;
}
echo json_encode($rows);
}
mysql_close($mysql);
?>
This works ok for generating a json object based on a database query. Im not very familiar with PHP, so i would like some feedback from you before i proceed with this. Is this a good way of calling the database using ajax? Other alternatives? Frameworks maybe?Are there any security problems when passing database queries like UPDATE, INSERT, SELECT etc using an ajax HTTPPOST? Thanks
To simplify CRUD operations definitely give REST a read.
As mentioned, stop using the # (AKA "shut-up") operator in favor of more robust validation:
if(isset($_GET['key'])){
$value = $_GET['key'];
}
Or some such equivalent.
Using JavaScript/AJAX, aggregate and send your request data, such as IDs and other parameters, from the form fields into a JSON object. Not the built query. The only time the client should be allowed to manipulate directly executed SQL is if you're creating an web based SQL client. Architect your URLs meaninfully (RESTful URLs) so that your HTTP request can be formed as:
GET users/?id=123
DELETE photos/?id=456
Or alternatively:
GET users/?id=123
GET photos/?method=delete&id=456
Server-side, you're going to receive these requests and based on parameters from the session, the request, etc., you can proceed by firing parametrized queries:
switch($method){
case 'get':
$sql = 'SELECT * FROM `my_table` WHERE `id` = :id';
break;
case 'delete':
$sql = 'DELETE FROM `my_table` WHERE `id` = :id';
break;
default:
// unsupported
}
// interpolate data from $_GET['id'] and fire using your preferred
// database API, I suggest the PDO wrapper.
See PDO
Generate output as necessary, and output. Capture on client-side and display.
Always validate and filter user input. Never send and execute raw SQL queries, or concatenate raw user input into SQL queries.
With regard to your question, here's a possible snippet:
(Note -- I haven't tested it, nor rigorously reviewed it, but it should still serve as a guide -- there is a lot of room for improvement, such as refactoring much of this logic into reusable parts; functions, classes, includes, etc.)
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
$error = array();
// get action parameter, or use default
if(empty($_POST['action']))
{
$action = 'default_action';
}
else
{
$action = $_POST['action'];
}
// try to connect, on failure push to error
try
{
$pdo = new PDO('mysql:dbname=fotosida;host=corte.no-ip.org', 'hostcorte', 'xxxx');
}
catch(Exception $exception)
{
$error[] = 'Error: Could not connect to database.';
}
// if no errors, then check action against supported
if(empty($error))
{
switch($action)
{
// get_persons action
case 'get_persons':
try
{
if(!isset($_POST['id']))
{
$sql = 'SELECT * FROM `persons`';
$stm = $pdo->prepare($sql);
$stm->execute();
}
else
{
$sql = 'SELECT * FROM `persons` WHERE `id` = :id';
$stm = $pdo->prepare($sql);
$stm->execute(array(
'id' => (int) $_POST['id'],
));
}
$rows = array();
foreach($stm->fetchAll() as $row)
{
$rows[] = $row;
}
}
catch(Exception $exception)
{
$error[] = 'Error: ' . $exception->getMessage();
}
break;
// more actions
case 'some_other_action':
// ...
break;
// unsupported action
default:
$error[] = 'Error: Unsupported action';
break;
}
}
// if errors not empty, dump errors
if(!empty($error))
{
exit(json_encode($error));
}
// otherwise, dump data
if(!empty($rows))
{
exit(json_encode($rows));
}
You can't do that. Sending database queries from the client is a huge security risk! What if he sends DROP TABLE fotosida as query?
You should always validate and sanitize data coming from the client before you do anything with it. Identify your use-cases and provide access to them with a clearly defined interface.
Update: To elaborate a bit about the interface you define. Say you're creating a gallery. Let's assume you have several use-cases:
Get a list of all images
Delete an image from the gallery
Upload an image to the gallery
There are different ways to do this, but the simplest way (for a beginner in PHP programming) is proably to have a PHP script for every case.
So you'll have:
imageList.php?gallery=1 that will return a list of all images in the gallery with ID 1
deleteImage.php?image=46 will delete the image with ID 46
uploadImage.php parameters will be passed via multipart POST and should be a uploaded file and the ID of the gallery where the image should be added to.
All these scripts need to make sure that they are receiving valid parameters. Eg. the ID should be a number, uploaded file needs to be checked for validity etc.
Only expose the needed functionality via your interface. This makes it much more secure and also better understandable for other users.
Like the other answers above, i agree that this is just asking for an injection attack (and probably other types). Some things that you can do to prevent that and enhance security in other ways could be the following:
1 Look for something suspicious with your response handler.
Lack of a query variable in the post, for instance, doesn't make sense, so it should just kill the process.
#$_POST["query"] or die('Restricted access');
2 Use preg_match to sanatize specific fields.
if (!preg_match("/^[a-zA-Z0-9]+$/", $_POST[query])){
die('Restricted access');
}
3 Use more fields, even if they are semi-meaningless and hidden, to add more reasons to kill the process through their absence, or lack of a certain text pattern (optional).
4 You shouldn't send a complete query through the POST at all. Just the elements that are necessary as input from the user. This will let you build the query in PHP and have more control of what actually makes it to the final query. Also the user doesn't need to know your table names
5 Use mysql_real_escape_string on the posted data to turn command characters into literal characters before entering data into a db. This way someone would have a last name of DROP TABLE whatever, instead of actually dropping table whatever.
$firstname = mysql_real_escape_string($_POST[fname]);
$lastname = mysql_real_escape_string($_POST[lname]);
$email = mysql_real_escape_string($_POST[email]);
$sql="INSERT INTO someTable (firstname, lastname, email)
VALUES('$firstname','$lastname','$email')";
6 Last, but not least, be creative, and find more reasons to kill your application, while at the same time giving the same die message on every die statement (once debugging is done). This way if someone is hacking you, you don't give them any feedback that they are getting through some of your obstacles.
There's always room for more security, but this should help a little.
You shouldn't trust your users so much! Always take into account, when working with Javascript, that an user could edit your calls to send what (s)he wants.
Here you are taking the query from the GET parameters and executing it without any kind of protection. How can you trust what $_GET['query'] contains? A way to do this would be to call a php page with some parameters through ajax, validate them using PHP and then execute a query built on the parameters you get, always thinking about what the values of such parameters could be.