Updating database according to check-boxes checked - php

I've the following table layout in my database with some data.
I'm taking input by check-boxes so user can select all applicable accident road conditions and it to database. I would say it is okay if you're adding a new record you just loop through the checkboexs checked and insert them in DB
The first information is now saved in the database, now user decided to change the road conditions for any reasons user came back and change it the to the following.
Now my question, how should I update my table. The first thing that came into my mind was to delete the record that were already there and insert the new one's.
My real issue here is, assume user have choose the 3 items before but changed it two or one then how would i delete the those are not checked you know what I'm saying. Below is some code snippets that I've been trying.
$accidentRoadConditions = AccidentRoadConditions::findAccidentRoadConditions($acc_det_id);
$wc_array = [];
while ($roadConditions = $accidentRoadConditions ->fetch(PDO::FETCH_OBJ)) {
$wc_array[] = $roadConditions ->rc_id;
}
Above I'm selecting all the road conditions that is already stored in the database.
if (isset($_POST['rta_ad_rc'])) {
foreach ($_POST['rta_ad_rc'] as $rc_id) {
//AccidentRoadConditions::save(array(null, $ad_lsid, $rc_id));
// $tmprory = AccidentRoadConditions::findByADAndRCIds($acc_det_id, $rc_id);
// if(!$tmprory){
// AccidentRoadConditions::save(array(null, $acc_det_id, $rc_id));
// }
if(in_array($rc_id, $wc_array)){
$errors[] = "in array <br />";
unset($wc_array[0]);
}
}
}
So my question is how to update values in database according to what was checked by user and deleting those which were unchecked which were checked before. Getting bit complicated so simply how to update database according to above mention scenario.
Any Idea?

I think you need to do the following
Store the selected checks in an array
Check in the database if any of those are already saved or not
if yes, skipped them otherwise add them into an array
$old_rc_array = [];
$new_rc_array = [];
while ($roadConditions = $accidentRoadConditions->fetch(PDO::FETCH_OBJ)) {
$old_rc_array[] = $roadConditions->rc_id;
}
if (isset($_POST['rta_ad_rc'])) {
foreach ($_POST['rta_ad_rc'] as $rc_id) {
if(in_array($rc_id, $old_rc_array)){
unset($old_rc_array[array_search($rc_id, $old_rc_array)]);
}else{
$new_rc_array[] = $rc_id;
}
}
}
foreach ($old_rc_array as $rc_to_delete) {
AccidentRoadConditions::deleteByADIdAndRCId($hidden_acc_det_id, $rc_to_delete);
}
foreach ($new_rc_array as $rc_to_insert) {
AccidentRoadConditions::save(array(null, $hidden_acc_det_id, $rc_to_insert));
}

I think this is what you should do.
Create composite unique constraint on ad_id and rc_id
Delete all the rows not in the selected checkbox ids.
Try to insert all the rows but user INSERT IGNORE. This will insert the record if it does not exist or it will just ignore it. As you are using some framework see how you can do that.
If you can not then just wrap it using try/catch and ignore if the error is related to constraint violation.
This way You don't need to check if the values exist and also there will not be any unnecessary inserts.

Related

Problem Regarding Updating Values using Foreach

