So you can use something like this:
$query = $db->select();
$query->from('pages', array('url'));
echo $query->__toString();
to examine the sql that the Zend Db Framework is going to use for that SELECT query. Is there an equivilent way to view the SQL for an update?
$data = array(
'content' => stripslashes(htmlspecialchars_decode($content))
);
$n = $db->update('pages', $data, "url = '".$content."'");
??
Use Zend_Db_Profiler to capture and report SQL statements:
$db->getProfiler()->setEnabled(true);
$db->update( ... );
print $db->getProfiler()->getLastQueryProfile()->getQuery();
print_r($db->getProfiler()->getLastQueryProfile()->getQueryParams());
$db->getProfiler()->setEnabled(false);
Remember to turn the profiler off if you don't need it! I talked to one fellow who thought he had a memory leak, but it was the profiler instantiating a few PHP objects for each of the millions of SQL queries he was running.
PS: You should use quoteInto() in that query:
$n = $db->update('pages', $data, $db->quoteInto("url = ?", $content));
No, not directly, since Zend Framework builds and executes the SQL inside the adapter method Zend_Db_Adapter_Abstract::update:
/**
* Updates table rows with specified data based on a WHERE clause.
*
* #param mixed $table The table to update.
* #param array $bind Column-value pairs.
* #param mixed $where UPDATE WHERE clause(s).
* #return int The number of affected rows.
*/
public function update($table, array $bind, $where = '')
{
/**
* Build "col = ?" pairs for the statement,
* except for Zend_Db_Expr which is treated literally.
*/
$set = array();
foreach ($bind as $col => $val) {
if ($val instanceof Zend_Db_Expr) {
$val = $val->__toString();
unset($bind[$col]);
} else {
$val = '?';
}
$set[] = $this->quoteIdentifier($col, true) . ' = ' . $val;
}
$where = $this->_whereExpr($where);
/**
* Build the UPDATE statement
*/
$sql = "UPDATE "
. $this->quoteIdentifier($table, true)
. ' SET ' . implode(', ', $set)
. (($where) ? " WHERE $where" : '');
/**
* Execute the statement and return the number of affected rows
*/
$stmt = $this->query($sql, array_values($bind));
$result = $stmt->rowCount();
return $result;
}
You can, temporarily, insert a var_dump and exit inside this method to inspect the sql to ensure that it is correct:
/**
* Build the UPDATE statement
*/
$sql = "UPDATE "
. $this->quoteIdentifier($table, true)
. ' SET ' . implode(', ', $set)
. (($where) ? " WHERE $where" : '');
var_dump($sql); exit;
I quess another way is to log the actual SQL query, rather than changing the ZF library code, by combining the profiler data.
$db->getProfiler()->setEnabled(true);
$db->update( ... );
$query = $db->getProfiler()->getLastQueryProfile()->getQuery();
$queryParams = $db->getProfiler()->getLastQueryProfile()->getQueryParams();
$logger->log('SQL: ' . $db->quoteInto($query, $queryParams), Zend_Log::DEBUG);
$db->getProfiler()->setEnabled(false);
Recently came across this looking for a way to debug a zend_db_statement. If anyone else comes across this with the same search, you can use the following function.
Just replace "self::getDefaultAdapter()" with your method of getting a DB connection or adapter.
/**
* replace any named parameters with placeholders
* #param string $sql sql string with placeholders, e.g. :theKey
* #param array $bind array keyed on placeholders, e.g. array('theKey', 'THEVALUE')
*
* #return String sql statement with the placeholders replaced
*/
public static function debugNamedParamsSql($sql, array $bind) {
$sqlDebug = $sql;
foreach($bind as $needle => $replace) {
$sqlDebug = str_replace(
':' . $needle,
self::getDefaultAdapter()->quote($replace),
$sqlDebug
);
}
return $sqlDebug;
}
Related
Assume the following code snippet:
$res = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
$foreign . '.*', $local, $mm, $foreign, 'AND ' . $local . '.uid=' . $constraintUid);
while ($r=$GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
...
$foreign / foreign: name of foreign DB table
$local / local: name of local DB table
$mm / mm : name of relation DB table (typically somethingorother_mm)
$constraintUid / constraintUid: some uid
I would like to migrate code to TYPO3 9 using the Doctrine based database functions.
The above code gets converted to the following SQL statement:
SELECT foreign.* FROM local,mm,foreign
WHERE local.uid=mm.uid_local
AND foreign.uid=mm.uid_foreign
AND local.uid = constraintUid
see exec_SELECT_mm_query:
Parameters:
string $select Field list for SELECT
string $local_table Tablename, local table
string $mm_table Tablename, relation table
string $foreign_table Tablename, foreign table
string $whereClause Optional additional WHERE clauses put in
the end of the query. ...
...
My suggestion would be:
$foreign = 'a';
$mm = 'mm';
$local = 'b';
$item = 1;
/** #var \TYPO3\CMS\Core\Database\Query\QueryBuilder $queryBuilder */
$queryBuilder = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
\TYPO3\CMS\Core\Database\ConnectionPool::class
)->getQueryBuilderForTable($foreign);
$expr = $queryBuilder->expr();
/** #var \Doctrine\DBAL\Driver\Statement $rows */
$rows = $queryBuilder
->select('foreign.*')
->from($foreign, 'foreign')
->innerJoin('foreign', $mm, 'mm', $expr->eq('foreign.uid', 'mm.uid_foreign'))
->innerJoin('mm', $local, 'local', $expr->eq('mm.uid_local', 'local.uid'))
->where(
$expr->eq('local.uid', $queryBuilder->createNamedParameter($item, \PDO::PARAM_INT))
)
->execute();
while (($row = $rows->fetch()) != null) {
}
In moodle site (use moodle Version 2.6.3), I have generated install.xml file by XMLDB editor, but it's use only to create table in database during plugin installation. I want to insert some default rows in the table also.
Any body can help me how to edit in install.xml file for insert data
To add data after an install, create a file called yourplugin/db/install.php with
UPDATE: added xml parser
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir . '/xmlize.php');
function xmldb_yourpluginname_install() {
global $CFG, $OUTPUT, $DB;
// Your add data code here.
$xmltext = file_get_contents('import.xml');
$records = parse_xml($xmltext, 'records', 'record');
foreach ($records as $record) {
$DB->insert_record('yourtablename', $record);
}
}
/**
* Converts XML text into an array of stdclass objects.
*
* #param type $text - xmltext
* #param type $elementnames - plural name of elements
* #param type $elementname - name of element
* #return array|boolean - array of record objects
*/
function parse_xml($text, $elementnames, $elementname) {
// Seems that xmlize needs a lot of memory.
ini_set('memory_limit', '256M');
// Ensure content is UTF-8.
$content = xmlize($text, 1, 'UTF-8');
$records = array();
if (!empty($content[$elementnames]['#'][$elementname])) {
$rows = $content[$elementnames]['#'][$elementname];
foreach ($rows as $row) {
$fields = $row['#'];
$row = new stdClass();
foreach ($fields as $fieldname => $fieldvalue) {
$row->$fieldname = $fieldvalue[0]['#'];
}
$records[] = $row;
}
return $records;
}
return false;
}
This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 10 years ago.
I'm hesitant to post this, as I'd really prefer to figure this out myself, but I don't think I will. I'm just trying to set a class for mysqli stuff, to make it as dynamic as possible, and yes I'm newer to OOP, but have been using PHP and Mysql as a hobby, and more heavily lately, for quite some time. I figured it was time to switch, but there just isn't that much on oop classes with mysqli and prepared statements with a possibility of multiple results (yes I've check documentation, guess I'm just not getting it or something). After quite a few hours, this is what I have. I'm not necessarily looking for a "quick fix". I really want to understand this and learn, so please explain thoroughly.
I'm using a dbconfig.php file to store my database info at root/config/dbconfig.php
in root/classes/mysqlicon.php
<?php
/*
* class MYSQLIDB
* #param Host
* #param User
* #param Password
* #param Name
*/
class MYSQLIDB
{
private $host; //MySQL Host
private $user; //MySQL User
private $pass; //MySQL Password
private $name; //MySQL Name
private $mysqli; //MySQLi Object
private $last_query; //Last Query Run
/*
* Class Constructor
* Creates a new MySQLi Object
*/
public function __construct()
{
include('./config/dbconfig.php');
$this->host = $db_host;
$this->user = $db_user;
$this->pass = $db_pass;
$this->name = $db_name;
$this->mysqli = new mysqli($this->host, $this->user, $this->pass, $this->name);
if ($mysqli->connect_errno) {
return "Failed to connect to MySQLi: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
}
private function __destruct()
{
$mysqli->close();
}
public function select($fields, $from, $where, $whereVal, $type, $orderByVal, $ASDESC, $limitVal)
{
if (is_int($whereVal))
{
$bindVal = 'i';
} else {
$bindVal = 's';
}
switch($type)
{
case "regular":
$queryPre = "SELECT " . $fields;
$querySuff = " WHERE " . $where . " = ?";
break;
case "orderByLimit":
$queryPre = "SELECT " . $fields;
$querySuff = " ORDER BY " . $orderByVal . " " . $ASDESC . " LIMIT " . $limitVal;
break;
}
//$query = "SELECT * FROM news ORDER BY id DESC LIMIT 4";
if ($stmt = $mysqli->prepare('$queryPre . " FROM " . $from . " " . $querySuff'))
{
if ($type == 'regular') {
$stmt->bind_param($bindVal, $whereVal);
}
$stmt->execute();
$stmt->bind_result($values);
$stmt->store_result();
$sr = new Statement_Result($stmt);
$stmt->fetch();
// call by this style printf("ID: %d\n", $sr->Get('id') );
//$stmt->fetch();
//$stmt->close();
//return $value;
printf("ID: %d\n", $sr->Get_Array() );
} else return null;
}
//use to call $db = new MYSQLI('localhost', 'root', '', 'blog');
/*
* Function Select
* #param fields
* #param from
* #param where
* #returns Query Result Set
function select($fields, $from, $where, $orderBy, $ASDESC, $limit, varNamesSent)
{
if ($orderBy == null &&)
$query = "SELECT " . $fields . " FROM `" . $from . "` WHERE " . $where;
$result = $this->mysqli->query($query) or exit("Error code ({$sql->errno}): {$sql->error}");
$this->last_query = $query;
return $result;
}
/*
* Function Insert
* #param into
* #param values
* #returns boolean
*/
public function insert($into, $values)
{
$query = "INSERT INTO " . $into . " VALUES(" . $values . ")";
$this->last_query = $query;
if($this->mysqli->query($query))
{
return true;
} else {
return false;
}
}
/*
* Function Delete
* #param from
* #param where
* #returns boolean
*/
public function delete($from, $where)
{
$query = "DELETE FROM " . $from . " WHERE " . $where;
$this->last_query = $query;
if($this->mysqli->query($query))
{
return true;
} else {
return false;
}
}
}
//Hand arrays for multiple returned items from database
class Statement_Result
{
private $_bindVarsArray = array();
private $_results = array();
public function __construct(&$stmt)
{
$meta = $stmt->result_metadata();
while ($columnName = $meta->fetch_field())
$this->_bindVarsArray[] = &$this->_results[$columnName->name];
call_user_func_array(array($stmt, 'bind_result'), $this->_bindVarsArray);
$meta->close();
}
public function Get_Array()
{
return $this->_results;
}
public function Get($column_name)
{
return $this->_results[$column_name];
}
}
?>
And just as a test, I'm trying to pull all the news in my db by:
<?php
require_once('classes/mysqlicon.php');
$testing = new MYSQLIDB;
$testing->select('*','news',null,null,'orderByLimit','id','DESC',4);
?>
But what I really want is stuff that can do the equivalent of this:
<?php
/*
require('config/dbconfig.php');
$query = "SELECT * FROM news ORDER BY id DESC LIMIT 4";
if ($stmt = $mysqli->prepare($query)) {
// execute statement
$stmt->execute();
// bind result variables
$stmt->bind_result($idn, $titlen, $categoryn, $descn, $postdaten, $authorn);
// fetch values
while ($stmt->fetch()) {*/
//echo 'id: '. $id .' title: '. $title;
echo "<table border='0'>";
$shortDescLengthn = strlen($descn);
if ($shortDescLengthn > 106) {
$sDCutn = 106 - $shortDescLengthn;
$shortDescn = substr($descn, 0, $sDCutn);
} else {
$shortDescn = $descn;
}
echo "<h1>$titlen</h1>";
echo "<tr><td>$shortDescn...</td></tr>";
echo '<tr><td><a href="javascript:void(0);" onclick="'
. 'readMore(' . $idn . ',' . htmlspecialchars(json_encode($titlen)) . ','
. htmlspecialchars(json_encode($categoryn)) . ','
. htmlspecialchars(json_encode($descn)) . ',' . htmlspecialchars(json_encode($postdaten)) . ','
. htmlspecialchars(json_encode($authorn)) . ')">Read More</a></td></tr>';
echo "<tr><td>Written by: $authorn</td></tr>";
echo '<tr><td><img src="images/hardcore-games-newsbar-border.png" width="468px" /></td></tr>';
}
echo "</table><br />";
/* close statement */
$stmt->close();
}
/* close connection */
$mysqli->close();
?>
Again, please, please explain in detail. I'm a blockhead sometimes.
I'm assuming the error is coming from this line:
if ($stmt = $mysqli->prepare('$queryPre . " FROM " . $from . " " . $querySuff'))
$mysqli is not defined in the context of this function. You should be accessing $this->mysqli instead. The same applies to other references you have made such as here:
if ($mysqli->connect_errno)
I am using this to retrieve the database connection atm.
$db = Zend_Db_Table::getDefaultAdapter();
I do set this up in my config like this:
resources.db.adapter = pdo_mysql
resources.db.isDefaultTableAdapter = true
resources.db.params.host = localhost
resources.db.params.username = root
resources.db.params.password = password
resources.db.params.dbname = db
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class = Zend_Db_Profiler
I would like to output everything to a sql.log for example. Is this possible to apply on the default adapter? for example through the settings, so I can ignore it in production environment?
Much appriciated.
I did look at: How to enable SQL output to log file with Zend_Db? but it didn't seem to cover my issue.
/Marcus
There is an example of extending Zend_Db_Profiler so you can write the queries to /logs/db-queries.log file.
So you have to do the following:
Create My_Db_Profiler_Log class in the library folder
Add the following lines to the application.ini
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class = My_Db_Profiler_Log
Note: be aware, that the log file will become very big, very soon! So it is a good idea to log only the queries you are interested in. And this example should be considered only as a starting point in implementation of such a logging system.
Here is the code for the custom profiler class:
<?php
class My_Db_Profiler_Log extends Zend_Db_Profiler {
/**
* Zend_Log instance
* #var Zend_Log
*/
protected $_log;
/**
* counter of the total elapsed time
* #var double
*/
protected $_totalElapsedTime;
public function __construct($enabled = false) {
parent::__construct($enabled);
$this->_log = new Zend_Log();
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH . '/logs/db-queries.log');
$this->_log->addWriter($writer);
}
/**
* Intercept the query end and log the profiling data.
*
* #param integer $queryId
* #throws Zend_Db_Profiler_Exception
* #return void
*/
public function queryEnd($queryId) {
$state = parent::queryEnd($queryId);
if (!$this->getEnabled() || $state == self::IGNORED) {
return;
}
// get profile of the current query
$profile = $this->getQueryProfile($queryId);
// update totalElapsedTime counter
$this->_totalElapsedTime += $profile->getElapsedSecs();
// create the message to be logged
$message = "\r\nElapsed Secs: " . round($profile->getElapsedSecs(), 5) . "\r\n";
$message .= "Query: " . $profile->getQuery() . "\r\n";
// log the message as INFO message
$this->_log->info($message);
}
}
?>
Extend the Zend_Db_Profiler to write to an SQL.log and attach the profiler to your db adapter
<?php
class File_Profiler extends Zend_Db_Profiler {
/**
* The filename to save the queries
*
* #var string
*/
protected $_filename;
/**
* The file handle
*
* #var resource
*/
protected $_handle = null;
/**
* Class constructor
*
* #param string $filename
*/
public function __construct( $filename ) {
$this->_filename = $filename;
}
/**
* Change the profiler status. If the profiler is not enabled no
* query will be written to the destination file
*
* #param boolean $enabled
*/
public function setEnabled( $enabled ) {
parent::setEnabled($enabled);
if( $this->getEnabled() ) {
if( !$this->_handle ) {
if( !($this->_handle = #fopen($this->_filename, "a")) ) {
throw new Exception("Unable to open filename {$this->_filename} for query profiling");
}
}
}
else {
if( $this->_handle ) {
#fclose($this->_handle);
}
}
}
/**
* Intercept parent::queryEnd to catch the query and write it to a file
*
* #param int $queryId
*/
public function queryEnd($queryId) {
$state = parent::queryEnd($queryId);
if(!$this->getEnabled() || $state == self::IGNORED) {
return;
}
$profile = $this->getQueryProfile($queryId);
#fwrite($this->_handle, round($profile->getElapsedSecs(),5) . " " . $profile->getQuery() . " " . ($params=$profile->getQueryParams())?$params:null);
}
}
Haven't test it, but it should do the trick. Give it a try and let me know.
Btw you do know that you can log all queries on the mysql as well?
this will let you see sql queries to the web page , IT MIGHT BE OFF TOPIC but it helpful
I am highly recommend you to use ZF debug bar , it will give you very handy information
i am using it to see my doctrine queries , and it had support for zend db too
https://github.com/jokkedk/ZFDebug
I have a function which is wanted to execute a statement like below:
UPDATE coupon_users SET status = status | '1' WHERE id IN ('3','4')
And in coupon_users model, I wrote a method like below do to:
/**
* #param array $ids #array(3,4)
* #param array $status #1
*/
public function updateStatus(array $ids, $status)
{
$result = $this->_db->query(
"UPDATE {$this->_name} SET status = status | ? WHERE id IN (?)",
array(
$status,
$ids
)
)->execute();
return $result;
}
But the query is always:
UPDATE coupon_users SET status = status | '1' WHERE id IN ('Array')
I don't know what am I wrong here, please help me, many thanks.
According to the PDO documentation (Zend_Db uses PDO as its DB access backend):
You cannot bind multiple values to a single named parameter in, for
example, the IN() clause of an SQL statement.
So, you'll probably need to prepare a bit further your query, so that it contains as many markers as elements in the array. A possible solution could be the following:
// Compose the query
$queryToExecute = "UPDATE {$this->_name} SET status = status | ? WHERE id IN (";
$questionMarks = array();
for ($id in $ids) {
$questionMarks[] = '?';
}
$queryToExecute .= implode(',', $questionMarks);
$queryToExecute .= ')';
// $queryToExecute should have the format "UPDATE ... WHERE id IN (?,?,?,...?)"
// Execute it
$result = $this->_db->query(
$queryToExecute,
array($status, $ids)
)->execute();
Hope that helps,
try:
public function updateStatus(array $ids, $status)
{
$result = $this->_db->query(
"UPDATE {$this->_name} SET status = ? WHERE id IN (?)",
array(
$status,
implode(',',$ids)
)
)->execute();
return $result;
}
Update:
Have you tried?:
$this->_db->update($this->_name, array('status'=>$status), array('id IN (?)'=>$ids));
I haven't tested it, it also depends on what $this->_db is an instance of
http://framework.zend.com/manual/en/zend.db.adapter.html#zend.db.adapter.write.update
Try this..
public function updateStatus(array $ids, $status)
{
$inarray= implode(',',$ids);
$result = $this->_db->query(
"UPDATE {$this->_name} SET status = status | ? WHERE id IN (?)",
array(
$status,
$inarray
)
)->execute();
return $result;
}
Its working fine for me.
$existingImagesIds = array(1, 2, 3, 7);
$where = $pImgModel->getAdapter()->quoteInto("id in (?) ", $existingImagesIds);
$pImgModel->update(array('status' => '0'), $where);