MySQL/PHP/JS Dynamically Populated Drop Down Menu - php

I'm working on a DB/web based frontend, and have encountered an issue. First off, I have a form with a drop down menu containing a list of contracts. Upon selection of a contract, I'd like for the jobs associated with that contract (fetched from the MySQL DB) to populate a second drop down menu below the first.
I would have just had all the info in one menu, but an 8000 entry drop down menu is a little unwieldy.
My PHP and HTML are barely passable, but enough for my purposes, however my ECMA experience is limited to a little bit of ActionScript in Flash MX, many moons ago.
I'd like to avoid using third party JS libraries (such as jQuery) if at all possible, and I don't mind writing more code. I just need to know whether this is doable, and a little shove in the right direction.
I'll shut up now, heres the form to fetch the contract ID (and the associated client), and the incomplete job menu.
<select name='idcontract' onchange=''>
<!--fetch/display contracts/clients-->
<?php
include 'sqldb.php';
$cntqres = mysqli_query($dbc, 'SELECT * FROM contract');
while ($cntrow = mysqli_fetch_array($cntqres))
{
$cliqres = mysqli_query($dbc, "SELECT * FROM client WHERE idclient = '$cntrow[idclient]'");
while ($clirow = mysqli_fetch_array($cliqres))
{
echo "<option value='$cntrow[idcontract]'>$cntrow[idcontract] $clirow[name]</option>";
}
}
?>
</select>
<select name='idjob'>
<option value='NULL'>Please select a contract</option>
<!--here goes the magical piece of code I don't know how to write-->
</select>
Any help would be much appreciated.
Edit:
Here's the PHP called by FeatherAJAX:
<?php
include 'sqldb.php';
$cnt = mysqli_real_escape_string($dbc, $_GET['cnt']);
$sql = "SELECT * FROM job WHERE idcontract='$cnt' ORDER BY job.idjob";
$jqres = mysqli_query($dbc, $sql);
$i = 1;
while (($jrow = mysqli_fetch_array($jqres)) && ($i < count($jrow)))
{
echo "idjob=><option value='$jrow[idjob]' id='$jrow[idjob]'>Job-$i $jrow[part_desc]</option>";
$i++;
}
?>