This is the input fields
<?php while($educationalQualificationsFromDB = Database::fetchData($queryForEducationalQualifications))
{
$eduQualifcationId = $educationalQualificationsFromDB['education_qualification_id'];
$eduQualifcation = $educationalQualificationsFromDB['edu_qualification'];
echo "<input class='form-control' type='text' name='eduqualification[]' value='$eduQualifcation'>";
echo "<br>";
}
?>
This is the query I used,
$eduQualifications = $_POST['eduqualification'];
foreach($eduQualifications as $oneEduQualifications)
{
Database::query("UPDATE educational_qualification SET edu_qualification = '$oneEduQualifications'");
}
I'll simply explain like this there are multiple values coming from the database from the educational qualifications table.I have used a while loop to fetch them all inside inputs.And there are several inputs right.So I need a condition to update all those relevant database data.I used foreach loop to fetch data from the inputs cause i used the name of the input fields as an array.When I update them using foreach loop it update all records with the same name.Please explain me why such thing happened and give me a solution to update all relevant multiple database values with the relevant input values.
An UPDATE query will update all rows, unless constrained to specific rows by a WHERE clause. So you'll need to add something like:
UPDATE educational_qualification
SET edu_qualification = '$oneEduQualifications'
WHERE education_qualification_id = '$eduQualifcationId'
So you need to transport the $eduQualifcationId through the form together with the $eduQualifcation as well. The best way for that is to just use it as the $_POST array key:
<input type='text' name='eduqualification[$eduQualifcationId]' value='$eduQualifcation'>
Now your $_POST array will look something like:
array(
'eduqualification' => array(
'42' => '69'
)
)
So you can do:
foreach ($_POST['eduqualification'] as $id => $qualification) {
Database::query("UPDATE educational_qualification SET edu_qualification = '$qualification' WHERE education_qualification_id = '$id'");
}
As is, you appear to be open to both SQL and HTML injection BTW, which you'll want to fix:
How can I prevent SQL injection in PHP?
How to prevent XSS with HTML/PHP?
If you have multiple users, and certain users should only be allowed to update their own data, then you'll want even more constrains and checks, something like:
UPDATE educational_qualification
SET edu_qualification = '$oneEduQualifications'
WHERE education_qualification_id = '$eduQualifcationId'
AND user = '$current_user'
Because POST requests are just HTTP requests, and anyone can send any arbitrary data in an HTTP request…

Insert Json object to database in php

I am working on an android app which uses APIs made with php. Here, i am dynamically creating columns and their values.
I am verifying the API via postman and a strange thing happens every time, While looping through the Json Object what i am doing is first creating column and then inserting its values.
The problem is only the 1st iteration saves the element and rest of them only creates the column but does not insert the values. I don't know if i am doing anything wrong, below is my php code.
<?php
include("connection.php");
$data = file_get_contents('php://input');
$json_data = json_decode($data);
foreach($json_data as $key => $val) {
$column_name = $key ;
$c_column_name = preg_replace('/[^a-zA-Z]+/', '', $column_name);
$column_value = $val ;
$table_name = "test2";
$email = "ht#t.com";
$result = mysqli_query($conn,"SHOW COLUMNS FROM $table_name LIKE '$c_column_name'");
$exists = (mysqli_num_rows($result))?TRUE:FALSE;
if($exists) {
$query1 = "INSERT INTO $table_name($c_column_name)VALUES('$column_value') ";
$data0=mysqli_query($conn,$query);
if($data0)
{
echo json_encode(array("success"=>"true - insertion","message"=>"Column existed, Successfully data sent."));
}
else{
echo json_encode(array("success"=>"false - insertion","message"=>"Column existed, data not inserted."));
}
}
else{
$query2="ALTER TABLE $table_name ADD COLUMN `$c_column_name` varchar(50) NOT NULL";
$data1=mysqli_query($conn,$query2);
if($data1){
$query3="INSERT INTO $table_name($c_column_name)VALUES('$column_value')";
$data2=mysqli_query($conn,$query3);
if($data2)
{
echo json_encode(array("success"=>"true - insertion","message"=>"Successfully data sent."));
}
else{
echo json_encode(array("success"=>"false - insertion","message"=>"Column created but data not inserted."));
}
}
else
{
echo json_encode(array("success"=>"false - column creation","message"=>"Failed to create column.'$column_name', '$table_name', '$conn'"));
}
}
}
?>
Here is the Json Object through postman.
{"Shape":"rewq","Trans.No.":"yuuiop","Color":"qwert"}
Please help me with this, any help or suggestions are highly appreciated.
The second column name is Trans.No. which contains a dot, this is why it fails, probably you have an error as a result which prevents further columns from being created.
I think it would be much better to have a table with this structure:
attributes(id, key, value)
and whenever a key-value pair is received, you just insert/update it, depending on the logic you need to be executed. Your current model will create a separate row for each attribute, which is probably not what you want to achieve.
EDIT
Based on the information received in the comment section I reached the following conclusion:
You could create the missing columns first and then generate the insert statement with all the columns, having a single insert.
But it would be better to not create a separate column for each value, as the number of columns could quickly get out of hand. Instead you could have a table:
myentity(id, name)
for storing the entities represented by the JSON and
attributes(id, myentity_id, key, value)
for storing its attributes. This would be a neat schema with all the dinamicity you could want.

