Dynamic PHP website using POST arguments and recursive function - php

I'm creating a website using PHP for the logic part of it.
It is a dynamic test. A set of questions are presented to the user and depending on his answers, a new set of questions will appear, thus, the questions will always be different if the answers are different.
In the form of the test, I have used POST to get the answers of the user and the action is empty because I want the page to reload but with different arguments (POST) every time. I also use a recursive function that takes the ID's of the next questions to be presented and the level (set of questions) just to not get lost. This recursive function also uses the arguments in POST to, again, obtain the user's answers so it can get the next questions at my DB.
function searchTree($arrNodes, $nivel){
$nxtQ = array(); $j=1;
$arr=getPreguntas($arrNodes,$nivel); //arr has "question" objects
$numPreg=count($arr);
for ($i=0; $i < count($arr); $i++) {
$arrayA[$i] = treeRule($arr[$i]->idPreg); //Array[i] is the set of related rules
}
echo '<form action="" method="post">';
for ($i=0; $i < $numPreg; $i++) {
echo $arr[$i]->pregunta .'</br>';
insertHTML($arr[$i]->idPreg, $numPreg, $j, $nivel); //Add HTML
$j++;
}
echo '</br><input type="submit" id="buttonSend'.$nivel.'" name="buttonSend'.$nivel.'" value="Enviar respuesta">';
echo '</form>';
$button= 'buttonSend'.$nivel;
if(isset($_POST[$button])){ //If the user answered
for ($i=0; $i < count($arrayA); $i++) { //arrayA has all related rules
if(!empty($arrayA[$i])){ //At least one related rule
$radio = 'valorRadio'.($i+1).'_'.$nivel;
$node = 'idNode'.($i+1).'_'.$nivel;
$answ[$i] = analize($_POST[$radio],$_POST[$node],$arrayA[$i],$i+1, $nivel); //$answ is the set of related rules which happened to be true
if($answ[$i]!=null){
echo 'True related rule: '.$answ[$i].' </br> ';
}else{
echo 'Answer all questions';
}
}else{ //End of analysis
echo 'No related rules </br>';
}
}
$relatedMR = getmetaRules($arrayA);
for ($i=0, $j=0; $i < count($relatedMR); $i++) {
$res = analizeMR($relatedMR[$i], $arrayA);
if($res=='1'){
$nxtQ[$j] = getNxtQ($relatedMR[$i]); //nxtQ is the set of next questions' IDs
$j++;
}
}
if($nxtQ!=NULL){
$_POST=array();
$arrNodes = array();
searchTree($nxtQ,($nivel+1));
}else{
echo 'End of test';
}
}else{
time_sleep_until($_POST);//Wait for an answer
}
}
I call this function at the beginning of my program like this:
searchTree(initialNodes(1),1);
initialNodes() function only enters the DB and gets the questions with level 1.
The first and second set of questions are presented (kind of) correctly (the first set of questions is always present and the second one appears under it, but that can be fixed at front-end.), however, the third one if not working anymore. The POST arguments are there but the new questions and radio buttons do not appear.
First set and POSTFirst and second set and POSTThird attempt and POST
I created an easier example of this by not using "sets of questions" but only one question each time. The first and second questions work as I previously mentioned but for the third one, the second one is replaced by the third and the third by the fourth and so on.
I don't know if my program is getting confused by the names of my HTML elements, if my recursive function is wrong or something else.

Related

Using For loop to get values of multiple elements in PHP

