This question already has answers here:
PHP: Chaining Method Calls [duplicate]
(2 answers)
PHP method chaining or fluent interface?
(10 answers)
Closed 26 days ago.
I have a couple of functions in the same class that extends PDO.
One of them is sort of a shorthand function to make executing update sql queries a bit faster to write.
In practice i can write this:
$db->u('table_name SET column WHERE column',[$_POST['newValue'],$_POST['id']]);
and it would turn it into this:
/**
* "u" for UPDATE
*
* #param string $sql mysql query string
* #param string|array $V value(s) to be executed
* #param int $l set "LIMIT", defaults to "LIMIT 1"
* #param string|null $chk output a preview of the query with values (does not execute the query).
* #return int affected rows if success, error message if failed
*
*/
$u = $db->prepare("UPDATE table_name SET column = ? WHERE column = ? LIMIT 1");
$u->execute(["newValue", "99"]);
$rows = $u->rowCount(); // this is returned if everything was ok
Here's the part with this function:
class KD_DB extends PDO {
protected static $db = ''; // database connection object is made available through this variable with all the available PDO methods etc.
public function u($sql,$V=[],$l=1,$chk=null){
# HANDLE PARAMETERS
$chk_arr = [debug,check];
if(in_array($V,$chk_arr)){ $chk = $V; $V=[]; } // allow $chk as 2. parameter, and reset $V
if(in_array($l,$chk_arr)){ $chk = $l; $l=1; } // allow $chk as 3. parameter, and reset $l
//
# SPLIT SQL-STRING
$sql = str_replace('UPDATE ','',trim($sql)); // remove "UPDATE "
list($T,$sql)=explode(' ',$sql,2); // sepparate tablename ($T) from sql-string
list($F,$W) = explode(' WHERE ',$sql); // split sql-string into FIELD-list ($F) and WHERE-list ($W)
//
# BUILD NEW PARTS
$Fstr = str_replace(['SET ',','],['',' = ?, '],$F).' = ?'; // create new sql field-string
$Wstr = str_replace(',',' = ? AND ',$W).' = ?'; // create new WHERE-str
//
# CREATE NEW SQL STRING
$sql = "UPDATE $T SET $Fstr WHERE $Wstr ".($l==0?'':"LIMIT $l");
//
# DEBUG
$chk_str = '<code><big style="letter-spacing:.1em; line-height:1.5em;">'.__METHOD__.($chk?'(<i style="color:red;"><b>'.ltrim($chk,'kd_').'</b>: no data executed</i>)':'').'<br> prepare->("'.$sql.'");<br> ->execute(["'.(is_array($V)?implode('", "',$V):$V).'"]);<br> return ->rowCount();</big>'.KD::backtrace().'</code>';
if($chk===check){ return print($chk_str); }
if($chk===debug){ die($chk_str); }
//
# EXECUTE QUERY
try{
$q = $this->prepare($sql);
$q->execute(!is_array($V)?[$V]:$V);
return $q->rowCount();
}catch(PDOException $e){
return $e->getMessage();
}
//
}
}
The other function is a shorthand for if(){...}else{} that utilizes a couple of other functions to handle response messages, and an optional redirect at the end:
public function verify($dbh,$yep='',$nope='',$redirect=''){
# HANDLE PARAMETERS
// ... just code to allow using $nope and $redirect as 2. and 3. parmameters
//
if(is_int($dbh)&&$dbh>0){
if(defined('is_ajax')){ echo json_encode(['type'=>'success','msg'=>($yep?:'Vellykket'),'data'=>$dbh]); exit; }
KD::setSuccess($yep?:'Default success message if none is provided');
} else {
if(defined('is_ajax')){ echo json_encode(['type'=>'error','msg'=>($nope?:'OBS! Noe gikk galt'),'data'=>$dbh]); exit; }
KD::setError($nope?:'OBS! Default error message if none is provided');
}
if($redirect){
KD::redirect($redirect);
}
}
This is how I would use them in practice:
$upd = $db->u('table_name SET column WHERE column',[$_POST['newValue'],$_POST['id']]);
$db->verify($upd);
And that's OK.
But I would really like to do one of the following:
$upd = $db->u('table_name SET column WHERE column',[$_POST['newValue'],$_POST['id']])->verify();
$upd = $db->u('table_name SET column WHERE column',[$_POST['newValue'],$_POST['id']]);
$upd->verify()
I think this would be the most clean and shortest code to write:
$upd = $db->u('table_name SET column WHERE column',[$_POST['newValue'],$_POST['id']])->verify('Custom Success message','Custom error message',redirect);
So I suppose the question is "How can a chain the verify()-function to u()-function?"
Related
I'm working for a company that uses this Mysqli php class to do mysql calls.
The problem is, the previous programmer wasn't great about preventing unbounded queries. So there are things dispersed throughout the code like the following:
$db -> where('id',$_POST['id']);
$db -> delete('table');
This code is supposed to only delete one record where id = $_POST['id']. However, if $_POST['id'] is empty, we've got problems. It then deletes the entire table. One solution to this problem would be to find all the places in the code where delete or update functions are called and then make sure that the where variable is actually set.
if(isset($_POST['id']) && $_POST['id']!=''){
$db -> where('id',$_POST['id']);
$db -> delete('table');
}
But, that would take a lot of work because I know there are about 200 instances in the code. I'm hoping there might be a way to alter the following 2 functions to prevent them from executing unbound queries in the first place. Any help is appreciated!!
/**
* Update query. Be sure to first call the "where" method.
*
* #param string $tableName The name of the database table to work with.
* #param array $tableData Array of data to update the desired row.
*
* #return boolean
*/
public function update($tableName, $tableData)
{
if ($this->isSubQuery)
return;
$this->_query = "UPDATE " . self::$_prefix . $tableName ." SET ";
$stmt = $this->_buildQuery (null, $tableData);
$status = $stmt->execute();
$this->reset();
$this->_stmtError = $stmt->error;
$this->count = $stmt->affected_rows;
return $status;
}
/**
* Delete query. Call the "where" method first.
*
* #param string $tableName The name of the database table to work with.
* #param integer $numRows The number of rows to delete.
*
* #return boolean Indicates success. 0 or 1.
*/
public function delete($tableName, $numRows = null)
{
if ($this->isSubQuery)
return;
$this->_query = "DELETE FROM " . self::$_prefix . $tableName;
$stmt = $this->_buildQuery($numRows);
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return ($stmt->affected_rows > 0);
}
public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND')
{
// forkaround for an old operation api
if (is_array($whereValue) && ($key = key($whereValue)) != "0") {
$operator = $key;
$whereValue = $whereValue[$key];
}
if (count($this->_where) == 0) {
$cond = '';
}
$this->_where[] = array($cond, $whereProp, $operator, $whereValue);
return $this;
}
You should catch the bad value when it's being passed to the where function, not later. That way it's easier to follow stack traces.
public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND')
{
if (is_null($whereValue) || trim($whereValue) == '') {
throw new Exception('Cannot pass null or empty string as a condition to MysqliDb::where')
}
// ...
}
You could check through the _where protected property array inside of the delete function too, but it's not good practice to silently fail a method by doing a simple return out of the function. If you insist, though:
public function delete($tableName, $numRows = null)
{
foreach ($this->_where as $w) {
if (is_null($w[3]) || trim($w[3]) == '') {
return;
// or alternatively throw new Exception('...')
}
}
// ...
}
What about returning if this (the where clause) is empty?
https://github.com/joshcam/PHP-MySQLi-Database-Class/blob/b3754d20bcebf07d65d552b2257696539a1cb144/MysqliDb.php#L1074
if (count($this->_where) == 0) {
return;
}
please update to a latest stable version. this issue is fixed there.
where('val', unset) wont count as no condition.
Right now, I'd wrote a function in my model as:
public function getRowsByZipCode($zip)
{
// SQL to get all the rows with the given zip code
$stmt = $this -> getAdapter()
-> query( "SELECT *
FROM
table_name
WHERE
table_name.status = 1 AND
table_name.zip={$zip}");
$resultRows = $stmt->fetchAll();
// -------------------------------------------------------- //
// Convert result set to an array of objects
$resultObjects = array();
// If there is atleast one row found in DB
if(count($resultRows) > 0)
{
// Loop throguh all the rows in the resultset
foreach($resultRows as $resultRow) {
// Create table row and fill it with the details got from DB
$h = $this->createRow();
$h->setFromArray($resultRow);
// Add to the array
$resultObjects[] = $h;
}
}
return $resultObjects;
// -------------------------------------------------------- //
}
Which is working perfectly as I needed. And it is returning me an array that contains the tables row objects(App_Model_TableName Objects), which will be used later for further operations like save and delete etc.
What I really want is to remove the code that loop through the rows I got from the result set and converting each row to an object of App_Model_TableName that I'd wrote inside the comments // --- //.
Thanks in advance.
Firstly, I am assuming you are using PDO.
Try the following
class App_Model_TableName
{
public $status;
public $zip;
// public $other_column;
}
class YourClass
{
protected function getAdapter()
{
// Do adapter stuffs
}
public function query($query, array $param)
{
// When Using PDO always use prepare and execute when you pass in a variable
// This will help prevent SQL injection
$stmt = $this->getAdapter()->prepare($query);
return $query->execute($param);
}
/**
* #return App_Model_TableName[]
*/
public function getRowsByZipCode($zip)
{
// SQL to get all the rows with the given zip code
// This way will help prevent SQL injection
$query = "SELECT * FROM table_name WHERE table_name.status = 1 AND table_name.zip = :zip";
$qData = array(':zip' => $zip);
$results = $this->query($query, $qData);
return $results->fetchAll(PDO::FETCH_CLASS, 'App_Model_TableName');
}
}
Calling YourClass::getRowsByZipCode() will then return you an array of App_Model_TableName objects. You can then access them like:
$data = $instance_of_yourclass->getRowsByZipCode(12345);
foreach ($data as $row)
{
echo $row->zip;
echo $row->do_stuff();
}
All these awesome functions I found on:
http://php.net/manual/en/pdostatement.fetchall.php
http://php.net/manual/en/pdostatement.execute.php
Disclaimer: this code was not tested :(
Be cool but stay warm
Finally, I had found the solution:
public function getRowsByZipCode($zip)
{
// SQL to get all the rows with the given zip code
$stmt = $this -> getAdapter()
-> query( "SELECT *
FROM
table_name
WHERE
table_name.status = 1 AND
table_name.zip={$zip}");
$resultObjects= array();
while($data = $stmt->fetch())
{
$h = $this->createRow();
$h->setFromArray($data);
// Add to array
$resultObjects[] = $h;;
}
return $resultObjects;
}
I had removed the code that do the fetchAll() and loop through each row in the resultset. Now, I am taking each row from the resultset and creating an row of App_Model_TableName object using the data we got from the resultset.
Working perfectly for me.
May wabsite has faced max_user_connections error
After contacting the website hosting provider they said I have to close the connection immediately after accessing the database using mysql_close()
My question is that how I can close the connection if my php file is :
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
/**
* Indicates to the Q2A database layer that database connections are permitted fro this point forwards
* (before this point, some plugins may not have had a chance to override some database access functions).
*/
function qa_db_allow_connect()
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_allow_connect;
$qa_db_allow_connect=true;
}
/**
* Connect to the Q2A database, select the right database, optionally install the $failhandler (and call it if necessary).
* Uses mysqli as of Q2A 1.7.
*/
function qa_db_connect($failhandler=null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_connection, $qa_db_fail_handler, $qa_db_allow_connect;
if (!$qa_db_allow_connect)
qa_fatal_error('It appears that a plugin is trying to access the database, but this is not allowed until Q2A initialization is complete.');
if (isset($failhandler))
$qa_db_fail_handler = $failhandler; // set this even if connection already opened
if ($qa_db_connection instanceof mysqli)
return;
// in mysqli we connect and select database in constructor
if (QA_PERSISTENT_CONN_DB)
$db = new mysqli('p:'.QA_FINAL_MYSQL_HOSTNAME, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE);
else
$db = new mysqli(QA_FINAL_MYSQL_HOSTNAME, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE);
// must use procedural `mysqli_connect_error` here prior to 5.2.9
$conn_error = mysqli_connect_error();
if ($conn_error)
qa_db_fail_error('connect', $db->connect_errno, $conn_error);
// From Q2A 1.5, we explicitly set the character encoding of the MySQL connection, instead of using lots of "SELECT BINARY col"-style queries.
// Testing showed that overhead is minimal, so this seems worth trading off against the benefit of more straightforward queries, especially
// for plugin developers.
if (!$db->set_charset('utf8'))
qa_db_fail_error('set_charset', $db->errno, $db->error);
qa_report_process_stage('db_connected');
$qa_db_connection=$db;
}
/**
* If a DB error occurs, call the installed fail handler (if any) otherwise report error and exit immediately.
*/
function qa_db_fail_error($type, $errno=null, $error=null, $query=null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_fail_handler;
#error_log('PHP Question2Answer MySQL '.$type.' error '.$errno.': '.$error.(isset($query) ? (' - Query: '.$query) : ''));
if (function_exists($qa_db_fail_handler))
$qa_db_fail_handler($type, $errno, $error, $query);
else {
echo '<hr><font color="red">Database '.htmlspecialchars($type.' error '.$errno).'<p>'.nl2br(htmlspecialchars($error."\n\n".$query));
qa_exit('error');
}
}
/**
* Return the current connection to the Q2A database, connecting if necessary and $connect is true.
*/
function qa_db_connection($connect=true)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_connection;
if ($connect && !($qa_db_connection instanceof mysqli)) {
qa_db_connect();
if (!($qa_db_connection instanceof mysqli))
qa_fatal_error('Failed to connect to database');
}
return $qa_db_connection;
}
/**
* Disconnect from the Q2A database.
*/
function qa_db_disconnect()
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_db_connection;
if ($qa_db_connection instanceof mysqli) {
qa_report_process_stage('db_disconnect');
if (!QA_PERSISTENT_CONN_DB) {
if (!$qa_db_connection->close())
qa_fatal_error('Database disconnect failed');
}
$qa_db_connection=null;
}
}
/**
* Run the raw $query, call the global failure handler if necessary, otherwise return the result resource.
* If appropriate, also track the resources used by database queries, and the queries themselves, for performance debugging.
*/
function qa_db_query_raw($query)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (QA_DEBUG_PERFORMANCE) {
global $qa_usage;
// time the query
$oldtime = array_sum(explode(' ', microtime()));
$result = qa_db_query_execute($query);
$usedtime = array_sum(explode(' ', microtime())) - $oldtime;
// fetch counts
$gotrows = $gotcolumns = null;
if ($result instanceof mysqli_result) {
$gotrows = $result->num_rows;
$gotcolumns = $result->field_count;
}
$qa_usage->logDatabaseQuery($query, $usedtime, $gotrows, $gotcolumns);
}
else
$result = qa_db_query_execute($query);
// #error_log('Question2Answer MySQL query: '.$query);
if ($result === false) {
$db = qa_db_connection();
qa_db_fail_error('query', $db->errno, $db->error, $query);
}
return $result;
}
/**
* Lower-level function to execute a query, which automatically retries if there is a MySQL deadlock error.
*/
function qa_db_query_execute($query)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$db = qa_db_connection();
for ($attempt = 0; $attempt < 100; $attempt++) {
$result = $db->query($query);
if ($result === false && $db->errno == 1213)
usleep(10000); // deal with InnoDB deadlock errors by waiting 0.01s then retrying
else
break;
}
return $result;
}
/**
* Return $string escaped for use in queries to the Q2A database (to which a connection must have been made).
*/
function qa_db_escape_string($string)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$db = qa_db_connection();
return $db->real_escape_string($string);
}
/**
* Return $argument escaped for MySQL. Add quotes around it if $alwaysquote is true or it's not numeric.
* If $argument is an array, return a comma-separated list of escaped elements, with or without $arraybrackets.
*/
function qa_db_argument_to_mysql($argument, $alwaysquote, $arraybrackets=false)
{
if (is_array($argument)) {
$parts=array();
foreach ($argument as $subargument)
$parts[] = qa_db_argument_to_mysql($subargument, $alwaysquote, true);
if ($arraybrackets)
$result = '('.implode(',', $parts).')';
else
$result = implode(',', $parts);
}
elseif (isset($argument)) {
if ($alwaysquote || !is_numeric($argument))
$result = "'".qa_db_escape_string($argument)."'";
else
$result = qa_db_escape_string($argument);
}
else
$result = 'NULL';
return $result;
}
/**
* Return the full name (with prefix) of database table $rawname, usually if it used after a ^ symbol.
*/
function qa_db_add_table_prefix($rawname)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$prefix = QA_MYSQL_TABLE_PREFIX;
if (defined('QA_MYSQL_USERS_PREFIX')) {
switch (strtolower($rawname)) {
case 'users':
case 'userlogins':
case 'userprofile':
case 'userfields':
case 'messages':
case 'cookies':
case 'blobs':
case 'cache':
case 'userlogins_ibfk_1': // also special cases for constraint names
case 'userprofile_ibfk_1':
$prefix = QA_MYSQL_USERS_PREFIX;
break;
}
}
return $prefix.$rawname;
}
/**
* Callback function to add table prefixes, as used in qa_db_apply_sub().
*/
function qa_db_prefix_callback($matches)
{
return qa_db_add_table_prefix($matches[1]);
}
/**
* Substitute ^, $ and # symbols in $query. ^ symbols are replaced with the table prefix set in qa-config.php.
* $ and # symbols are replaced in order by the corresponding element in $arguments (if the element is an array,
* it is converted recursively into comma-separated list). Each element in $arguments is escaped.
* $ is replaced by the argument in quotes (even if it's a number), # only adds quotes if the argument is non-numeric.
* It's important to use $ when matching a textual column since MySQL won't use indexes to compare text against numbers.
*/
function qa_db_apply_sub($query, $arguments)
{
$query = preg_replace_callback('/\^([A-Za-z_0-9]+)/', 'qa_db_prefix_callback', $query);
if (!is_array($arguments))
return $query;
$countargs = count($arguments);
$offset = 0;
for ($argument = 0; $argument < $countargs; $argument++) {
$stringpos = strpos($query, '$', $offset);
$numberpos = strpos($query, '#', $offset);
if ($stringpos === false || ($numberpos !== false && $numberpos < $stringpos)) {
$alwaysquote = false;
$position = $numberpos;
}
else {
$alwaysquote = true;
$position = $stringpos;
}
if (!is_numeric($position))
qa_fatal_error('Insufficient parameters in query: '.$query);
$value = qa_db_argument_to_mysql($arguments[$argument], $alwaysquote);
$query = substr_replace($query, $value, $position, 1);
$offset = $position + strlen($value); // allows inserting strings which contain #/$ character
}
return $query;
}
/**
* Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
*/
function qa_db_query_sub($query) // arguments for substitution retrieved using func_get_args()
{
$funcargs=func_get_args();
return qa_db_query_raw(qa_db_apply_sub($query, array_slice($funcargs, 1)));
}
/**
* Return the number of rows in $result. (Simple wrapper for mysqli_result::num_rows.)
*/
function qa_db_num_rows($result)
{
if ($result instanceof mysqli_result)
return $result->num_rows;
return 0;
}
/**
* Return the value of the auto-increment column for the last inserted row.
*/
function qa_db_last_insert_id()
{
$db = qa_db_connection();
return $db->insert_id;
}
/**
* Return the number of rows affected by the last query.
*/
function qa_db_affected_rows()
{
$db = qa_db_connection();
return $db->affected_rows;
}
/**
* For the previous INSERT ... ON DUPLICATE KEY UPDATE query, return whether an insert operation took place.
*/
function qa_db_insert_on_duplicate_inserted()
{
return (qa_db_affected_rows() == 1);
}
/**
* Return a random integer (as a string) for use in a BIGINT column.
* Actual limit is 18,446,744,073,709,551,615 - we aim for 18,446,743,999,999,999,999.
*/
function qa_db_random_bigint()
{
return sprintf('%d%06d%06d', mt_rand(1, 18446743), mt_rand(0, 999999), mt_rand(0, 999999));
}
/**
* Return an array of the names of all tables in the Q2A database, converted to lower case.
* No longer used by Q2A and shouldn't be needed.
*/
function qa_db_list_tables_lc()
{
return array_map('strtolower', qa_db_list_tables());
}
/**
* Return an array of the names of all tables in the Q2A database.
*/
function qa_db_list_tables()
{
return qa_db_read_all_values(qa_db_query_raw('SHOW TABLES'));
}
/*
The selectspec array can contain the elements below. See qa-db-selects.php for lots of examples.
I bet you have your code set to use persistent connections for MySQL. This can result in open but idle connections, maxing out whatever your (most likely shared) hosting provider allocates for you.
You have a QA_PERSISTENT_CONN_DB constant in your code that determines your connection mode. You will find it defined as true or false somewhere in a configuration file. (It's not defined in your code above). Ensure it is set to false and see if the problem is resolved. When persistent connections are off, the code above takes care of closing the connections. You don't need to add it in.
If you're interested in knowing more about persistent connections, this answer covers a lot of ground; and there's more in the manual:
Persistent connections are links that do not close when the execution of your script ends.
Next time, make sure you read through your code to see if the functionality is already there (in this case, it's inside function qa_db_disconnect()), and if it's simply a matter of configuration.
I have tried a few different ways to make this work to no avail.
Basically, I have these 4 lines that are in many functions:
function getID($URL){
//These 3 plus this comment line
$parentQ = "select * from cdi_content where URL=\"$URL\"";
$parentResult = mysql_query($parentQ); // Run the Query
$link = mysql_fetch_assoc($parentResult); // Query Result
...continues...
That basically tells the database to check $URL and if it matches the URL string in the database, it grabs all of the data associated with that URL, and I grab what I need with
$link['ID'];
Which would give me the ID associated with the $URL URL.
The function checks a list of conditionals that either print the 'override' variable ($ID), the default variable ($defaultID) or pulls from the server ($link['ID']), like below. The 'override' is a variable outside of the function that is currently called in by global $ID.
$ID = ''; //Overrides if set
function parseData($URL){
//Initialize Query for Table Data
$parentQ = "select * from cdi_content where URL=\"$URL\"";
$parentResult = mysql_query($parentQ); // Run the Query
return mysql_fetch_assoc($parentResult); // Query Result
};
function getID($URL){
global $ID;
$serverID = parseData($URL);
if(empty($ID)){
if(empty($serverID)){
echo $defaultID;
} else { echo $serverID['ID']; }
}//end of ifs
else
{ echo $ID; }
};
So you you want to parse the data row you would need search criteria $criterion and a field you you want it to return:
function getField($criterion, $returnField){
$parentQ = "select * from cdi_content where URL='".$criterion."' LIMIT 1"; // also make sure you that $criterion is safe to use in a query
$parentResult = mysql_query($parentQ); // Run the Query
if ($parentResult)
{
$row = mysql_fetch_assoc($parentResult);
return $row[$returnField];
}
else
{
return FALSE;
}
}
Now you can call it:
$linkId = getField($url, 'ID');
If I'm understanding you correctly, you're just trying to refactor that function so you don't have to keep on re-writing it. Is that correct?
If so, you can extract a new function like this:
function getLink($URL){
$parentQ = "select * from cdi_content where URL=\"$URL\"";
$parentResult = mysql_query($parentQ); // Run the Query
return mysql_fetch_assoc($parentResult);
}
And then call it like this:
function getID($URL){
$link = getLink($URL);
}
I have a simple recursive array function that looks like this:
function recursive_array($results) {
global $DBH;
if (count($results)) {
echo $res - > Fname;
foreach($results as $res) {
$STH = $DBH - > query("SELECT FID,FParentID,Fname FROM list WHERE FParentID = ".$res - > FID."");
$fquerycount = $STH - > rowCount();
$STH - > setFetchMode(PDO::FETCH_OBJ);
recursive_array($STH);
}
}
}
$FID = isset($_GET['FID']) ? $_GET[' FID'] : 0;
$STH = $DBH - > query("SELECT FID,FParentID,Fname FROM list WHERE FParentID ='0' ");
$STH - > setFetchMode(PDO::FETCH_OBJ);
recursive_array($STH);
I also have created a simple query class that looks like this:
class queryloop {
function __construct($args) {
global $DBH;
$table = $args['tbl'];
if (array_key_exists('orderby', $args)): $orderby = 'ORDER BY '.$args['orderby'];
else: $orderby = '';endif;
if (array_key_exists('groupby', $args)): $groupby = 'GROUP BY '.$args['groupby'];
else: $groupby = '';endif;
if (array_key_exists('start', $args)): unset($orderby);$start = $args['start'].' , ';
else: $start = '';endif;
if (array_key_exists('limit', $args)): $limit = 'LIMIT '.$start.' '.$args['limit'];
else: $limit = '';endif;
// UNSET the previously used array keys so they are not use again to create the query string
unset($args['tbl']);
unset($args['orderby']);
unset($args['groupby']);
unset($args['start']);
unset($args['limit']);
// Checks if args still an array after UNSET above. If not empty create the query string
if (!empty($args)): foreach($args as $k = > $v): $querystr. = 'AND '.$k.' = \''.$v.'\'';endforeach;
// If args array empty return empty query string
else: $querystr = '';endif;$STH = $DBH - > query("SELECT * FROM ".$table." WHERE key = '".KEY."' ".$querystr." ".$groupby." ".$orderby." ".$limit." ");
if ($STH): $STH - > setFetchMode(PDO::FETCH_OBJ);
while ($row = $STH - > fetch()): foreach($row as $key = > $val):
// check if value is numeric //
if (is_numeric($row - > $key)): $data[$row - > ID][$key] = $row - > $key;
// check if value is array //
elseif(is_array($row - > $key)): $data[$row - > ID][$key] = $row - > $key;
// check if value is not numeric or array convert to html entities //
else: $data[$row - > ID][$key] = htmlentities($row - > $key);endif;endforeach;endwhile;$this - > data = json_encode($data); // return json array if data
else: $this - > data = ''; // return 'null' if no data
endif;
}
}
$args = array('tbl' = > 'atable', 'limit' = > '5', 'start' = > '200', 'orderby' = > 'ID DESC');
$loop = new queryloop($args) // run the loop etc.
How do I turn my recursive array into something like the class queryloop so that I can "pull out" json endoded data I know that this (below) is totally wrong but what ever I do I cannot get a correctly formed json array or even anything to return form my attempted class below. Help would be much appreciate. Thanks in advance.
class recloop {
function __construct() {}
function recursive_array($results) {
global $DBH;
if (count($results)) {
foreach($results as $res) {
echo $res - > Name;
$STH = $DBH - > query("SELECT * FROM atable WHERE ParentID = ".$res - > ID."");
$fquerycount = $STH - > rowCount();
$STH - > setFetchMode(PDO::FETCH_OBJ);
recursive_array($STH);
}
}
}
function recursive_start() {
global $DBH;
$ID = isset($_GET['ID']) ? $_GET['ID'] : 0;
$STH = $DBH - > query("SELECT * FROM atable WHERE ParentID = '".$ID."' ");
$STH - > setFetchMode(PDO::FETCH_OBJ);
recursive_array($STH);
}
}
How do I turn my recursive array into something like the class queryloop so that I can "pull out" json endoded data I know that this (below) is totally wrong but what ever I do I cannot get a correctly formed json array or even anything to return form my attempted class below. Help would be much appreciate. Thanks in advance.
To answer your question, I would say it's not specific if you encapsulate your routines into objects or not that much, but that you take care that each object is there for a sole purpose. For example:
One object is fetching the data from the database.
One object/composite/array is the data-structure, representing the data.
One object or function is taking over the job to convert/encode the data into json.
Within your code I see that you right now are only running SQL-queries. The data fetched from the database server is not stored into a return variable at all, it get's directly consumed while being recursively processed. I assume you do this for debugging reasons.
So the actual question is, what do you want to do? You write that you want to encode an object into json output, which is perfectly possible with json_encodeDocs, however I think you refer to some specific data, like the entity (data) of the most parentId or something.
Following is some mock-up code based on your code for reading purposes (not tested, must not match your needs) that can provide all parent objects of that one specified by ID by using recursion. The recursion has been criticised because this can result in running a lot of queries - and additionally there is risk to create an endless loop which will result in a recursion stack overflow - your program crashes then.
To handle that alternatively, this is bound to the database design (which should be done before the design of the code, and I don't know your database design nor what you actually want to do, so I can't add assumptions for that). So the following code takes care of already queried objects only while still using recursion as the strategy to query your database.
For the actual data-structure I opted for an array of plain old PHP objects, keyed by the ID field from the database (which I assume that it exists per record):
/**
* HTTP Get Parameter (Input)
*/
class HTTPGetParameter {
private $name;
private $default;
public function __construct($name, $default = '') {
$this->name = (string) $name;
$this->default = (string) $default;
}
/**
* #return string
*/
public function getValue()
{
return isset($_GET[$name]) ? $_GET[$name] : $this->default;
}
/**
* #return int
*/
public function getValueInt()
{
return (int) $this->getValue();
}
/**
* #link http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring
*/
public function __toString()
{
return $this->getValue();
}
}
/**
* Data Provider
*/
class PDODataProvider
{
private $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
/**
* #return array
*/
public function findAllATableParents($id)
{
return $this->findAllOn('atable', 'ParentID', $id);
}
public function findAllBTableParents($id)
{
return $this->findAllOn('btable', 'ParentID', $id);
}
private function findAllOn($table, $field, $id)
{
$id = (int) $id;
$objects = array();
$sql = sprintf("SELECT * FROM %s WHERE %s = '%d'", $table, $field, $id);
$pdoStatement = $this->pdo->query($sql);
$pdoStatement->setFetchMode(PDO::FETCH_OBJ);
foreach($pdoStatement as $parent)
{
$parentId = $parent->ID;
# parents that had been queried are skipped
if (isset($objects[$parentId]))
continue;
$objects[$parentId] = $parent;
# add parent objects by recursion
$objects += $this->findAllParents($parentId);
}
return $objects;
}
}
/**
* main
*/
$data = new PDODataProvider($DBH);
$id = new HTTPGetParameter('ID', 0);
$objects = $data->findAllParents($id->getValueInt());
echo json_encode($objects);
I hope this example is helpful for you to answer your question.