To delete() or forceDelete(). A better way to determine

I have a database with 95 tables. A users tables exists for the system users. Many other tables (45 out of the 95) have a "created_by" that refers to the user who created/added the row, through users.id.
Now. If I wanted to delete a user, I just cannot go and do $user->delete(), I need to keep the user around (soft-delete it) in case this user has created rows on other tables. But what if this user didn't add any content, then I should just go ahead and $user->forceDelete() it.
My question is: is there a good way to go about doing this? To check whether a user should be deleted or Force-deleted when we have this big number of tables.
I figured I could just loop though the tables and check if the id of the user (to be deleted) exists, if found then it's ->delete(), else it's ->forceDelete(). Here is the code:
// Get all tables
$allTables = \DB::connection()->getDoctrineSchemaManager()->listTableNames();
$tablesWithCreatedBy = [];
foreach($allTables as $tableName){
$tableColumns = \DB::getSchemaBuilder()->getColumnListing($tableName);
if(in_array('created_by', $tableColumns)){
$tablesWithCreatedBy[] = $tableName;
}
}
foreach($tablesWithCreatedBy as $tableName){
$result = \DB::select(" SELECT created_by FROM `$tableName`
WHERE `created_by` = {$this->user->id} LIMIT 0, 1 ");
if(isset($result[0])){
$this->user->delete();
break;
}
}
// If wasn't trashed from the code above, then force delete the user!
if(!$this->user->trashed()){
$this->user->forceDelete();
}
I feel there must be a better way to do it! Is there?
You've to add records_count to users table that will be incremented every time when user adds content to other tables, so after that change solution will be simple as:
$result = ($this->user->records_count > 0)
? $this->user->delete()
: $this->user->forceDelete();
Or write Laravel Console Command and run it at background that will walk through db and do cleanup operations.

How to remove rows or filter a recordset or collection

I would like to know if there is a way to remove rows or filter a recordset or collection.
For example, if I have two tables: one for questions and one for the answer choices. The questions belong to different forms. Questions 1-10 belongs to form a, 11-20 belong to form b. Depending on the answers of the previous questions, certain questions may or may not show up, and certain answers later on may or may not show up. Instead of constantly hitting the database, I want to cache the recordset or collection of questions belonging to each form into memory and filter off of the in memory set of questions per session.
This way each user will only hit the database once, at the beginning of their session, instead of every time they click on next.
The Collection object used by the models is extended from lithium\util\Collection which provides a method for filtering an existing collection and returning a new one based on a user provided closure.
$newQuestions = $oldQuestions->find(function($question) {
if (your new criteria) {
return true;
}
return false;
});
Simply determine the criteria you wish to apply and perform the filtering in the closure. Once it runs you should have a new Collection object with only the records that matched.
After you get a Recordset or Collection from your database, you can execute a couple of filters on it. See the lithium\util\Collection for more info.
An example would be
$questions = Questions::all();
$form_questions = $questions->find(function($question) {
if($query->form == 'b') {
return true;
}
return false;
}), true);
To handle keeping these questions persist between page requests, look into lithium\storage\Session.
It's unlikely that simple 'once-per-page' database calls will put much of a strain on your server unless you have truely terrific traffic, but if you do want to do this, the easiest way to do this will be to cache this information in the PHP $_SESSION superglobal when a user logs in. Assuming you've set PHP to use filesystem storage (though even if you use database session storage it will have only a tiny affect on performance), you'll have your questions stored in super fast-to-access files which are already pre-built to be unique to each specific user. As soon as a script is loaded, the session file is automatically read into memory and you can access any of the information from there.
EXAMPLE:
Assuming that your questions table has columns question_number and question_text and your answers table has the columns question_number and answer_text:
<?php
//on login:
//first get the answer array, so we can use it in the logic below:
$query = mysql_query('SELECT * FROM `questions` WHERE [criteria]',[connection identifier]) or die(mysql_error());
if (!mysql_num_rows($query)){
die("No questions!");
}
$answer_array = array();
//create a keyed array that you can access by question number
while($row=mysql_fetch_array($query)){
$answer_array[$row['question_number']] = $row['answer_text'];
}
//now get the questions and put everything into the session variable
$query = mysql_query('SELECT * FROM `questions` WHERE [criteria]',[connection identifier]) or die(mysql_error());
if (!mysql_num_rows($query)){
die("No questions!");
}
//loop through the results and generate session arrays we can work with later
while($row = mysql_fetch_array($query)){
//assign the question to the correct form:
if ($row['question_number']<=10){
$session_key = 'form_a';
} elseif($row['question_number']<=20){
$session_key = 'form_b';
} elseif($row['question_number']<=30){
$session_key = 'form_c';
} else {
$session_key = 'form_d';
}
//if the session variable does exist yet, create it:
if (!isset($_SESSION[$session_key])){
$_SESSION[$session_key] = array();
}
//get the existing answer if it exists, otherwise leave the answer blank:
$my_answer = "";
if(isset($answer_array[$row['question_number']])){
$my_answer = $answer_array[$row['question_number']];
}
//add this question array as a child array element in the session array, keyed by the question number
$_SESSION[$session_key][$row['question_number']] = array(
'question' => $row,
'answer' => $my_answer
);
}
Now, if we're loading Form B, for instance, we can just read it out of the session array $_SESSION['form_b'] and perform any logical switches we want based on the answers to previous questions:
$html = "";
foreach($_SESSION['form_b'] as $question_number => $data){
//perform any logic, for instance, if question 2 from form a is equal to '5', don't show question 3 on form B:
switch($question_number){
case '3': if ($_SESSION['form_a']['2']['answer']=='5'){ continue 2; }; break;
}
//add the question to the form, and populate the answer if they already answered it earlier:
$html .= "<label>".$data['question']."<input type='text' value=\"".$data['answer']."\" name='question_".$question_number."' /></label>";
}
Then, when you submit each form, in addition to updating the mysql answers table, you'll also want to update your _SESSION array. For instance, if you're submitting form B via POST:
$form = 'form_b';
foreach($_POST as $key=>$value){
if (substr($key,0,strlen('question_')!='question_'){
continue;
}
$number = str_replace('question_','',$key); //this will give us the question number
$saved = add_answer($number,$value); //call the function to insert the new answer into the database (this is a dummy function, and please make sure to escape your variables
if ($saved){//assuming it saved:
$_SESSION[$form ][$number]['answer']=$value; //now we've updated the session array as well.
}
}

using REPLACE INTO for both new items and edited items?

This refactor seems workable in my head, but I'd like someone to check my logic:
Current process:
if (item == new) {
INSERT INTO basic_table
$itemUID = get last insert ID // item's UID
INSERT INTO another_table // more stuff
INSERT INTO another_table2 // more stuff
INSERT INTO another_table3 // more stuff
} else {
$itemUID = $_POST['uid']
UPDATE basic_table
REPLACE INTO another_table // more stuff
REPLACE INTO another_table2 // more stuff
REPLACE INTO another_table3 // more stuff
}
(note - REPLACE INTO is used for existing items because they may or may not have entries in all the tables, depending on their initial configuration)
It occurred to me that since all the follow-on queries are identical except for INSERT INTO // REPLACE INTO, I should be able to refactor as:
if (item == new) {
INSERT INTO basic_table
$itemUID = get last insert ID // item's UID
} else {
$itemUID = $_POST['uid']
UPDATE basic_table
}
REPLACE INTO another_table // more stuff
REPLACE INTO another_table2 // more stuff
REPLACE INTO another_table3 // more stuff
Considering that I'm using PDO and each of those queries has lots of parameters, this would save a crapload of space.
But I wanted to post it here first, to make sure I'm not overlooking something.
Would this refactoring produce the same result?
If you don't care about the primary key changing, use REPLACE. If the key needs to remain consistent, as in, if it has been mapped to other tables, continue to use INSERT and UPDATE. REPLACE deletes and re-inserts so if your primary key is an auto_increment field it will change to the new increment value.

Categories