-
Notifications
You must be signed in to change notification settings - Fork 1
phpMySQL
In early versions of these plugins, if there are data that need to be stored outside of the javascript code, we use an external database in mySQL. That requires an intermediary server-side language; we use php. In the future, these might be migrated to Firebase, but that was too hard for Tim.
The basic pattern for implementation is this:
-
foo.phpConnect.jsorfoo.DBconnect.jscontains js routines that you call in order to send or receive data from the database. This file preparesRequests and sends them using asendCommand()method, which usesfetch()(the Fetch API) to connect to php. -
foo.phpis accessed by thatfetch(); it contains code that assembles queries and runs them, and returns the results of the queries (if any) to the javascript caller (foo.DBconnect.sendCommand()).
This communication is tricky and has changed over the years. We hope we're using the most modern versions.
- For js-to-php, we use the Fetch API, and some associated ideas such as the
FormDataandRequestclasses. So basically, we trick teh system into thinking that we are submitting a form and receiving the results. - For php-to-mySQL, we use PHP Data Objects (PDO), which have replaced the now-deprecated calls that look like
mysql_query()and stuff like that. In thePDOmodel, you use the database credentials to create a database handle (which we typically name$DBH) that is your key to all subsequent interactions with mySQL.
We're using the Fetch API in javascript. Let's see how that looks on the javascript end of this communication, and then see what that looks like when we're in php.
Let's see how to use the Fetch API. Here are two routines from acs.DBconnect.js for your perusal:
sendCommand: async function (iCommands) {
const theCommand = iCommands.c;
let theBody = new FormData();
for (let key in iCommands) {
if (iCommands.hasOwnProperty(key)) {
theBody.append(key, iCommands[key])
}
}
// here is where the JS tells the PHP which server we're on.
theBody.append("whence", fish.whence);
let theRequest = new Request(
fish.constants.kBaseURL[fish.whence],
{method: 'POST', body: theBody, headers: new Headers()}
);
try {
const theResult = await fetch(theRequest); // here (finally) is the fetch!
if (theResult.ok) {
const theJSON = await theResult.json();
return theJSON;
} else {
console.error("sendCommand error: " + theResult.statusText);
}
}
catch (msg) {
console.log('fetch sequence error: ' + msg);
}
},
getCasesFromDB : async function(iAtts) {
const tSampleSize = document.getElementById("sampleSizeInput").value;
let tAttNames = [];
// iAtts is an array, we need a comma-separated string:
iAtts.forEach( a => tAttNames.push("`" + a.name + "`" ));
try {
const theCommands = {
"c": "getCases",
"atts": "," + tAttNames.join(','),
"n" : tSampleSize
};
const iData = await acs.DBconnect.sendCommand(theCommands);
return iData;
}
catch (msg) {
console.log('getCasesFromDB() error: ' + msg);
}
},
In this example, suppose some other routine needs cases from the DB. It calls await acs.DBconenctor.getCasesFromDB(). This is the bottom routine in the example. That function constructs the commands that php will need (they will be $_REQUEST variables on the inside) in the object theCommands. Then it asks sendCommand to send them.
sendCommand, for its part, does a little dance. It:
- translates the commands object into a
FormDatacalledtheBody; - adds an extra command,
whence, which tells us what system we're on (e.g.,"local"); - creates a
Requestobject that includes the commands as well as the URL for the php file (which depends onwhenceas well); - finally performs the
fetch(), awaits its completion (it's aPromise), and returnstheResult; - extracts the JSON version of
theResultand returns that JSON.
What happens when this fetch(theRequest) call hits the php?
The basics of the communication is that fetch() specifies the URL of the foo.php file. The server runs the file; at the end, an echo statement is the text that gets returned.
Remember the javascript object theCommands? We have access to that in php; the object gets delivered as an associative array called $_REQUEST. So you will see something like this (from acs.php):
$DBH = CODAP_MySQL_connect("localhost", $user, $pass, $dbname); // get that handle!
// this is the overall command, the only required part of the POST:
$command = $_REQUEST["c"];
switch ($command) {
case 'getCases':
// code to assemble query comes here, see below...
break;
case 'getAllAttributeInfo':
// etc...
So the js theCommand = { c: "getCases" ... } has yielded $command = "getCases" in php. We then use that command in a switch; there is a different section in the code for each command, switched by the theCommand value.
In PDO, there are two main phases: establishing credentials; and then actually constructing queries and executing them.
This issue has its own page: see Configuration.
A correct configuration specifies the various URLs needed as well as the values of the mySQL credentials such as $user, $pass, and $dbname. It also makes a connection to mySQL and defines the vital database handle, $DBH.
Now let's look again at that switch structure:
$DBH = CODAP_MySQL_connect("localhost", $user, $pass, $dbname);
// this is the overall command, the only required part of the POST
$command = $_REQUEST["c"];
switch ($command) {
case 'getCases':
$params = array();
$params["n"] = $_REQUEST["n"];
$theVariables = $_REQUEST['atts']; // includes opening comma
$query = "SELECT " . $theVariables . " FROM peeps ORDER BY RAND( ) LIMIT :n";
$out = CODAP_MySQL_getQueryResult($DBH, $query, $params);
break;
case 'getAllAttributeInfo':
// etc...
As you see, our output $out is returned (synchronously) by CODAP_MySQL_getQueryResult(), which has three arguments:
- the database handle
$DBH - the query, a string that has parameters introduced by colons
- the parameters, which is an associative array whose keys, preceded by colons, can go into the query, where the values will get substituted for them.
In our example, the sample size (which was originally a field in
theCommandsin js) becomes:nand installed in aLIMITclause in the mySQL query.
The function CODAP_MySQL_getQueryResult() is defined in ../common/TE_DBCommon.php, which was included back up at the top.
Then, that variable $out gets converted to JSON, the echo-ed at the end of the php file:
switch ($command) {
case 'getCases':
// stuff...
$out = something;
break;
case 'getDecoderInfo':
// stuff...
$out = something else;
break;
}
$jout = json_encode($out);
echo $jout;
and that (if you follow it back and back) becomes iData in javascript, returned in getCasesFromDB(), way up near the top of this page.