First off, you may want to rewrite the chunk of code that produces the contract options. Looping through query results and performing another query for each record is inefficient. Based on your queries, you might be able to use this code, which does a single query and then generate the options based on that. (I had to use made-up column names in the ORDER clause. In general, you should always sort your recordset so that results are in a determinate order -- even if you don't care what that order is.
<select name="idcontract" id="idcontract">
<!--fetch/display contracts/clients-->
<?php
include 'sqldb.php';
$clients = mysqli_query($dbc, '
SELECT ct.idcontract, ct.idclient, cl.name
FROM contract ct LEFT OUTER JOIN client cl ON ct.idclient = cl.idclient
ORDER BY ct.contractname, cl.clientname
');
while ($client = mysqli_fetch_array($clients)) {
echo "<option value=\"{$client[idcontract]}\">{$client[idcontract} {$client[name]}</option>";
}
?>
</select>
<select name="idjob" id="idjob">
<option value="NULL">Please select a contract</option>
</select>
To your question, the code you're looking for actually doesn't go where that comment is. What you need is an event handler that responds to the user picking an option in the first SELECT; it should then grab the value of that option and request from the server a set of key-value pairs to stuff into the second SELECT.
Something like this:
document.getElementById('idcontract').onchange = function(event) {
// grab currently selected value
var sValue = null;
for(var i = 0, imax = this.childNodes.length; i < imax; i++) {
var eOption = this.childNodes[i]; // shorthand
if(eOption.selected) {
sValue = eOption.value;
break;
}
}
if(!sValue) return;
// get the sub-options for this value
getSubOptions(sValue, function(XHR) {
// this code runs once the response comes back from the server
var aPairs = [];
var nlJobs = XHR.getElementsByTagName('jobs'); // assumptions #1 & #2: response is XML, includes <job> tag for each job
// extract key-value pairs from XML
for(var i = 0, imax = nlJobs.length; i < imax; i++) {
var xJob = nlJobs[i]; // shorthand
/*
assumption #3: <job> tag has "id" property
assumption #4: job name appears inside <job> tag
assumption #4.5: you've got an abstraction layer that normalizes XML node interfaces so that "text" and "textContent" are folded into "textContent"
*/
aPairs.push({ 'key': xJob.getAttribute('id'), 'value': xJob.textContent });
}
// given array of key-value pairs, rebuild select box
var eJobs = document.getElementById('idjob');
setOptions(eJobs, aPairs);
});
}
function setOptions(eNode, aPairs) {
if(!eNode || !eNode.nodeName || eNode.nodeName.toUpperCase() !== 'SELECT') return false;
// empty SELECT of all options
while(eNode.firstChild) {
eNode.removeChild(eNode.firstChild);
}
// build up new nodes
var eOpt = null;
for(var i = 0, imax = aPairs.length; i < imax; i++) {
eOpt = document.createElement('OPTION');
eOpt.value = aPairs[i].key;
eOpt.appendChild(document.createTextNode(aPairs[i].value));
eNode.appendChild(eOpt);
}
return true;
}
Of course, this is missing an important piece: you need some kind of AJAX abstraction layer. You don't need to get that from a framework, and a good library for this can be less than 50 lines of code (e.g. see PPK's ajax script on quirksmode.org), but you absolutely need something. That layer will provide two benefits: (1) cross-browser compatibility; (2) syntactic sugar.
For example, the code above doesn't include the definition of getSubOptions. That's because the logic will vary based on the interface provided by your AJAX abstraction. The idea, though, is that you'll perform a GET request against a script you write that accepts arguments and returns data satisfying that request. In the code above, I pretended that the script you write will return properly-formed XML data, with a MIME type identifying it as such. Alternatively, you could use JSON (or JSONP), straight text (e.g. CSV-style data), or even raw HTML that you'll just insert into the page.
The benefit of using a full framework is that they all provide convenient ways of doing DOM manipulation (i.e. syntactic sugar again).
The bottom line: you can absolutely do this with a homegrown approach (and I'm proud to say I've done it myself). But it will take longer -- not just because it's less convenient, but also because you'll have to re-invent the wheel === finding and fixing bugs in your code instead of leveraging well-tested core components from some library.
EDIT: If you want to use JSON as a data interchange format instead of XML, you'd modify the response handler being passed to getSubOptions like so:
getSubOptions(sValue, function(XHR) {
// this code runs once the response comes back from the server
var aPairs = eval(XHR.responseText); // assumes JSON defines an array of key-value pairs
// given array of key-value pairs, rebuild select box
var eJobs = document.getElementById('idjob');
setOptions(eJobs, aPairs);
});
And here's a sample of what that JSON might look like:
[ { key: '1234', value: 'Job #1' },
{ key: '2345', value: 'Job #2' },
...
];
In this example, the JSON structure conveniently mirrors the property names expected by setOptions; that said, key and value seem pretty inoffensive.
If you're set on using JSON for data, you may want to look into JSONP as a more secure alternative. It's real similar, but the design pattern is a little different from the anonymous callback technique above.
EDIT 2: Modified sample code for the responder:
<?php
include 'sqldb.php';
$cnt = mysqli_real_escape_string($dbc, $_GET['cnt']);
$sql = "SELECT * FROM job WHERE idcontract='$cnt' ORDER BY job.idjob";
$jqres = mysqli_query($dbc, $sql);
$i = 1;
// prepare the response
header('Content-Type: text/html');
while (($jrow = mysqli_fetch_array($jqres)) && ($i < count($jrow))) {
echo "<option value=\"$jrow[idjob]\" id=\"$jrow[idjob]\">Job-$i ${htmlentities(jrow[part_desc])}</option>";
$i++;
}
?>

Related

Best way to output datas after insert record on database with ajax / JSON / php

I'm working on a project and I would like know which is the best way for show the data after having insert a record on the database having an complex structure html between use the structure in php or jQuery and why?.
Example of scenario:
I'm building a system of posts and comments. When the user write the post and publish it an call ajax trigger the function php and it insert the information on the database. until here all ok, but then I have to display the message insert on the wall of the user, so which the best way for display that post insert?
There are so many different way, but which is the best keeping attention on speed, security, and compatibility?
some example, suggest are welcome:
<script> // default example of call ajax
$.ajax({
type: "POST",
url: 'http://localhost/ajax_ouput_post',
dataType: 'json',
data: { id_user : 1, title : "Hello everyone", content : "Good morning" },
success: function(html) {
// output under php example
},
error: function(){
alert('Error on ajax call');
}
});
</script>
1- I create the template for the output in php.
<?php
function ajax_output_post() {
$title = $_POST['title'];
$content = $_POST['content'];
$id_user = $_POST['id_user'];
// all the check for the input
$query = // insert data on mysql #return id_post;
$select_last_post_mysql = // select the last query from the db using id_user and id_post
foreach ($select_last_post_mysql as $post) {
$template = // html structure;
$template .= // continue html structure;
if ($post->photo == 1) {
$template .= // <div>photo user</div>
}
$template .= // ecc...
}
echo json_encode(array('template' => $template));
}
?>
jquery output
<script>
$(#wall).append(html.template);
</script>
php second example with output jQuery template
<?php
function ajax_output_post() {
$title = $_POST['title'];
$content = $_POST['content'];
$id_user = $_POST['id_user'];
// all the check for the input
$query = // insert data on mysql #return id_post;
$select_last_post_mysql = // select the last query from the db using id_user and id_post
foreach ($select_last_post_mysql as $post) {
$title_json = $post->title;
$content_json = $post->content;
if ($post->photo == 1) {
$photo_user_json = $post->photo_user;
} else {
$photo_user_json = "";
}
$id_post = $post->id_post;
}
echo json_encode(array('title' => $title_json, 'content' => $content_json, 'id_post' => $id_post));
}
?>
jquery
<script>
// in jquery
var Template = {
main_container: "<div class='post_container' data-post='" + html.id_post + "'>",
title: "<div class='title'>" + html.title + "</div>",
content: "<div class='content'>" + html.content + "</div>",
close_main: "</div>",
run: function() {
return Template.main_container + Template.content + Template.close_main;
}
};
$('#wall').append(Template.run());
</script>
Well, there is not really a 'best' way to do this, it always depends on the concrete situation.
In your case you could:
simply attach the user post to the DOM via javascript, without knowing whether it was inserted to the database or not (because you have all data of the post at client side available, you do not need to select it again)
attach the user post by javascript (like in the point above) after you know it was inserted (in the success handler, but still no data in response from php)
I would recommend not to select the inserted data again anyway, except you need an auto generated value like id or inserted-timestamp, instead you could simply return the values of the request in your response after the insert.
Hope this helps.
There are a few ways to accomplish this, and there are tradeoffs to be made for each of them.
The simplest way to accomplish this is to retain the user input in javascript as answered by Nico above. The pro for this method is it is very simple to accomplish and move on to other problems and due to this simplicity, has few moving parts to be misused. The major con for this method is that it means the frontend is making blind assumptions about what is going on when the backend logic processes the data, which presents the risk of misinforming the user about what happened if the backend silently fails, and this sort of bug is often difficult to catch until it bites someone and they complain. This is probably your best answer for simple use cases, but if you intend to scale it or introduce significant complexity this makes the likelihood of bugs high and should be replaced.
As per your given example, you seem to be on this frame of thought already. So to address each of your given concerns, first I would say that compatibility is best addressed by being generally unopinionated. This is appropriate for a library meant to be used for general consumption, but is not necessarily preferable for a private project or internal business logic, which require opinion to execute a specific desired result. The major caveat in terms of compatibility is that templating is handled by the frontend at least as often as the backend. It may in some cases be done in ReactJS or Angular, or it may be done on the backend by Twig, or any number of other things. If you want wide compatibility, then this should have some configuration for whether to pass response in raw format or accompanied by a template. In the case of private business logic or an app you are building with a specific purpose, the underlying point is to accomplish a specific result, and either using the existing templating structure of the system or picking one and sticking to it is preferable so you are focusing on the end goal and not getting distracted. But either way a github library author and a app developer would likely solve this same problem in completely different ways, and neither of them are wrong.
In terms of security, the typical concerns all apply. Individual approach is mostly arbitrary, provided you cover these bases if user input is being output, entered into template content, or stored in a database.
In terms of speed, the javascript DOM option is always going to be the fastest. However you can make it almost as fast depending how much tolerance for optimization you have. You could perhaps use client side storage to cache unmodified templates client side, and just use a hash of the template content as its identifying key so it automatically flushes when you change the template on the server. If you then send the key to the server and it matches, you don't need to serve the template in the response body because the client already has the correct one. If the template hash on the backend is different, you serve the new one, bust the storage cache for that template and replace the key and value with the new one. This will make the template body, which is almost certainly the longest part of the response, only need to be sent when changes are made. You would need to inject the values into the static template clientside to do it this way, and still obtain those from the server on each request. On the backend, you do not want to make a separate SELECT statement. You want to store the values in an array and just return those if your INSERT query is successful, like maybe something like this:
<?php
// List of keys that MUST be present for the query to succeed. Don't bother calling the db if any of these did not come in the request.
$expected_keys = [
'id_user',
'title',
'content' // add whatever else has to be passed
];
// key/val for db insert
$vals = [];
try {
// verify expected keys are provided
foreach ( $expected_keys as $k => $expected ) {
if !(array_key_exists ( $expected, $_POST ) ) {
// not a valid post, return an error
throw new \InvalidArgumentException( sprintf( 'Expected field [%1$s] was not provided. ', $expected ) );
}
// retain the field in $vals for insert
$vals[ $expected ] = $_POST[$expected];
}
$dsn = "mysql:host=localhost;dbname=myDatabase;charset=utf8mb4";
$options = [
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
];
$pdo = new \PDO($dsn, "username", "password", $options);
$stmt = $pdo->prepare(
'INSERT INTO `myTable` ( `:' .
implode( '`, `:', array_keys( $vals ) ) .
'` ) VALUES (' .
implode( '", ":', $vals ) .
');'
);
$stmt->execute( $vals );
$stmt = null;
}
// User error
catch \InvalidArgumentException $e {
// return 400 Bad Request and $e->getMessage() as the error message
}
// Server error
catch \Exception $e {
// log or whatever and return a 500 error
}
// return success with whatever you need to send back from $vals or $_POST

retrieve json formatted data using xmlhttp request

I was using jQuery and this was done easily but I want to change it to regular javascript because I will be using it with phonegap and I don't want to loop through js frameworks every time I make a request to the server. Maybe it's a bad reason to go away from jQuery but seems like it would make everything faster. So I need some help with this code:
<body onload="init();">
<div id="daily"></div>
<script>
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState === 4 && xmlhttp.status === 200){
var a = JSON.parse(xmlhttp.responseText);
document.getElementById('daily').innerHTML = a.items;
}
};
xmlhttp.open("GET", serviceURL +"getmainnews.php" , true);
xmlhttp.send();
}
</script>
getmainnews.php
//mysqli query
$daily = array();
while ($r = mysql_fetch_assoc($day)){
$daily[] = $r;
}
echo json_encode(array("items" => $daily, "allitems" => $mainnews,...));
In the Chrome DT, I can see the encoded data which is returned but I can't display the first item into my first div with id="daily". With the code I provided, all I get is [object, Object]. What am I doing wrong?
EDIT:
So my query selects the entire row from my database and it's in an array:
{"items":[{"id":"1","pic":"","topic":"daily","article":" \t\t\t\t\t \t\t\t\t\t\u0417...","link":"http://www....","text_cont":..."}]}
How can I display just the pic and the article without all the other junk? Do I have to modify my php file?
EDIT 2:
$day = mysql_query("SELECT pic, article, link FROM table WHERE topic='daily' ORDER BY id DESC LIMIT 1");
$daily = array();
while ($r = mysql_fetch_assoc($day)){
$daily[] = $r;
}
$exc = mysql_query("SELECT pic, article, link FROM table WHERE topic='exclusive' ORDER BY id DESC LIMIT 1");
$excl = array();
while ($e = mysql_fetch_assoc($exc)){
$excl[] = $e;
}
$mus = mysql_query("SELECT pic, article, link FROM table WHERE topic='must' ORDER BY id DESC LIMIT 1");
$must = array();
while ($m = mysql_fetch_assoc($mus)){
$must[] = $m;
}
echo json_encode(array("items" => $daily, "exc" => $excl, "must" => $must));
That's my full php file with the queries. Items, exc, and must are arrays, so the responseText, I guess, is a multidimensional array? Is that the problem I'm having with displaying it?
EDIT 3:
console.log(a.items) gives me an "undefined" so I logged the xmlhttp.responseText.
Without seeing exactly what your JSON looks like it is hard to know for sure. However since you are encoding an array, I suspect changing:
a.items
to
a[0].items
will do the trick...
Edit
I would do:
document.getElementById('daily').innerHTML = JSON.stringify(a.items);
That will convert your object into a string that can be assigned to the innerHTML of an element. I am not sure if this is exactly what you want to do, but when you find the element you want to display using that method should work.
Edit 2
You could change your PHP to only output what you want. However, you could filter it in your JS as well:
a.items[0].pic
and
a.items[0].article
If those don't display as is you can use the stringify method, but you shouldn't need to since those items appear to be strings.
Also note, if you have multiple items you will need to change the 0 to the index of the item you are targeting.

mysql php select random with javascript rotator

I just started learning PHP, and have been trying to build a website to learn. I found a javascript script that rotates text on the internet here which looks like:
<script language="JavaScript">
function rotateEvery(sec)
{
var Quotation=new Array()
// QUOTATIONS
Quotation[0] = 'First quotation';
Quotation[1] = 'Second quotation';
Quotation[2] = 'Third quotation';
Quotation[3] = 'Fourth quotation';
Quotation[4] = 'Fifth quotation';
Quotation[5] = 'Sixth quotation';
Quotation[6] = 'You can add <b>as many</b> quotations <b>as you like</b>';
var which = Math.round(Math.random()*(Quotation.length - 1));
document.getElementById('textrotator').innerHTML = Quotation[which];
setTimeout('rotateEvery('+sec+')', sec*1000);
}
</script>
I also have a database table called events that has three fields ( id, when, tag) When is a date, tag is the description of the event (e.g Christmas Party/Halloween at my house).
What i am trying to do is select the events that are happening today and put them in my javascript rotator, randomly.
Is this possible? How would I go about implementing this? I know I am really bad at explaining my questions so if I left out any more details if you could just tell me and I can help.Thanks!
So if I understand your intent, you want to pull events from your database and pass them into the JavaScript on your page to use in your rotator:
In your PHP
Use whichever MySQL API you are using already to execute the query. Using the old mysql_*() functions would look like the following. (Note: use of the mysql_*() functions is actually NOT recommended, but it seems most likely that's what you're currently using. I'll update if I find out otherwise...)
// Assuming `when` is a real DATE or DATETIME data type in MySQL...
// compare to CURDATE() to get today's
$result = mysql_query("SELECT tag FROM events WHERE DATE(when)=CURDATE()");
if ($result) {
// array to hold all the output
$events = array();
while ($row = mysql_fetch_assoc($result)) {
// Add the event to your array
$events[] = $row['tag'];
}
// After building the array, encode it as JSON
// Later you'll echo this into your JavaScript in place of the array...
$events = json_encode($events);
}
Later in your HTML/JavaScript output
Output the JSON string (your array) into the JavaScript on your page:
function rotateEvery(sec)
{
// The JSON from PHP output here
// Would look something like
// ["Event 1","Event 2","Event 3"]
var Quotations = <?php echo $events; ?>;
var which = Math.round(Math.random()*(Quotation.length - 1));
document.getElementById('textrotator').innerHTML = Quotation[which];
setTimeout('rotateEvery('+sec+')', sec*1000);
}

AJAX enabled web form

I am new to Jquery and want some help from you guys.
i want to decode json data in jquery as i am able to pass data from php to ajax but after it came back in jquery it is not parsing it says undefined. the code is bellow
JavaSript file
$.post("GetData.php", function(data) {
if(data==false)
var tpl = '<p>no record found<p>'
else
var tpl = DrawTableRowsforSection(data);
$("#result").append($(tpl));
},"json");
function DrawTableRowsforSection(p)
{
alert(p.id[0]);
var o = '<table>';
for (var i = 0; i < p.length; i++)
alert(p.id[i]);
o += '<tr><td>'+p.id[i]+'</td><td>'+p.section_name[i]+'<td></tr>';
o+='</table>'
return O;
}
PHP Script
header('Content-Type: application/json');
mysql_connect('localhost','root','') ;
mysql_select_db('news');
session_start();
$query = 'select id,section_name from section';
if ($result = mysql_query($query)) {
if (!mysql_num_rows($result)==null) {
$myArray = array();
while ($row = mysql_fetch_assoc($result)) {
$id = ToSring($row['id']);
$myArray[] = $row;
}
echo json_encode($myArray);
}
}
The database has a table named section
fields are as follows
id int(11)
section_name varchar(20)
There are total 5 records there.
What I want is to populate a table using returned data.
Can any one guide me where I am making mistake
Regards
Kashif Afzaal
Be sure that mysql returns results and you can take them through ajax.
Also, I've seen the following mistake:
You are using the variable tpl wrong. It is just a js variable, no need to use $ .
Use this way:
$("#result").append(tpl);
OK, from the top in the PHP script:
The MySQL extension is ancient and decrepit. Use MySQLi or PDO.
You never handle a failed MySQL query, so the response will quite possibly be empty. In 99% of cases, any if block should have a corresponding else block.
The result of mysql_num_rows() is never null. Do if (mysql_num_rows($result) > 0), or better yet, just if (mysql_num_rows($result)).
Never do if (!something == something) - it rarely does what you want it to. Do if (something != something)
You never handle mysql_num_rows() failing/giving no rows, so (again) the response will quite possibly be empty.
You never do anything with the $id variable you create.
I'm pretty sure ToSring should read ToString. But there's actually no such function in PHP - you would have either define it or invoke it as a method of a class/object. Indeed, this is likely to be the problem, as it will result in a fatal error.

Suggestive Search From JS Array, Possible?

Ok so what i want to be able to do is to perform a suggestive style search using the contents of an array instead of a doing a mySQL database query on every keyup.
So imagine a javascript object or array that is full of people's names:
var array = (jack,tom,john,sarah,barry,...etc);
I want to then query the contents of this array based on what the user has typed into the input box so far. So if they have typed 'j',both jack and john will be pulled out of the array.
I know this is possible via php mysql and ajax calls, but for reason of optimization I would like to be able to query the js array instead.
Hope someone can help me with this!
W.
as the name suggests, this finds elements of an array starting with the given string s.
Array.prototype.findElementsStartingWith = function(s) {
var r = [];
for(var i = 0; i < this.length; i++)
if(this[i].toString().indexOf(s) === 0)
r.push(this[i]);
return r;
}
// example
a = ["foo", "bar", "fooba", "quu", "foooba"];
console.log(a.findElementsStartingWith("fo"))
the rest is basically the same as in ajax-based scripts.
http://wwwo.google.com?q=autosuggest+using+javascript
AJAX calls fetch the contents from another serverside script files. You already have your data in the JS. Read a AJAX tutorial doing this. Then, just remove the parts where AJAX calls are made and replace it with your array's contents, and you're good to go.
I ended up using the following function to build my AJAX free instant search bar:
Example JS object being searched:
var $members = {
"123":{firstname:"wilson", lastname:"page", email:"wilpage#blueyonder.co.uk"},
"124":{firstname:"jamie", lastname:"wright", email:"jamie#blueyonder.co.uk"}
}
Example of function to search JS object:
$membersTab.find('.searchWrap input').keyup(function(){
var $term = $(this).val(),
$faces = $membersTab.find('.member'),
$matches = [];
if($term.length > 0){
$faces.hide();
$.each($members,function(uID,details){
$.each(details,function(detail,value){
if(value.indexOf($term) === 0){//if string matches term and starts at the first character
$faces.filter('[uID='+uID+']').show();
}
});
});
}else{
$faces.show();
}
});
It shows and hides users in a list if they partially match the entered search term.
Hope this helps someone out as I was clueless as to how to do this at first!
W.

Categories