I write a class to load data and export as SMARTY format through MySQLi
public function myRow($sql)
{
$this->connect();
$rec = $this->conn->query($sql);
$this->recordCount = $rec->num_rows;
if ($this->makeRecordCount) {
$this->totalRecordCount = $this->recordCount;
}
if ($this->recordCount > 0) {
$names = array();
$result = array();
$temp = array();
$count = $rec->field_count;
// Get fields name
while ($fields = mysqli_fetch_field($rec)) {
$names[] = $fields->name;
}
while ($row = $rec->fetch_assoc()) {
foreach ($names as $name) {
$temp[$name] = $row[$name];
}
array_push($result, $temp);
}
} else {
$result = null;
}
$this->conn->close();
return $result;
}
Then I can to something like
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
There are maybe many data need to be loaded in one page, and I only want to connect to database once, but I want to do it all in class, I don't want to do something like
$class->connect();
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
$sql = "SELECT * FROM `table2`";
$datas = $class->myRow($sql);
$smarty->assign('data2s', $data2s);
$class->close();
I feel it's ugly, but if I do this in class, that means I open and close connection when each data is loading, how to do it more beautiful?
maybe i'm wrong but you don't need to force a mysql connection to close because of the fact that if the connection is not persistent the php garbage collector close all connections after the script execution.
so i suggest you not to force the mysql close, let the garbage collector handle this task and only close the connection by yourself if you're sure that no more mysql transactions are required.
You simply don't need to (and shouldn't) open/close the connection inside your myRow() function.
Option 1 (naive approach): handle the connection at class level
class MyDAOClass {
private static $connection = null;
public function __construct() {
if (self::$connection === null) {
// establish connection here
}
}
public function myRow(...) {
// use self::$connection here
}
}
Option 2:
Handle the connection from outside the class altogether (possibly in a singleton class), since all objects from your application probably can share the same object.
Your second suggestion is what I would do.
$class->connect();
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
$sql = "SELECT * FROM `table2`";
$datas = $class->myRow($sql);
$smarty->assign('data2s', $data2s);
$class->close();
You connect to the database once. As PHP is single threaded, you will load the first result, then go right away and load the second result. Once everything is done, you close the connection. No connection is kept alive longer than it has to, which is good.
What I usually do it make a method associated with Smarty that closes my database-connection too. That way I don't have to worry about closing it.
Something like this:
<?php
// Store reference to Smarty in Class
$class->setSmarty($smarty);
[...]
// Done with all database fetching, now display the template
$class->display('my_template.tpl');
[...]
// Example inplementation of the class
Class YourClass {
private $smarty;
public function setSmarty($smarty) {
$this->smarty = &$smarty;
}
public function display($tpl) {
$this->close();
$this->smarty->display($tpl);
}
}
?>
Related
So I search for this title hoping someone would have already answered it however, I came across similar topics on other languages but not PHP so maybe this will help others.
I am constantly using this following script to call on the database but how can I create it so that I can make it just once at the top of the class for example and use it in every method on the class page that needs it. Example: An single page may not have all of the data it needs from the same table but if the table contains 50% of the data or more for that page, how can I modify this so that I can just say it once and let the rest of the following scripts display the data it extracted in the first place by calling it all just once?
Here's what I have now.
<?php
if($res = $dbConn->query("SELECT Column FROM Table")){
while($d = $res->fetch_assoc()){
printf("Enter HTML here with proper %s", $d['Column']);
}
}
?>
I want to call on this without the printf(" "); collect and store the data so that I can then call the results while printing or echoing the results with the HTML in other methods. What os the most efficient way? I don't want to make the same call over and over and over... well, you get the point.
Should I use fetch_array or can I still do it with fetch_assoc?
not very sure if it's the answer you want.
you can use include/include_once/require/require_once at the top of the page you want to use the function
for example:
general_function.php:
-----
function generate_form( $dbConn, $sql ) {
if($res = $dbConn->query("SELECT Column FROM Table")) {
while($d = $res->fetch_assoc()) {
printf("Enter HTML here with proper %s", $d['Column']);
}
}
}
and for those pages you want to use the function, just put
include "$PATH/general_function.php";
and call generate_form
Try this:
class QueryStorage {
public static $dbConn = null;
public static $results = [];
public static function setConnection($dbConn) {
self::$dbConn = $dbConn;
}
public static function query($query, $cache = true) {
$result = (array_key_exists($query, self::$results))?
self::$results[$query] : self::$dbConn->query($query);
if($cache) {
self::$results[$query] = $result;
}
return $result;
}
public static function delete($query) {
unset(self::$results[$query]);
}
public function clean() {
self::$results = [];
}
}
usage:
at top somewhere pass connection to class:
QueryStorage::setConnection($dbConn);
query and store it:
$result = QueryStorage::query("SELECT Column FROM Table", true);
if($result){
while($d = $result->fetch_assoc()){
printf("Enter HTML here with proper %s", $d['Column']);
}
}
reuse it everywhere:
$result = QueryStorage::query("SELECT Column FROM Table", true); // it will return same result without querying db second time
Remember: it's runtime cache and will not store result for second script run. for this purposes You can modify current class to make it
work with memcache, redis, apc and etc.
If I understood you correctly, then the trick is to make an associative array and access with its 'key' down the code.
$dataArray = array();
// Add extra column in select query for maintaining uniqness. 'id' or it can be any unique value like username.
if($res = $dbConn->query("SELECT Column,id FROM Table")){
while($d = $res->fetch_assoc()){
$dataArray[$d['id']] = $d['Column'];
}
}
//you have value in the array use like this:
echo $dataArray['requireValueId'];
//or , use 'for-loop' if you want to echo all the values
You need a function which takes in the query as a parameter and returns the result.
Like this:
public function generate_query($sql) {
if($res = $dbConn->query($sql)){
while($d = $res->fetch_assoc()){
printf("Enter HTML here with proper %s", $d['Column']);
}
}
}
I am trying to retrieve a list of items from a mySQL db and insert them as a list in a select object on a webpage. The following is the bit of code that isnt working.
In the first line, I am trying to retrieve a JSON object from a public function called getBrands() in a singleton object I have created called DatabaseInterface.
The second line is then attempting to turn that JSON object into a php array.
Finally, I am running a loop which can option each item in between tags for the webpage.
Where am I going wrong?
<?php
var $brandArrayJSON = DatabaseInterface::getBrands();
$brandArray = JSON_decode($brandArrayJSON);
for ($loop=0; $loop < sizeof($brandArray); $loop++) {
echo "<option>$brandArray[$loop]</option>";
}
?>
EDIT: In case it helps, here is my DatabaseInterface singleton. I have included this file at the top of my php file
class databaseInterface {
private static $_instance;
// Private constructor prevents instantiation
private function __construct() {
}
public static function getInstance() {
if (!self::$_instance) {
self::$_instance = mysqli_connect(self::databaseHost, self::databaseUsername, self::databasePassword, self::databaseName);
if (mysqli_connect_errno(self::$_instance)) {
throw new Exception("Failed to connect to MySQL:" . mysqli_connect_error());
}
}
return self::$_instance;
}
public function getBrands() {
try {
$con = DatabaseInterface::getInstance();
} catch (Exception $e) {
// Handle exception
echo $e->getMessage();
}
$query = "SELECT psBrandName from brands";
$result = mysqli_query($con, $query) or die ("Couldn't execute query. ".mysqli_error($con));
$resultArray[] = array();
while ($row = mysqli_fetch_assoc($result)) {
extract($row);
$resultArray[] = $psBrandName;
}
return json_Encode($resultArray);
}
There is nothing "wrong" with the code, in that it should work (provided nothing is broken on the query-side). However, there are several things that should be improved.
First, basically what the getBrands() method is doing is equivalent to this:
$brandArray = json_encode(array('test','test2','test3'));
echo $brandArray; // returns ["test","test2","test3"]
Now, when you decode that you get the same thing you originally put in (an array):
$brandArray = json_decode('["test","test2","test3"]');
var_dump($brandArray); // Will dump an array
Since this is an array (not a PHP object), you can just use a foreach.
foreach($brandArray as $option) {
echo '<option>', $option, '</option>';
}
If you're worried about it being an object in some instances (maybe you had a non-array JS object which would be mostly the equivalent to a PHP associative array), you could cast the json_decode result into an array.
$brandArray = (array)$brandArray;
Now, in your getBrands() method, I would highly recommend just using $row['psBrandName'] instead of cluttering things up with extract, unless you have a really good reason to do this.
I have a somewhat general question regarding what's best when programming in PHP using also database connections. I am building a project which includes several modules and each module needs to connect to the MySQL sometimes. The module files are included in the main index.php depending on the action selected from the menu by the user. I guess, most projects work this way anyway.
So far what I do is always open the connection at the start of each module file and close it after the queries have run.
My question is this: is it better to open the connection to the database in the beginning of the index.php and close it in the end so to have 1 connection open, or do multiple connections which stay open for less time? What's best for speed and overhead?
As N.B. pointed out, you should probably setup a class to handle all your database related tasks. I am posting a snippet from my code archives to illustrate how you can setup such a class. This is solely for purposes of illustrating and not guaranteed to work for you if you just copy and paste. It may need some refinement. It also makes use of the smarty class to process data views.
I recommend stripping out whatever you don't need. Set your MySQL connection params. In your index file, you can instantiate a SQL object and simply call the appropriate methods. The methods return and keep your result as both an associative and indexed array. Iterating through table rows is as easy as:
$SQL->GetRows('RowTemplate.tpl', 'StoredProcedure', 'Parameters');
FYI This is part of a bigger $Portal framework object in case you're wondering what the $Portal reference is to. $SQL merely extends $Portal.
I hope this helps. Good luck
class SQL {
/********** MEMBER VARIABLES **********************************************************************/
var $Connection; // DB connection handler
var $Result; // ResultSet returned from last call during life of object instance
var $RowCount; // RowCount for ResultSet returned from last call during life of object instance
var $ResultArray; // ResultSet Array returned from last call during life of object instance
var $Query; // Query last submitted during life of object instance
var $ErrorNumber; // Error number for error returned from last call during life of object instance
var $Error; // Error returned from last call during life of object instance
var $Message; // Messages returned during life of object instance
// Switches, flags, markers, etc
var $DebugMode;
var $LogActive;
var $ShowErrorMsg;
// Modules array
var $Modules;
// SQL Connection Info - PROTECTED!
protected $Host = "localhost";
protected $User = "mydatabase";
protected $Password = "mypassword";
protected $Schema = "myschema";
/********** MEMBER FUNCTIONS **********************************************************************/
// Object Constructor
function __construct() {
// Automatically open DB Connection
//$this->OpenDBConnection();
//echo "User Object Constructor<br>";
}
// Open new DB Connection
function OpenDBConnection() {
return ($this->Connection = mysqli_connect($this->Host, $this->User, $this->Password, $this->Schema))? true : false;
}
// Close DB Connection
function CloseDBConnection() {
mysqli_close($this->Connection);
//return true;
}
// Return error messages
function GetError() {
return $this->Error;
}
// Return last query string
function GetQuery() {
return $this->Query;
}
// Call, execute stored procedure and return result set
/* NOTES: The result set is always returned as an int array of associative arrays
That is, if $Result was returned, the first row would be referenced as $Result[0]
and any column of the first row would be referenced as $Result[0]['ColumnName']
COMMENTS: */
function CallProcedure($StoredProcedure) {
// Clear any System Errors
$this->ErrorNumber = '';
$this->Error = '';
// Open DB Connection
if(!$this->OpenDBConnection()) return false;
// Kill error if there are no extra Params passed
$Params = #func_get_arg(1);
// Build Query
$this->Query = "CALL $StoredProcedure ($Params)";
//if($this->Result = $this->Connection->query($this->Query)) {
if($this->Result = mysqli_query($this->Connection, $this->Query)) {
// Reset global Result Set
$this->ResultArray = Array();
// Set record count for current record set
$this->RowCount = 0;
while($Row = #mysqli_fetch_array($this->Result, MYSQLI_BOTH)) {
$this->ResultArray[$this->RowCount] = $Row;
$this->RowCount++;
}
// Close DB Connection
$this->CloseDBConnection();
return $this->ResultArray;
}
// Grab Error
$this->ErrorNumber = mysqli_errno($this->Connection);
$this->Error = mysqli_error($this->Connection);
// Close DB Connection
$this->CloseDBConnection();
return false;
}
/* Using Smarty class, return row template filled with record set from given stored procedure
EXAMPLE 1: Primary Function - Using data set from stored procedure
$Portal->SQL->GetRows('RowTemplate.tpl', 'StoredProcedure', 'Parameters');
EXAMPLE 2: Secondary Function - Using data set in second dimensional associative array
$Portal->SQL->GetRows('RowTemplate.tpl', 'ARRAY', $MyDataSetArray); */
function GetRows($RowTemplate, $Procedure) {
// Kill error if there are no extra Params passed
$Parameters = #func_get_arg(2);
// Set return string
$ReturnString = '';
// If Procedure is ARRAY then params are data set else Call procedure and return results array
$Result = ($Procedure=='ARRAY')? $Parameters : $this->CallProcedure($Procedure, $Parameters);
// Loop through result set initializing smarty obj for each row
$Count = 0;
while(IsSet($Result[$Count])) {
$RowTemplateObj = new Smarty;
$RowTemplateObj->assign('SCRIPT_NAME', SCRIPT_NAME);
$RowTemplateObj->assign('HOST_NAME', HOST_NAME);
// Loop though each result row as an associative array of column - values
foreach ($Result[$Count] as $Key => $Value) {
if(IsSet($Result[$Count][$Key])) $RowTemplateObj->assign($Key, (is_array($Value))?$Value:stripslashes($Value));
//if(IsSet($Result[$Count][$Key])) $RowTemplateObj->assign($Key, $Value);
}
$RowTemplateObj->assign('bgcolor', '{$bgcolor'. ($Count%2 + 1) .'}');
// Concatenate populated row into return string
$ReturnString .= $RowTemplateObj->fetch($RowTemplate);
$Count++;
}
return $ReturnString;
}
function GetSelectList($Procedure, $Parameters, $OptionValueField, $OptionNameField) {
// Kill error if there are no extra Params passed
$Selected = #func_get_arg(4);
// Set return string
$ReturnString = '';
// Get List Resultset
$Result = $this->CallProcedure($Procedure, $Parameters);
// Loop through result set and set <option> ta row
$Count = 0;
while(IsSet($Result[$Count])) {
$ReturnString .= '<option value="'.$Result[$Count][$OptionValueField].'"';
$ReturnString .= ($Selected==$Result[$Count][$OptionValueField])? ' selected ' : '';
$ReturnString .= '>'.$Result[$Count][$OptionNameField].'</option>';
$Count++;
}
return $ReturnString;
}
function Execute($SQL) {
// Clear any System Errors
$this->ErrorNumber = '';
$this->Error = '';
// Open DB Connection
if(!$this->OpenDBConnection()) return false;
// Assign Query
$this->Query = $SQL;
if($this->Result = mysqli_query($this->Connection, $this->Query)) {
// Reset global Result Set
$this->ResultArray = Array();
// Set record count for current record set
$this->RowCount = 0;
while($Row = #mysqli_fetch_array($this->Result, MYSQLI_BOTH)) {
$this->ResultArray[$this->RowCount] = $Row;
$this->RowCount++;
}
// Close DB Connection
$this->CloseDBConnection();
return $this->ResultArray;
}
// Grab Error
$this->ErrorNumber = mysqli_errno($this->Connection);
$this->Error = mysqli_error($this->Connection);
// Close DB Connection
$this->CloseDBConnection();
return false;
}
MAKE Saperate connections in same file and close them according.
/*****Connection for portal1 ******/
$portal1_link = mysql_connect('localhost','root','') or die('Cannot connect to the DB');
mysql_select_db('mylekha_auction',$portal1_link) or die('Cannot select the DB');
/*****Connection for portal2 ******/
$portal2_link = mysql_connect('localhost','root','') or die('Cannot connect to the DB');
mysql_select_db('mylekha_auction',$portal2_link) or die('Cannot select the DB');
PHP/MySQLisolating database access in class - how to handle multiple row Selects
Here’s a coding question.
I isolated all DB access functions in a class
<?php
class DB {
var $conn;
function DBClass () {
#$this-> conn = mysqli_connect (DB_SERVER, DB_USER, DB_PASS, DB_NAME);
}
function validateUser ($aUserid, $aPassword) {
… validation code – sql injection code etc..
$sql = "Select userid, name, level From users where userid = '$aUserid' and password = '$aPassword'";
$result = mysqli_query ( $this->conn, $sql );
if (!$result || (mysqli_num_rows ($result) < 1)) {
return false;
}
$dbarray = mysqli_fetch_assoc ($result); // get a row
return $dbarray;
}
function getProduct ($aProductid) {
return $dbarray;
}
function getProductList () {
// <----------- this would be the problem function
}
}
$DB = new DBClass();
?>
My calling routine:
<?php
$dbarray = $DB->validateUser ($_POST['userid'], $_POST['password']);
?>
No problem it works fine. I run into a problem with a result set of more than one row. Now I have to get back to the class object for each row. It’s no problem if I include the MySQL code in the calling routine, but I’d like to keep it isolated in my class and I’m not sure how to code it.
Any thoughts? Any examples?
If you use PHP 5.3.0 and mysqlnd, you can use the new function mysqli_fetch_all(). This returns an array of associative arrays.
If you use an earlier version of PHP, you could switch to using PDO, and use the function PDOStatement::fetchAll().
You ask in a comment what about a very large result set. It's true that an unbounded result set could cause the array to exceed your PHP memory limit and that would cause a fatal error and halt the script. But is this really a problem? How many products do you have? You could use LIMIT to make sure the query isn't unbounded.
Re the other part of your questions regarding going back to a class, I'd suggest making an Iterator class:
class DB implements IteratorAggregate
{
protected $_data = array();
public function getProductList() {
// fetch all results from SQL query, stuff them into $this->_data
return $this->getIterator();
}
public function getIterator() {
return new ArrayIterator($this->_data);
}
}
Now you can use the class in a foreach loop:
$db = new DB();
foreach ($db->getProductList() as $product) {
// do something with each product
}
The IteratorAggregate interface means you can even do this:
$db = new DB();
$db->getProductList();
// ...other steps...
foreach ($db as $product) {
// do something with each product
}
Of course you could only store one result set at a time with this method. If you used your DB class for any other queries in the meantime, it would complicate things. For this reason, most people don't try to write a single class to encapsulate all database operations. They write individual classes for each type of Domain Model they need to work with, decoupled from the database connection.
you could save the result in an array and return it:
function getProductList () {
$sql = "SELECT ...";
$result = mysqli_query ( $this->conn, $sql );
$myProducts = array();
while ($row = mysqli_fetch_assoc($result))
$myProducts[] = $row; // or array_push($myProducts, $row)
}
return $myProducts
}
As a result you'll have an array of arrays and each element of it will contain one row of the result.
You have a SQL injection right in your login page.
What happens if someone inputs that as password:
xxx' OR 'yyy' <> 'x
I'm slowly moving all of my LAMP websites from mysql_ functions to PDO functions and I've hit my first brick wall. I don't know how to loop through results with a parameter. I am fine with the following:
foreach ($database->query("SELECT * FROM widgets") as $results)
{
echo $results["widget_name"];
}
However if I want to do something like this:
foreach ($database->query("SELECT * FROM widgets WHERE something='something else'") as $results)
{
echo $results["widget_name"];
}
Obviously the 'something else' will be dynamic.
Here is an example for using PDO to connect to a DB, to tell it to throw Exceptions instead of php errors (will help with your debugging), and using parameterised statements instead of substituting dynamic values into the query yourself (highly recommended):
// connect to PDO
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "password");
// the following tells PDO we want it to throw Exceptions for every error.
// this is far more useful than the default mode of throwing php errors
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// prepare the statement. the placeholders allow PDO to handle substituting
// the values, which also prevents SQL injection
$stmt = $pdo->prepare("SELECT * FROM product WHERE productTypeId=:productTypeId AND brand=:brand");
// bind the parameters
$stmt->bindValue(":productTypeId", 6);
$stmt->bindValue(":brand", "Slurm");
// initialise an array for the results
$products = array();
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$products[] = $row;
}
According to the PHP documentation is says you should be able to to do the following:
$sql = "SELECT * FROM widgets WHERE something='something else'";
foreach ($database->query($sql) as $row) {
echo $row["widget_name"];
}
If you like the foreach syntax, you can use the following class:
// Wrap a PDOStatement to iterate through all result rows. Uses a
// local cache to allow rewinding.
class PDOStatementIterator implements Iterator
{
public
$stmt,
$cache,
$next;
public function __construct($stmt)
{
$this->cache = array();
$this->stmt = $stmt;
}
public function rewind()
{
reset($this->cache);
$this->next();
}
public function valid()
{
return (FALSE !== $this->next);
}
public function current()
{
return $this->next[1];
}
public function key()
{
return $this->next[0];
}
public function next()
{
// Try to get the next element in our data cache.
$this->next = each($this->cache);
// Past the end of the data cache
if (FALSE === $this->next)
{
// Fetch the next row of data
$row = $this->stmt->fetch(PDO::FETCH_ASSOC);
// Fetch successful
if ($row)
{
// Add row to data cache
$this->cache[] = $row;
}
$this->next = each($this->cache);
}
}
}
Then to use it:
foreach(new PDOStatementIterator($stmt) as $col => $val)
{
...
}