I am trying to understand how to use Zend_DB in my program but I have some problems. The class below (DatabaseService) works when I pass it a simple query. However, if I pass it a query with a join clause my page just hangs and no error is returned. I cut and paste the qry in a query browser and it is valid
Any help would be great
$SQL = "select name from mytable"
$db = new DatabaseService($dbinfo)
$db ->fetchall($SQL ) // works
-----------------------------------------------------------
$SQL = "select count(*) as cnt from EndPoints join CallID on EndPoints.`CallID` = CallID.CallID where EndPoints.LastRegister >= '2010-04-21 00:00:01' and EndPoints.LastRegister <= '2010-04-21 23:59:59' "
$db = new DatabaseService($dbinfo)
$db ->fetchall($SQL ) // DOES NO WORK
class DatabaseService
{
function DatabaseService($dbinfo,$dbname="")
{
try
{
$dbConfig = array(
'host' => $this->host,
'username' => $this->username,
'password' => $password,
'dbname' => $this->dbname );
$this->db = Zend_Db::factory($this->adapter, $dbConfig);
Zend_Db_Table::setDefaultAdapter($this->db);
}
catch(Zend_Exception $e)
{
$this->error = $e->getMessage();
Helper::log($this->error);
return false;
}
}
public function connnect()
{
if($this->db !=null)
{
try
{
$this->db->getConnection();
return true;
}
catch (Zend_Exception $e)
{
$err = "FAILED ::".$e->getMessage()." <br />";
}
}
return false;
}
public function fetchall($sql)
{
$res= $this->db->fetchAll($sql);
return $res;
}
}
I can't see why that wouldn't work. It could be a bug in a particular release of ZF but as far as I can tell there are no SQL syntax errors. What you could do is Bootstrap the Zend_Db class somewhere in your system like in the index.php file just as you were doing in your DatabaseService class:
$dbConfig = array(
'host' => 'hostname',
'username' => 'username',
'password' => 'password',
'dbname' => 'dbname'
);
$db = Zend_Db::factory('mysqli', $dbConfig);
$db->setFetchMode(Zend_Db::FETCH_OBJ);
Zend_Db_Table::setDefaultAdapter($db);
And then Zend Framework should handle the connection process for you. Then instead of having a DatabaseService class you just create a model for each table you need like so:
<?php
class EndPoints extends Zend_Db_Table_Abstract
{
protected $_name = 'EndPoints';
/**
* the default is 'id'. So if your table's primary key field name is 'id' you
* will not be required to set this. If your primary key is something like
* 'EndPointsID' you MUST set this.
* #var primary key field name
*/
protected $_primary = 'EndPointsID';
}
Doing this will automagically give you access to functions such as fetchRow(), fetchAll(), find(), etc. Then you can also use Zend_Db_Table_Select for your queries which can be quite useful. Like so:
<?php
$endPointsModel = new EndPoints();
$callIdCount = $endPointsModel->getCallIdCount('2010-04-21 00:00:01', '2010-04-21 00:00:01');
Then in your EndPoints model you would create that function like so:
...
public function getCallIdCount($fromDate, $toDate)
{
$cols = array('cnt' => 'count(*)');
$select = $this->select->setIntegrityCheck(false) // this is crucial
->from($this->_name, $cols)
->join('CallID', "{$this->_name}.CallID = CallID.CallID", array())
->where("{$this->_name}.LastRegister >= ?", $fromDate)
->where("{$this->_name}.LastRegister <= ?", $toDate);
// if you need to see what the whole query will look like you can do this:
// echo $select->__toString();
return $this->fetchAll($select);
{
Related
I would like to use another database connection that queries a database using a hook in my own module called 'connector'. This hook shall take a parameter that is a query which shall be executed using the secondary database before it switches back to the primary Drupal 7-database. The problem seems to be that the query is somehow generated for the (primary) Drupal database (as it's been created before switching to the secondary db), even though the SQL itself looks fine to me. What am I doing wrong here?
Hook:
function connector_api_db($query) {
$result = [];
$database_info = array(
'database' => variable_get('connector_db_name'),
'username' => variable_get('connector_db_user'),
'password' => variable_get('connector_db_pwd'),
'host' => variable_get('connector_db_host'),
'driver' => 'mysql',
);
Database::addConnectionInfo('secondary_db', 'default', $database_info);
db_set_active('secondary_db'); // Initialize the connection
/* This actually works but I can here not use my own query as a parameter in this function. Not what I want. */
//$query = db_select('registration')->fields('registration')
//->condition('id', 46, '=');
echo $query->__toString() . "<br />"; /* Get query string */
var_dump($query->getArguments()); /* Get the arguments passed to the string */
$result = $query->execute()->fetchAssoc();
echo "<pre>";
print_r($result);
echo "</pre>";
db_set_active(); // without the paramater means set back to the default for the site
drupal_set_message(t('The queries have been made.'));
return $result;
}
Invoking the hook:
$query = db_select('registration')
->fields('registration')
->condition('id', 46, '=');
$response = module_invoke('connector', 'api_db', $query);
This results into this:
SELECT registration.* FROM {registration} registration WHERE (id = :db_condition_placeholder_0)
array(1) { [":db_condition_placeholder_0"]=> int(46) }
Additional uncaught exception thrown while handling exception.
Original
PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal.registration' doesn't exist: SELECT registration.* FROM {registration} registration WHERE (id = :db_condition_placeholder_0) ; Array ( [:db_condition_placeholder_0] => 46 ) in connector_api_db() (line 70 of /<path-to-drupal>/drupal-7.61/modules/connector/connector.main.inc).
This is not an answer, strictly speaking, but rather a suggestion.
The thing is that your hook receives a $query already built with the original database connection, this explains the errors. To be effective, db_set_active() should be called before calling the Query constructor.
Otherwise, you would have to override constructors for both the Select class and its parent class Query, or more precisely "reconstruct" the Query builder instance for Select statements (and probably others too if you need range query etc.).
Also, unless having explicit requirements to provide a dedicated hook in this situation, it is not necessary.
For example it would be simpler to do something like this :
connector.module :
/**
* Implements hook_init()
*
* Adds secondary database connection information.
*/
function connector_init() {
$database_info = array(
'database' => variable_get('connector_db_name'),
'username' => variable_get('connector_db_user'),
'password' => variable_get('connector_db_pwd'),
'host' => variable_get('connector_db_host'),
'driver' => 'mysql',
);
Database::addConnectionInfo('secondary_db', 'default', $database_info);
}
/**
* Switches from primary to secondary database and the other way round.
*/
function connector_switch_db($db_key = 'secondary_db') {
return db_set_active($db_key);
}
Example :
// Set secondary db active.
$primary_db_key = connector_switch_db();
$query = db_select('registration')
->fields('registration')
->condition('id', 46, '=');
$result = $query->execute()->fetchAssoc();
// Switch back to the primary database.
connector_switch_db($primary_db_key);
This softer approach also prevents hardcode like (...)->execute()->fetchAssoc() and let the implementation free to execute and fetch the results as needed.
For future reference, this is what I came up with (caching bits included):
function connector_init() {
foreach([
'connector_db_host',
'connector_db_name',
'connector_db_user',
'connector_db_pwd',
] as $var) {
drupal_static($var, variable_get($var, ''));
}
Database::addConnectionInfo('secondary_db', 'default', [
'host' => drupal_static('connector_db_host'),
'database' => drupal_static('connector_db_name'),
'username' => drupal_static('connector_db_user'),
'password' => drupal_static('connector_db_pwd'),
'driver' => 'mysql',
]);
}
function connector_api_db($query) {
$result = [];
$sql_query = $query->__toString();
$arguments = $query->getArguments();
if (count($arguments) > 0) {
ksort($arguments);
}
$cache_id = 'sql_' . md5($sql_query . '|' . print_r($arguments, 1));
$cache = cache_get($cache_id, 'cache_connector');
if ($cache && $cache->expire >= time()) {
$result = $cache->data;
} else {
db_set_active('secondary_db'); // Switch to secondary db */
$query_failed = FALSE;
try {
$db_result = db_query($sql_query, $arguments);
$db_result = (0 === $db_result->rowCount()) ? [] : $db_result->fetchAssoc();
} catch (\PDOException $e) {
$db_result = [];
$query_failed = TRUE;
} finally {
db_set_active(); /* switch back to default */
}
$result = (object)[
'query' => $sql_query,
'arguments' => $arguments,
'result' => (object)$db_result
];
if (!$query_failed) {
cache_set($cache_id, $result, 'cache_connector', strtotime('+10 minutes'));
}
}
return $result;
}
When I execute the method getMachineSettings I get an error:
Fatal error: Uncaught Error: Call to undefined method Doctrine\DBAL\Query\QueryBuilder::getQuery()
$data is an associative array:
$data['param'] = 'ip';
$data['value'] = '192.168.240.10';
If I replace getQuery()->getResult() with execute(), $result contains the query:
SELECT * FROM machine WHERE ip = ?
public function __construct()
{
try
{
$dbconf = parse_ini_file('.htLogin.ini');
$config = new \Doctrine\DBAL\Configuration();
$connectionParams = array
(
'dbname' => $dbconf['infoDb'],
'user' => $dbconf['infoLogin'],
'password' => $dbconf['infoPw'],
'host' => $dbconf['infoHost'],
'driver' => 'pdo_mysql',
'charset' => 'utf8',
'driverOptions' => array
(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
)
);
$this->mysql = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config);
}
catch(PDOException $e)
{
echo $e -> getMessage();
}
public function getMachineSettings($data)
{
$qb = $this->mysql->createQueryBuilder();
$qb->SELECT('*')
->FROM('`machine`')
->WHERE($data['param'] . ' = :value');
$qb->setParameters(
array
(
':value' => $data['value']
)
);
$results = $qb->getQuery()->getResult();
var_dump($result);
return $result;
}
Do you have any idea why the method getQuery() is not recognised?
Just do
$results = $qb->execute()->fetchAll();
Ignore the below - it assumes that your using the Doctrine ORM, which your not
The issue your having is that the QueryBuilder object your working with isn't the Doctrine ORM QueryBuilder - its the DBAL QueryBuilder.
You need to use the createQueryBuilder function from the EntityManager.
/** #var Doctrine\ORM\QueryBuilder $qb */
$qb = $this->entityManager->createQueryBuilder();
Then you can use the select / from etc methods and to get the result of the query you can run
$qb->getQuery()->getResult()
If I was to rewrite your function I would write it like this
public function getMachineSettings(string $field, string $value)
{
$qb = $this->entityManager->createQueryBuilder();
$qb->select('m')
->from('machine')
->where($field.' = :value');
$qb->setParameter('value', $value);
$results = $qb->getQuery()->getResult();
var_dump($result);
return $result;
}
Then you know that the function requires 2 parameters to function, passing an array doesn't let you immediately see what the function requires
So I know that in order for a.class.php to be used within b.class.php I would need to have the a class included in the b class file. My question is this.
I have two classes files at the moment within my website platform. db.class.php and account.class.php
db.class.php
<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/settings.php');
class db extends pdo{
//Website Variables
public $sitedb = '';
public $siteconfig;
public $sitesettings = array(
'host' => SITEHOST,
'database' => SITEDB,
'username' => SITEUSER,
'password' => SITEPASS,
);
public $realmdb = '';
public $realmconfig;
public $realmsettings = array(
'host' => REALMHOST,
'database' => REALMDB,
'username' => REALMUSER,
'password' => REALMPASS,
);
public function __construct(){
$this->sitedb = new PDO(
"mysql:host={$this->sitesettings['host']};" .
"dbname={$this->sitesettings['database']};" .
"charset=utf8",
"{$this->sitesettings['username']}",
"{$this->sitesettings['password']}"
);
$this->realmdb = new PDO(
"mysql:host={$this->realmsettings['host']};" .
"dbname={$this->realmsettings['database']};" .
"charset=utf8",
"{$this->realmsettings['username']}",
"{$this->realmsettings['password']}"
);
$this->sitedb->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$this->realmdb->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
$db = new db();
account.class.php
<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/settings.php');
class account extends db {
public function Login() {
$query = <<<SQL
SELECT id
FROM profile
WHERE password = :password
SQL;
$resource = $db->sitedb->prepare ( $query );
$resource->execute( array(
':password' => sha1(strtoupper($_POST['email'].':'.$_POST['password'],
));
$row_count = $resource->rowCount();
echo $row_count;
}
}
$account = new account();
This current format tells me that db cannot be redefined, however if I remove the requirement of including the settings file which has
foreach (glob("functions.d/*.class.php") as $class)
{
include $class;
}
It then tells me that class db cannot be found. What way can I work around to have this work correctly?
Turns out the best way to accomplish this was to keep the includes correctly and have all classes within the same file and within my include page utilizing classes have global $db or global $account depending on what I class function I was calling on.
Trying to get my head around using classes and using it as an object.
Im am converting a number of functions to use one single class. This class is for customers.
What I want to do is create the new customer object:
$customer = new Customer($customer_info);
My class currently looks:
class Customer {
private $customer_info;
private $mysqli;
function __construct(Array $customer_info, Mysqli $mysqli) {
$this->mysqli = $mysqli;
$this->customer_info = $customer_info;
$result = $mysqli->query("SELECT * FROM `customers` WHERE id = 33");
$customerArray = array();
while ($customer = $result->fetch_object()) {
$customerArray[] = array(
'id' => $customer->id,
'first_name' => $customer->first_name,
'last_name' => $customer->last_name,
'email' => $customer->email
);
}
$customer_ID = $customerArray->id;
//return var_dump($customerArray);
}
function getCustomerId() {
return "id:$customer_ID";
}
}
When I create a new customer object, I am passing in the information and then I want to either insert a row or update a current one with the new customer information array thats passed in.
That is fine, I have done that else where in another function.
However, after this I want to be able to use a number of functions on that object.
So example:
$customer = new Customer($customer_info); // create / update
$customer = customer->getCustomerID();
I would like that getCustomerID function to simply return the ID of the database row that was created / updated when the new object was initiated. In my class currently you can see how I tried to set a value and then use that in the function... but im sure thats not how it works.
I have used Laravel and want it to work similar to the way the classes work there.
Thanks.
you have to create Class variables
class Customer {
private $customer_info;
private $mysqli;
public $customer_ID;
function __construct(Array $customer_info, Mysqli $mysqli) {
$this->mysqli = $mysqli;
$this->customer_info = $customer_info;
$result = $mysqli->query("SELECT * FROM `customers` WHERE id = 33");
$customerArray = array();
while ($customer = $result->fetch_object()) {
$customerArray[] = array(
'id' => $customer->id,
'first_name' => $customer->first_name,
'last_name' => $customer->last_name,
'email' => $customer->email
);
}
$this->customer_ID = $customerArray->id;
//return var_dump($customerArray);
}
function getCustomerId() {
return $this->customer_ID;
}
}
I have written code to search for the First name and last name and display those particular records if available and perform actions like update and delete on those records , I have written the following code to search , please tell me what is the correct approach to build a search controller .I am getting the following error :
Message: Method "select" does not exist and was not trapped in __call()
The code I have written in the controller is :
public function searchAction($params)
{
$query = $this->select()
->from(
array('EMPLOYEES'),
array('FIRST_NAME','LAST_NAME','SALARY','HIREDATE')
);
$query = $this->_makeParams($query,$params);
return $this->fetchAll($query);
}
private function _makeParams($query, $params)
{
$firstname = isset($params['firstname']) ? trim($params['firstname']) : '';
$lastname = isset($params['lastname']) ? trim($params['lastname']) : '';
$salary = isset($params['salary']) ? trim($params['salary']) : '';
$hiredate= isset($params['hiredate']) ? trim($params['hiredate']) : '';
if($firstname!='')
{
$name = '%'.$this->quote($firstname).'%';//quote is my own function
$query->where("EMPLOYEES.FIRST_NAME LIKE '?'",$firstname);
}
if($lastname!='')
{
$query->where("EMPLOYEES.LAST_NAME =?",$lastname);
}
if($salary!='')
{
$query->where("EMPLOYEES.SALARY=?",$salary);
}
if($hiredate!='')
{
$query->where("EMPLOYEES.HIRE_DATE=?",$hiredate);
}
}
your error comes from the fact that your calling select() against the controller object instead of the database object:
public function searchAction($params)
{
//$this in this context is a Zend_Controller_Action object
//you need to query against your database object.
$db = Zend_Db_Table::getDefaultAdapter();
$query = $db->select()
->from(
array('EMPLOYEES'),
array('FIRST_NAME','LAST_NAME','SALARY','HIREDATE')
);
$query = $this->_makeParams($query,$params);
//again make sure to query against the database object
return $db->fetchAll($query);
}
if you don't have a database object created in your bootstrap.php or application.ini you can create one with the Zend_Db:
$db = Zend_Db::factory('Pdo_Mysql', array(
'host' => '127.0.0.1',
'username' => 'webuser',
'password' => 'xxxxxxxx',
'dbname' => 'test'
));
public function searchAction($params)
{
$db = Zend_Registry :: get('db');
$select = $db->select();
$query = $select->from(
array('EMPLOYEES'),
array('FIRST_NAME','LAST_NAME','SALARY','HIREDATE')
);
$query = $this->_makeParams($query,$params);
return $this->fetchAll($query);
}
You are getting the error because you have not created the object of Zend registry.So copy the function and replace your code