The title is so general mainly because I don't know what should be the appropriate title for it. Let me just explain the situation:
Say that I have two textboxes named LastName0 and FirstName0 and a button called addMore. When I click addMore, another two textboxes will be created through JavaScript. These textboxes will be named LastName1 and FirstName1. When I click the addMore button again, another two textboxes button will be created and named LastName2 and FirstName2 respectively. This will go on as long as the addMore button is clicked. Also, a button named deleteThis will be created alongside the textboxes. This simply deletes the created textboxes when clicked.
I also initialized a variable called counter. Every time the addMore button is clicked, the counter goes up by 1, and whenever the deleteThis button is clicked, the counter decreases by 1. The value of the counter is stored in a hidden input type.
When the user submits the form, I get the value of the counter and create a For loop to get all the values of the textboxes in the form. Here is the sample code:
//Suppose that the user decides to add 2 more textboxes. Now we have the following:
// LastName0 FirstName0
// LastName1 FirstName1
// LastName2 FirstName2
$ctr = $_POST['counter']; //the counter == 3
for ($x = 0; $x < $ctr; $ctr++)
{
$lastname = $_POST["LastName$x"];
$firstname = $_POST["FirstName$x"];
//This will get the values of LastName0,1,2 and FirstName0,1,2
//code to save to database…
}
On the code above, if the value of counter is equal to 3, then the values of textboxes LastName0,1,2 and FirstName0,1,2 will be saved. Now here is the problem: If the user decided to delete LastName1 and FirstName1, the For loop will not be able to iterate properly:
$ctr = $_POST['counter']; //the counter == 2
for ($x = 0; $x < $ctr; $ctr++)
{
//Only LastName0 and FirstName0 will be saved.
$lastname = $_POST["LastName$x"];
$firstname = $_POST["FirstName$x"];
//code to save to database…
}
Someone told me to use the "push and pop" concept to solve this problem, but I am not really sure on how to apply it here. So if anyone can tell me how to apply it, it'll be grand.
Add your input text boxes with name as array ie, <input type="text" name="FirstName[]" />
In php you can fetch them as a array. ie,
foreach($_POST["FirstName"] as $k=>$val){
echo $val; // give you first name
echo $_POST["LastName"][$k]; // will give you last ame
}
In this case even if one set of field is removed in HTML will not affect the php code.
One solution would be to use the isset function like this:
$ctr = $_POST['counter'];
for ($x = 0; $x < $ctr; $ctr++)
{
isset($_POST["LastName$x"])?$lastname = $_POST["LastName$x"]:;
isset($_POST["FirstName$x"])?$firstname = $_POST["FirstName$x"]:;
}
If it is possible, instead of using LastNameN and FirstNameN names try using LastName[N] and FirstName[N], this way the result is an array and you can iterate through it with a foreach, meaning you will not need the counter and the index of the value will not be important:
foreach ($_POST["LastName"] as $i=>$lastname) {
if (!isset($_POST["FirstName"][$i])) {
// This should only happen if someone messes with the client side before posting
throw new Exception("Last name input does not have a related First name input");
}
$firstname = $_POST["FirstName"][$i];
}
If not, then you may have to use your $counter in a different way
$current = 0;
while ($counter) { // Stop only when i found all
if (isset($_POST["LastName$current"]) {
$counter--; // Found one
$lastname = $_POST["LastName$current"];
$firstname = $_POST["FirstName$current"];
}
$current++;
}
A better way to solve this would be to use arrays for Firstname and Lastname. Instead of calling them Lastname0 and Firstname0, then Lastname1 and Firstname1, call them all Lastname[] and Firstname[]. Give them ID's of Lastname0 and Firstname0 and so on for the delete function, but keep the names as arrays.
When the form is submitted use the following:
foreach($_POST['Lastname'] as $i => $lastname) {
$firstname = $_POST['Firstname'][$i]
//... code to save into the database here
}
Be warned though that in IE if you have an empty field it will not be submitted, so if Lastname0 has a value, but Firstname0 does not, then $_POST['Firstname'][0] will in fact contain the value of Firstname1 (assuming it has a value in it). To get around this you can use javascript to check if a field is empty when submitting the form, and if so put the word EMPTY in it.
Do not use counter if not required
A much easier way is to add array name when admore clicked.
Give a name like first_name[] in textbox
if you create form like that you can use foreach through $_POST['first_name']
try var_dump($_POST) in you php code to see how things goes on.
Inside your for loop, maybe you could try...
if ((isset($_POST["LastName$x"])) && (isset($_POST["FirstName$x"]))){
$lastname = $_POST["LastName$x"];
$firstname = $_POST["FirstName$x"];
//code to save to database…
}
This will check if the variables exists before you try to do anything with them.

Single dynamic values submitted through one form

I am using Jquery UI Selectable. The user has the option to dynamically add new list items to the original setup.
I would like to include a 'clear' button that will give the user the ability to clear each individual item they created by clicking on an X input submit (img).
HTML (php)
if ($this->session->userdata('inactivefilter') == true) {
$inactivefilter = $this->session->userdata('inactivefilter');
$i=0;
foreach ($inactivefilter as $filter)
{
$filterdash = implode('-', explode(' ', $filter));
echo "<li class='ui-state-default' id='$filterdash'>$filter</li>";
echo "<div id=clear>
<form method='POST' action='".base_url()."main/clear_filter'>
<input type='image' name='filtervalue' value='$i' src='".base_url()."img/board/icons/clear.png'></input>
</form>
</div>";
$i++;
}
}
This is where the list is created. specifically the clear_filter action form.
Clear_filter currently 'attempts' to grab the value of '$i' but I don't know how to pass that correctly.
here is the controller:
public function clear_filter()
{
$i = $_POST['filtervalue'];
$this->thread_model->clear_filter($i);
}
I'll omit the clear_filter model due to its irrelevance to the problem.
Essentially, I just need $i to be picked up based on whatever value is clicked on in the dynamic form on the actual page.
Any help would be appreciated!
Well, it seems like I just had things a bit backwards.
The code was more or less correct.
For Codeigniter, you catch the passed input value=$i by using the name ="filtervalue"
Change the controller code to :
$i = $this->input->post('filtervalue');
and $i is set to whatever value was clicked on.

php script optimisation

I'm not a php guru, need your help.
I have a php form with select menu. The menu is formed form the customers table e-mails. I've used the following code:
for($i = 0; $i < sizeof($customers_table); $i++){
echo '<option value="'.$customers_table[$i]['id'].'">'.$customers_table[$i]['email'].'</option>';
}
The base is rather big, so it takes time to form the option field. Is there any way to optimize the script so it will work faster.
Thank you.
As the size does not change, store it:
$size = sizeof($customers_table);
for ($i = 0; $i < $size; $i++) {
echo '<option value="'.$customers_table[$i]['id'].'">'.$customers_table[$i]['email'].'</option>';
}
Then you can shorten the loop condition and reduce the array access:
$i = $size = sizeof($customers_table);
while ($i) {
$table = $customers_table[$size-$i--];
echo '<option value="'.$table['id'].'">'.$table['email'].'</option>';
}
Next part is the echo statement which can just output instead of concatinating strings before outputting them:
$i = $size = sizeof($customers_table);
while ($i) {
$table = $customers_table[$size-$i--];
echo '<option value="', $table['id'], '">', $table['email'], '</option>';
}
If this makes a difference, you need to metric. The largest time taken might be the amount of HTML you send to the browser. But that would be out of the scope of this fragment.
To improve the overall readability of the code I suggest using foreach:
foreach ($customer_table as $row)
{
echo '<option value="', $table['id'], '">', $table['email'], '</option>';
}
This generally is pretty fast as well.
As Andy said, it's not a good idea to optimize this script that way.
If you want significant time changes, you have to rethink the problem.
You have a list of customers, and if there is a problem of time, I assume that there are a lot. So, a drop-down list would be useless for a user.
There are other solutions, more efficient :
Using a sort of caching > Your html code will always be the same, so you just need to do the loop once, when you add, delete or update a customer, and store the html code in a script or database. So, each time your page is reloaded, you just have to retrieve the html code.
Do not load all the customers > The user doesn't need to see all the customers, just the ones he's interested in, so with some Ajax you can load the options while the user is typing (see this for example: jQuery Autocomplete)
Those solutions are the best for optimization, and if you combine them (caching and autocomplete above 3 characters typed) it will be even better for user experience too.

checkbox's stay checked after pagination in php

Hello i want any checkbox i am gonna check, to stay checked after pagination.
here is the code:
foreach($test as $string){
$queryForArray = "SELECT p_fname,p_id FROM personnel WHERE p_id = " .$string["p_id"]. " ;" ;
$resultForArray = mysql_query($queryForArray, $con);
$rowfForArray = mysql_fetch_array($resultForArray);
?>
<td id="<?php echo $rowfForArray["p_id"]?>" onclick="setStyles(this.id)" ><?php echo $rowfForArray["p_fname"]?></td>
<td><input id="<?php echo $rowfForArray["p_id"]?>" class="remember_cb" type="checkbox" name="how_hear[]" value="<?php echo $rowfForArray["p_fname"]?>"
<?php foreach($_POST['how_hear'] as $_SESSION){echo (( $rowfForArray["p_fname"] == $_SESSION) ? ('checked="checked"') : ('')); } ?>/></td>
</tr>
<tr>
I am geting the data from a search result i have in the same page , and then i have each result with a checkbox , so that i can check the "persons" i need for $_Session use.
The only think i want is the checkbox's to stay checked after pagination and before i submit the form!(if needed i can post the pagination code, but he is 100% correct)
In the checkbox tag use the ternary operation, without that foreach inside him:
<input [...] value="<?php echo $rowfForArray["p_fname"]?>" <?php $rowfForArray["valueToCompareIfTrue"] ? "checked='checked'" : ''; ?> />
because the input already is inside of 'for' loop, then each time of the loop will create a new checkbox wich will verify if need to being check or not.
I hope I have helped you.
A few ways to tackle this:
(Straight up PHP): Each page needs to be a seperate form then, and your "next" button/link needs to submit the form everytime they click next. The submit data should then get pushed to your $_SESSION var. The data can then be extracted and used to repopulate the form if they navigate backwards as well. Just takes some clever usage of setting the URL with the proper $_GET variables for the form.
(HTML5): This will rely more on JavaScript, but basically you get rid of pagination and then just break the entire data set into div chunks which you can hide/reveal with JavaScript+CSS or use a library like JQuery.
(AJAX): Add event listeners to the checkboxes so that when a button is checked an asynchronous call is made back to a PHP script and the $_SESSION variable is updated accordingly. Again, this one depends on how comfortable you are with JavaScript.
Just keep in mind that PHP = ServerSide & JavaScript = ClientSide. While you can hack some PHP together to handle "clientside" stuff, its usually ugly and convoluted...
I did it without touching the database...
The checkbox fields are a php collection "cbgroup[]".
I then made a hidden text box with all the values which equal the primary keys of the selectable items mirroring the checkboxes. This way, I can iterate through the fake checkboxes on the current page and uncheck the checkboxes by ID that exist on the current page only. If the user does a search of items and the table changes, the selectable items remain! (until they destroy the session)
I POST the pagination instead of GET.
After the user selects their items, the page is POSTED and I read in the hidden text field for all the checkbox IDs that exist on that current page. Because PhP only tells you which ones are checked from the actual checkboxes, I clear only the ones from the session array that exist on the POSTED page from this text box value. So, if the user selected items ID 2, 4, 5 previously, but the current page has IDs 7,19, and 22, only 7, 19, and 22 are cleared from the SESSION array.
I then repopulate the array with any previously checked items 7, 19, or 22 (if checked) and append it to the SESSION array along with 2, 4, and 5 (if checked)
After they page through all the items and made their final selection, I then post their final selections to the database. This way, they can venture off to other pages, perhaps even adding an item to the dB, return to the item selection page and all their selections are still intact! Without writing to the database in some temp table every page iteration!
First, go through all the checkboxes and clear the array of these values
This will only clear the checkboxes from the current page, not any previously checked items from any other page.
if (array_key_exists('currentids', $_POST)) {
$currentids = $_POST['currentids'];
if (isset($_SESSION['materials']) ) {
if ($_SESSION['materials'] != "") {
$text = $_SESSION['materials'];
$delimiter=',';
$itemList = explode($delimiter, $text);
$removeItems = explode($delimiter, $currentids);
foreach ($removeItems as $key => $del_val) {
//echo "<br>del_val: ".$del_val." - key: ".$key."<br>";
// Rip through all possibilities of Item IDs from the current page
if(($key = array_search($del_val, $itemList)) !== false) {
unset($itemList[$key]);
//echo "<br>removed ".$del_val;
}
// If you know you only have one line to remove, you can decomment the next line, to stop looping
//break;
}
// Leaves the previous paged screen's selections intact
$newSessionItems = implode(",", $itemList);
$_SESSION['materials'] = $newSessionItems;
}
}
}
Now that we have the previous screens' checked values and have cleared the current checkboxes from the SESSION array, let's now write in what the user selected, because they could have UNselected something, or all.
Check which checkboxes were checked
if (array_key_exists('cbgroup', $_POST)) {
if(sizeof($_POST['cbgroup'])) {
$materials = $_POST['cbgroup'];
$N = count($materials);
for($i=0; $i < $N; $i++)
{
$sessionval = ",".$materials[$i];
$_SESSION['materials'] = $_SESSION['materials'].$sessionval;
}
} //end size of
} // key exists
Now we have all the items that could possibly be checked, but there may be duplicates because the user may have paged back and forth
This reads the entire collection of IDs and removes duplicates, if there are any.
if (isset($_SESSION['materials']) ) {
if ($_SESSION['materials'] != "") {
$text = $_SESSION['materials'];
$delimiter=',';
$itemList = explode($delimiter, $text);
$filtered = array();
foreach ($itemList as $key => $value){
if(in_array($value, $filtered)){
continue;
}
array_push($filtered, $value);
}
$uniqueitemschecked = count($filtered);
$_SESSION['materials'] = null;
for($i=0; $i < $uniqueitemschecked; $i++) {
$_SESSION['materials'] = $_SESSION['materials'].",".$filtered[$i];
}
}
}
$_SESSION['materials'] is a collection of all the checkboxes that the user selected (on every paged screen) and contains the primary_key values from the database table. Now all you need to do is rip through the SESSION collection and read\write to the materials table (or whatever) and select/update by primary_key
Typical form...
<form name="materials_form" method="post" action="thispage.php">
Need this somewhere: tracks the current page, and so when you post, it goes to the right page back or forth
<input id="_page" name="page" value="<?php echo $page ?> ">
if ($page < $counter - 1)
$pagination.= " next »";
else
$pagination.= "<span class=\"disabled\"> next »</span>";
$pagination.= "</div>\n";
Read from your database and populate your table
When you build the form, use something like this to apply the "checked" value of it equals one in the SESSION array
echo "<input type='checkbox' name='cbgroup[]' value='$row[0]'";
if (isset($filtered)) {
$uniqueitemschecked = count($filtered);
for($i=0; $i < $uniqueitemschecked; $i++) {
if ($row[0] == $filtered[$i]) {
echo " checked ";
}
}
}
While you're building the HTML table in the WHILE loop... use this. It will append all the select IDs to a comma separated text value after the loop
...
$allcheckboxids = "";
while ($row = $result->fetch_row()) {
$allcheckboxids = $allcheckboxids.$row[0].",";
...
}
After the loop, write out the hidden text field
echo "<input type='hidden' name='currentids' value='$allcheckboxids'>";

Trying and failing to place looped content within a div

Basically here is the scenario:
I am trying to generate a test from a database. Each test has many questions(items) each item is of a certain type (multi-choice etc), each question has multiple answers attached to it.
(I'm not looking for somebody to tell me im going about it all wrong)
I currently, loop through my query results and print out every question there. Within that loop I execute a function (present_question) for every question. Within this function is a switch case, which identifies the item type. Within each case, I run another loop to print out all the answers attached to that question.
What I am trying to do, is insert each question into a unique <div> so that I toggle the display etc between each question as they select a button for each so 1,2,3,4 etc.
However, it would appear I am struggling with this. My questions all print out, however It appears they are not within individual <divs> as I tried testing it by changing some attributes of the class and its doing nothing. :(
Here is the main code covering the whole procedure:
main loop:
//loop through every question present in query results and run function to present the different question structures
while ($qs = mysql_fetch_assoc($get_questions))
{
$type = $qs['item_type'];
$item_id = $qs['item_id'];
$question = $qs['question_text'];
echo "<div class='q_center' id='q_$q_num'>"; // insert each question into its own div
echo "<h2>Question No.$q_num </h2><br>";
echo "$question <br>"; // print out actual question
present_question($item_id, $type);
$q_num ++;
}
present_question function
function present_question($ID, $type){
// grab all the answers attached to the question
$get_answers = mysql_query("SELECT * FROM answers_tb WHERE item_id='$ID'");
echo "<form>";
// switch for different structured questions. (switched by question type)
switch ($type)
{
case "1":
multi_choice($get_answers); // just working on this type at the moment
break;
case "2":
echo "this type hasnt been done yet";
break;
}
echo "</div>";
echo "</form>";
return;
}
function for multiple choice type
function multi_choice($get_answers){
while ($answers = mysql_fetch_assoc($get_answers))
{
$as = $answers['text_value'];
echo "<input type='radio' name='1' value='$as'>$as<hr />";
}
return;
}
hopefully you guys can help me.
I am a beginner, so I do apologise if my code is an abomination :D
thanks,
rough version (untested)
$i = 0; //declares the counter $i
//loop through every question present in query results and run function to present the different question structures
while ($qs = mysql_fetch_assoc($get_questions))
{
$type = $qs['item_type'];
$item_id = $qs['item_id'];
$question = $qs['question_text'];
$i++; // adds 1 to $i
echo "<div class='q_center' id='q_$q_num'>"; // insert each question into its own div
echo "<h2>Question No.$q_num </h2><br>";
echo "<div class=question" .$i. ">"; //appends $i to the div name creating question[$i]
echo "$question <br>"; // print out actual question
echo "</div>";
present_question($item_id, $type);
$q_num ++;
}
should make div's named question1, question2, etc

Categories