How to paginate a native query in Doctrine 2? - php

Doctrine 2 has the Doctrine\ORM\Tools\Pagination\Paginator class which can be used to paginate normal DQL queries.
However if I pass it a native query, I get this error:
Catchable fatal error: Argument 1 passed to Doctrine\ORM\Tools\Pagination\Paginator::cloneQuery() must be an instance of Doctrine\ORM\Query, instance of Doctrine\ORM\NativeQuery given
I've tried removing the type-hinting from the paginator class in the cloneQuery method, but this just gives further errors because other bits of the paginator class expect methods found in Query that aren't in NativeQuery.
Is there any easy way of paginating the native queries without needing to build a new paginator class or fetching every row from the database into an array?

I made my own paginator adapter class compatible with Zend_Paginator.
Probably won't be the most flexible since it relies on there being a " FROM " near the start of the query (see the count() method) but it's a relatively quick and easy fix.
/**
* Paginate native doctrine 2 queries
*/
class NativePaginator implements Zend_Paginator_Adapter_Interface
{
/**
* #var Doctrine\ORM\NativeQuery
*/
protected $query;
protected $count;
/**
* #param Doctrine\ORM\NativeQuery $query
*/
public function __construct($query)
{
$this->query = $query;
}
/**
* Returns the total number of rows in the result set.
*
* #return integer
*/
public function count()
{
if(!$this->count)
{
//change to a count query by changing the bit before the FROM
$sql = explode(' FROM ', $this->query->getSql());
$sql[0] = 'SELECT COUNT(*)';
$sql = implode(' FROM ', $sql);
$db = $this->query->getEntityManager()->getConnection();
$this->count = (int) $db->fetchColumn($sql, $this->query->getParameters());
}
return $this->count;
}
/**
* Returns an collection of items for a page.
*
* #param integer $offset Page offset
* #param integer $itemCountPerPage Number of items per page
* #return array
*/
public function getItems($offset, $itemCountPerPage)
{
$cloneQuery = clone $this->query;
$cloneQuery->setParameters($this->query->getParameters(), $this->query->getParameterTypes());
foreach($this->query->getHints() as $name => $value)
{
$cloneQuery->setHint($name, $value);
}
//add on limit and offset
$sql = $cloneQuery->getSQL();
$sql .= " LIMIT $itemCountPerPage OFFSET $offset";
$cloneQuery->setSQL($sql);
return $cloneQuery->getResult();
}
}

public function countTotalRecords($query, string $primaryKey = '*'): int
{
if ($query instanceof QueryBuilder) {
$paginator = new Paginator($query->getQuery());
return count($paginator);
} else if ($query instanceof NativeQuery) {
$rsm = new ResultSetMappingBuilder($query->getEntityManager());
$rsm->addScalarResult('count', 'count');
$sqlCount = "select count(".$primaryKey.") as count from (" . $query->getSQL() . ") as item";
$count = $query->getEntityManager()->createNativeQuery($sqlCount, $rsm);
if ($query->getParameter('limit')) {
$query->setParameter('limit', null);
}
$count->setParameters($query->getParameters());
return (int)$count->getSingleScalarResult();
}
return 0;
}

If you have a dbal query builder that you have constructed with
$yourDbalQueryBuilder = $connection->createQueryBuilder();
then you can use:
$yourDbalQueryBuilder->setFirstResult(0)
->setMaxResults(100000000)
->execute()
->rowCount();

Related

Fatal error while trying to call a method in constructor

I am trying to write my own MVC Framework using this tutorial. Everything worked fine and i completed the tutorial with no issues.
However later i decided to use PDO (PHP Data objects) instead of mysql() functions for Database Operations. So i modified My sqlQuery.php file to use PDO istead of mysql functions.
sqlQuery.php
<?php
class SQLQuery {
private $_dbHandle;
private $_result;
/**
* Connects to database
*
* #param $address
* #param $account
* #param $pwd
* #param $name
*/
function connect($address, $account, $pwd, $name) {
try{
$this->_dbHandle = new PDO("mysql:host=$address;dbname=$name", $account, $pwd);
}catch (PDOException $e) {
die($e->getCode() . " : " . $e->getMessage());
}
}
/** Disconnects from database **/
function disconnect() {
$this->_dbHandle = null;
}
function get($whereClause = array()) {
$query = "select * from $this->_table";
if(is_array($whereClause) && count($whereClause)){
$query .= " where ";
foreach($whereClause as $column=>$value)
$query .= " $column = $value";
}else if(is_int($whereClause)){
$query .= " where id = $whereClause ";
}
return $this->query($query);
}
/**
* Custom SQL Query
*
* #param $query
* #return array|bool
*/
function query($query) {
$this->_result = $this->_dbHandle->query($query);
$this->_result->setFetchMode(PDO::FETCH_CLASS, $this->_model);
if (preg_match("/select/i",$query)) {
$result = array();
$numOfFields = $this->_result->rowCount();
if($numOfFields > 1){
while($result[] = $this->_result->fetch()){
}
}else{
$result = $this->_result->fetch();
}
return $result;
}
return true;
}
}
Now when In my Controller when i print $this->Item->get() I get all the results in my database as Objects of Items Model and $this->Item->get(2) gives me the item object with id=2 as expected.
However, I don't like the idea that my API needs to be to call an additional method get() to get the objects of the Items Model, Instead it makes more sense that when a Items Model is initialized i get the desired object and so my API can be $this->item->mycolumnName.
To achieve this i tried to move the get() call in the Models constructor like this :
model.php
<?php
class Model extends SQLQuery{
protected $_model;
function __construct() {
$this->connect(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME);
$this->_model = get_class($this);
$this->_table = strtolower($this->_model)."s";
$this->get();
}
function __destruct() {
}
}
However this gives me a Fatal error
Fatal error: Maximum function nesting level of '256' reached, aborting! in /var/www/html/FitternityAssignment/library/sqlquery.php on line 59
I don't know what i have done wrong.
The issue is line 59: while($result[] = $this->_result->fetch()){
function query($query) {
$this->_result = $this->_dbHandle->query($query);
$this->_result->setFetchMode(PDO::FETCH_CLASS, $this->_model);
if (preg_match("/select/i",$query)) {
$result = array();
$numOfFields = $this->_result->rowCount();
if($numOfFields > 1){
while($result[] = $this->_result->fetch()){
}
}else{
$result = $this->_result->fetch();
}
return $result;
}
return true;
}
Let's narrow in on the issue:
while($result[] = $this->_result->fetch()){
}
Infinite loop.
When fetch() returns something, then it is added to $result, because $result is an array, and then of course it evaluates to true because the size of $result grows each time a fetched result is added.
$results = [];
while($row = $this->_result->fetch()){
$results[] = $row; // or whatever you need to do with the row
}
Might work. I say might because it depends on what $this->_result->fetch() returns when no results are left. I'll assume false or null, in which case the above will work, because when there are no more results then fetch() will return null or false and $row will then evaluate to false.

How do I view a SQL Statement in Phalconphp?

I want to view the SQL statement that is about to be executed below :
<?php
//Deleting existing robot
$success = $connection->delete(
"robots",
"id = 101"
);
//Next SQL sentence is generated
DELETE FROM `robots` WHERE `id` = 101
How can I add some kind of listener or just plain var_dump the select query that is about to generated by the $connection->delete
Thanks
The way I settled on is to use a logger class and the event system: Phalcon Events Manager
You create a class that extends the logger adapter
<?php
namespace PhalconX\Logger\Adapter;
/**
* Basic Array based Logging for debugging Phalcon Operations
* #package PhalconX\Logger\Adapter
*/
class Basic extends \Phalcon\Logger\Adapter
{
private $data = array();
/**
* Add a statement to the log
* #param string $statement
* #param null $type
* #param array $params
* #return $this|\Phalcon\Logger\Adapter
*/
public function log($statement, $type=null, array $params=null)
{
$this->data[] = array('sql'=>$statement, 'type'=>$type, 'params'=>$params); // array('sql'=>$statement, 'type'=>$type);
return $this;
}
/**
* return the log
* #return array
*/
public function getLog(){
return $this->data;
}
/**
* Required function for the interface, unused
* #param $message
* #param $type
* #param $time
* #param $context
*/
public function logInternal($message, $type, $time, $context){
}
/**
* Required function for the interface, unused
*/
public function getFormatter(){
}
/**
* Required function for the interface, unused
*/
public function close(){
}
}
and then attach it to your database, and plumb in the events by type
$eventsManager = new \Phalcon\Events\Manager();
$logger = new \PhalconX\Logger\Adapter\Basic();
$profiler = $phalconDi->getProfiler();
//Listen all the database events
/** #var $event \Phalcon\Events\Event */
/** #var $phalconConnection \Phalcon\Db\Adapter\Pdo\Mysql */
$eventsManager->attach('db', function($event, $phalconConnection) use ($logger, $profiler) {
if ($event->getType() == 'beforeQuery') {
$profiler->startProfile($phalconConnection->getSQLStatement());
$logger->log($phalconConnection->getSQLStatement(), \Phalcon\Logger::INFO, $phalconConnection->getSQLVariables());
}
if ($event->getType() == 'afterQuery') {
$profiler->stopProfile();
}
});
This presumes you have a 'db' key in your dependency injector.
My logger just stores the queries in an array so I can output them at the bottom of my page.
My trick to factor a closest to real SQL statement out of those prepared ones:
function statement2sql($connection) {
$stmt = $connection->getSQLStatement();
foreach ( $connection->getSQLVariables() as $k => $v ) {
// replaces :p1, .. :p11 .. and defined binds with binded values
$stmt = preg_replace('/:' . $k . '([^A-Za-z0-9])/', '\'' . $v . '\'$1', $stmt);
}
return $stmt;
}
Defined as method or function, you can push its result to profiler as in accepted answer:
$eventsManager->attach('db:beforeQuery', function($event, $connection) {
$profiler->startProfile(statement2sql($connection));
}
$eventsManager->attach('db:afterQuery', function($event, $connection) {
$profiler->stopProfile();
}
or store in other way - using logger or other debugging class.
I've had good luck wrapping my SQL execute call in a try/catch, then printing the exception. Any error message returned by MySQL is in the exception's message, which will contain the raw query.
function get_item_by_id ($db_connection, $item_id) {
try {
$stmt = 'SELECT * FROM inventory WHERE id=:id';
$prepared_stmt = $db_connection->prepare ($stmt);
$result = $db_connection->executePrepared ($prepared_stmt,
array (
"id" => $item_id
),
array (
"id" => Column::BIND_PARAM_INT
)
);
$result->setFetchMode (Phalcon\Db::FETCH_OBJ);
$item_arr = $result->fetchAll ();
return $item_arr;
}
catch (Exception $e) {
print_r ($e->getMessage());
}
}
Another option, my personal preference, is to look at the situation from the perspective of the database. Most SQL databases allow you to set a trigger for certain events (in your case, DELETE), and generate a log entry with the full text of the incoming request.
Reference: https://stackoverflow.com/a/10671410/1504367.

Undefined method mysqli_stmt::get_result() [duplicate]

This question already has answers here:
Call to undefined method mysqli_stmt::get_result
(10 answers)
Closed 5 years ago.
On my server i get this error
Fatal error: Call to undefined method mysqli_stmt::get_result() in /var/www/virtual/fcb/htdocs/library/mysqlidbclass.php on line 144
And I am having a wrapper like this:
<?php
/* https://github.com/aaron-lord/mysqli */
class mysqlidb {
/**
* Set up the database connection
*/
public function __construct($server,$user,$password,$db){
$this->connection = $this->connect($server, $user, $password, $db, true);
}
/**
* Connect to the database, with or without a persistant connection
* #param String $host Mysql server hostname
* #param String $user Mysql username
* #param String $pass Mysql password
* #param String $db Database to use
* #param boolean $persistant Create a persistant connection
* #return Object Mysqli
*/
private function connect($host, $user, $pass, $db, $persistant = true){
$host = $persistant === true ? 'p:'.$host : $host;
$mysqli = new mysqli($host, $user, $pass, $db);
if($mysqli->connect_error)
throw new Exception('Connection Error: '.$mysqli->connect_error);
$mysqli->set_charset('utf8');
return $mysqli;
}
/**
* Execute an SQL statement for execution.
* #param String $sql An SQL query
* #return Object $this
*/
public function query($sql){
$this->num_rows = 0;
$this->affected_rows = -1;
if(is_object($this->connection)){
$stmt = $this->connection->query($sql);
# Affected rows has to go here for query :o
$this->affected_rows = $this->connection->affected_rows;
$this->stmt = $stmt;
return $this;
}
else {
throw new Exception;
}
}
/**
* Prepare an SQL statement
* #param String $sql An SQL query
* #return Object $this
*/
public function prepare($sql){
unset($this->stmt);
$this->num_rows = 0;
$this->affected_rows = -1;
if(is_object($this->connection)){
# Ready the stmt
$this->stmt = $this->connection->prepare($sql);
if (false===$this->stmt)
{
print('prepare failed: ' . htmlspecialchars($this->connection->error)."<br />");
}
return $this;
}
else {
throw new Exception();
}
}
public function multi_query(){ }
/**
* Escapes the arguments passed in and executes a prepared Query.
* #param Mixed $var The value to be bound to the first SQL ?
* #param Mixed $... Each subsequent value to be bound to ?
* #return Object $this
*/
public function execute(){
if(is_object($this->connection) && is_object($this->stmt)){
# Ready the params
if(count($args = func_get_args()) > 0){
$types = array();
$params = array();
foreach($args as $arg){
$types[] = is_int($arg) ? 'i' : (is_float($arg) ? 'd' : 's');
$params[] = $arg;
}
# Stick the types at the start of the params
array_unshift($params, implode($types));
# Call bind_param (avoiding the pass_by_reference crap)
call_user_func_array(
array($this->stmt, 'bind_param'),
$this->_pass_by_reference($params)
);
}
if($this->stmt->execute()){
# Affected rows to be run after execute for prepares
$this->affected_rows = $this->stmt->affected_rows;
return $this;
}
else {
throw new Exception($this->connection->error);
}
}
else {
throw new Exception;
}
}
/**
* Fetch all results as an array, the type of array depend on the $method passed through.
* #param string $method Optional perameter to indicate what type of array to return.'assoc' is the default and returns an accociative array, 'row' returns a numeric array and 'array' returns an array of both.
* #param boolean $close_stmt Optional perameter to indicate if the statement should be destroyed after execution.
* #return Array Array of database results
*/
public function results($method = 'assoc', $close_stmt = false){
if(is_object($this->stmt)){
$stmt_type = get_class($this->stmt);
# Grab the result prepare() & query()
switch($stmt_type){
case 'mysqli_stmt':
$result = $this->stmt->get_result();
$close_result = 'close';
break;
case 'mysqli_result':
$result = $this->stmt;
$close_result = 'free';
break;
default:
throw new Exception;
}
$this->num_rows = $result->num_rows;
# Set the results type
switch($method) {
case 'assoc':
$method = 'fetch_assoc';
break;
case 'row':
//return 'fetch_row';
$method = 'fetch_row';
break;
default:
$method = 'fetch_array';
break;
}
$results = array();
while($row = $result->$method()){
$results[] = $row;
}
$result->$close_result();
return $results;
}
else {
throw new Exception;
}
}
/**
* Turns off auto-committing database modifications, starting a new transaction.
* #return bool Dependant on the how successful the autocommit() call was
*/
public function start_transaction(){
if(is_object($this->connection)){
return $this->connection->autocommit(false);
}
}
/**
* Commits the current transaction and turns auto-committing database modifications on, ending transactions.
* #return bool Dependant on the how successful the autocommit() call was
*/
public function commit(){
if(is_object($this->connection)){
# Commit!
if($this->connection->commit()){
return $this->connection->autocommit(true);
}
else {
$this->connection->autocommit(true);
throw new Exception;
}
}
}
/**
* Rolls back current transaction and turns auto-committing database modifications on, ending transactions.
* #return bool Dependant on the how successful the autocommit() call was
*/
public function rollback(){
if(is_object($this->connection)){
# Commit!
if($this->connection->rollback()){
return $this->connection->autocommit(true);
}
else {
$this->connection->autocommit(true);
throw new Exception;
}
}
}
/**
* Return the number of rows in statements result set.
* #return integer The number of rows
*/
public function num_rows(){
return $this->num_rows;
}
/**
* Gets the number of affected rows in a previous MySQL operation.
* #return integer The affected rows
*/
public function affected_rows(){
return $this->affected_rows;
}
/**
* Returns the auto generated id used in the last query.
* #return integer The last auto generated id
*/
public function insert_id(){
if(is_object($this->connection)){
return $this->connection->insert_id;
}
}
/**
* Fixes the call_user_func_array & bind_param pass by reference crap.
* #param array $arr The array to be referenced
* #return array A referenced array
*/
private function _pass_by_reference(&$arr){
$refs = array();
foreach($arr as $key => $value){
$refs[$key] = &$arr[$key];
}
return $refs;
}
}
?>
Is there any way to use another function so that I won't have to rewrite whole app? Please tell me if any.
Please read the user notes for this method:
http://php.net/manual/en/mysqli-stmt.get-result.php
It requires the mysqlnd driver. if it isn't installed on your webspace you will have to work with BIND_RESULT & FETCH
http://www.php.net/manual/en/mysqli-stmt.bind-result.php
http://www.php.net/manual/en/mysqli-stmt.fetch.php
Extracted from here
The reason for this error is that your server doesn't have the mysqlnd driver driver installed. (See here.) If you have admin privileges you could install it yourself, but there is also an easier way:
I have written two simple functions that give the same functionality as $stmt->get_result();, but they don't require the mysqlnd driver.
You simply replace
$result = $stmt->get_result(); with $fields = bindAll($stmt);
and
$row= $stmt->get_result(); with $row = fetchRowAssoc($stmt, $fields);.
(To get the numbers of returned rows you can use $stmt->num_rows.)
You just have to place these two functions I have written somewhere in your PHP Script. (for example right at the bottom)
function bindAll($stmt) {
$meta = $stmt->result_metadata();
$fields = array();
$fieldRefs = array();
while ($field = $meta->fetch_field())
{
$fields[$field->name] = "";
$fieldRefs[] = &$fields[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $fieldRefs);
$stmt->store_result();
//var_dump($fields);
return $fields;
}
function fetchRowAssoc($stmt, &$fields) {
if ($stmt->fetch()) {
return $fields;
}
return false;
}
How it works:
My code uses the $stmt->result_metadata(); function to figure out how many and which fields are returned and then automatically binds the fetched results to pre-created references. Works like a charm!
Also posted here.

Function arguments, return all records from database table

I have a script that loops through and returns all records in the database table, code below.
PHP:
for($i=0;$i<$group_layer_row;$i++){
$my_layer_string="MyMap_".mb_convert_encoding(mssql_result ($rs_group_layer, $i, 0),"UTF-8","SJIS")."_".mb_convert_encoding(mssql_result ($rs_group_layer, $i, 1),"UTF-8","SJIS");
echo "var ".$my_layer_string.";\n";
}
What I am trying to do is turn this into an argument. Somewhat like this(this is an example, please don’t judge).
PHP:
function getLayers(){
$my_layer_string="MyMap_".mb_convert_encoding(mssql_result ($rs_group_layer, $i, 0),"UTF-8","SJIS")."_".mb_convert_encoding(mssql_result ($rs_group_layer, $i, 1),"UTF-8","SJIS");
$layers="var ".$my_layer_string.";\n";
echo $layers;
}
for($i=0;$i<$group_layer_row;$i++){
getLayers();
}
Any help on this would be very appreciated.
For reference I am including the sql query
$sql= "SELECT * FROM m_group_layer WHERE group_id=\"".$_SESSION["group_id"]."\" ORDER BY display_order";
$rs_group_layer= mssql_query ($sql, $con);
$group_layer_row =mssql_num_rows($rs_group_layer);
EDIT: This is almost the exact same loop just with different output.
for($i=0;$i<$group_layer_row;$i++){
$my_layer_string="MyMap_".mb_convert_encoding(mssql_result ($rs_group_layer, $i, 0),"UTF-8","SJIS")."_".mb_convert_encoding(mssql_result ($rs_group_layer, $i, 1),"UTF-8","SJIS");
echo "".$my_layer_string." = new OpenLayers.Layer.WMS( \"".$my_layer_string."\",\"http://192.0.0.0/cgi-bin/mapserv.exe?map=C:/ms4w/Apache/htdocs/mapserver/data/toyama/toyama_mymap.map&service=WMS&SRS=EPSG:2449&VERSION=1.1.1&format=image/PNG&layers=".$my_layer_string."\", {'layers': '".$my_layer_string."'}, {isBaseLayer: false, visibility: false,opacity:0.5,alpha:true});
map.addLayer(".$my_layer_string.");\n";
}
If I understand you correctly, this is a prime candidate for creating an object. As for making a function out of it, I think it's significantly cleaner to process the db resultset in a for loop. I don't see much benefit to creating a function (as passing the db result back and forth to a function inside a loop is very inefficient). Perhaps you might clarify your reasoning for wanting a function or what you are looking to accomplish?
BUT, if you really wanted to make a function out of it it would pretty much look how you outlined it ...
function getLayer($result_set, $row) {
$str = "MyMap_" . mb_convert_encoding(mssql_result($result_set, $i, 0),"UTF-8","SJIS")
$str .= "_".mb_convert_encoding(mssql_result($result_set, $i, 1),"UTF-8","SJIS");
return "var MyMap_$str;\n";
}
// $con = ...
$sql = "SELECT * FROM m_group_layer WHERE group_id=\"".$_SESSION["group_id"]."\" ORDER BY display_order";
$result = mssql_query ($sql, $con);
$row_count = mssql_num_rows($result);
for($i=0; $i<$row_count; $i++){
echo getLayer($result, $i);
}
UPDATE -- CLASS EXAMPLE
Okay, hopefully this doesn't scare you away. Everyone was afraid of OOP at some point. The important thing is to keep working at it and eventually you'll be like, 'OMG I <3 OOP LIKE GAGA LOVES HER LITTLE MONSTERS!!!'
I tried to document as much as possible. There's only so much you can explain without teaching a semester course :) How to use it is at the bottom of the code.
<?php
/**
* Retrieves layers from the db and provides methods for outputting
*/
class LayerMaker
{
/**
* Our MSSQL database connection
* #var MSSQL connection resource
*/
protected $db_conn;
/**
* Our array of records from the DB
* #var array
*/
protected $records;
/**
* Constructor function
*
* Called when you first instantiate the object. If you specify
* the db_conn, it will go ahead and retrieve the records as
* soon as the object is created.
*
* #param MSSQL connection resource $db_conn
*
* #return void
*/
public function __construct($db_conn=NULL)
{
if ($db_conn) {
$this->set_db_conn($db_conn);
$this->records = $this->query_db();
}
}
/**
* Setter function for protected $db_conn property
*
* You could just as easily create a method in the object
* to create the db connection, but for testing reasons that
* you likely don't care about it's better to inject the
* db connection into our object using a setter function like this.
*
* #param MSSQL link identifier $db_conn
*/
public function set_db_conn($db_conn)
{
$this->db_conn = $db_conn
}
/**
* How we get the records from the database into our object's $results property
*
* #return MSSQL record set on success or FALSE if no db connection is set
*/
protected function query_db()
{
// make sure we've set a database connection to use
// query the db and return the results
$sql = 'SELECT * FROM m_group_layer WHERE group_id="' .
$_SESSION["group_id"] . '" ORDER BY display_order';
return mssql_query($sql, $this->db_conn);
}
/**
* A function to get a count of the rows in our result set
*
* #return int Rows in the result property
*/
public function count_result_rows()
{
if ($this->records) {
return mssql_num_rows($this->records);
}
return 0;
}
/**
* Wrapper for mb_convert_encoding function
*
* #return string
*/
protected function layer_builder($row)
{
$str0 = mb_convert_encoding(mssql_result($this->records, $row, 0),"UTF-8","SJIS")
$str1 = mb_convert_encoding(mssql_result($this->records, $row, 1),"UTF-8","SJIS");
return "var MyMap_$str0_$str1";
}
/**
* Finally, build our layers!
*
* #param int $row Result set row number
*
* #return mixed Layer string if $row specified or Array of all layer strings
* if no specific row requested
*/
public function get_layers($row=NULL)
{
if ($row) {
// if we want one specific row ...
return $this->layer_builder($row);
} else {
// otherwise, give us back an array of all the rows
$layers = array();
for($i=0; $i<$this->count_result_rows(); $i++){
$layers[] = $this->layer_builder($i
}
return $layers;
}
}
/**
* Getter function for protected $records property
*
* Useful because you might want access to the resultset
* outside of the object context.
*
* #return array MSSQL record set
*/
public function get_records()
{
return $this->records;
}
}
// Now this is how you could use it
$conn = (however you retrieve a db connection);
$layer_obj = new LayerMaker($conn);
$layers = $layer_obj->get_layers();
print_r($layers);
?>

Brackets in Doctrine query

hi
i have to nest some or / and conditions
but i need brackets in my sql statement to do it in the right order
but how do you make this
is should be in this form
(... OR ...) AND ...
thnx
According to this blog post, "Solving the Doctrine Parenthesis Problem", you need to do a $query->where("(ConditionA OR ConditionB) AND ConditionC");
That may look like:
Doctrine_Query::create()
->from(...)
->where('A = ? OR B = ?', array(valA, valB))
->andWhere('C = ?', valC);
The poster does, however, provide a more generic solution, whereParenWrap(), by extending Doctrine_Query:
DQ::create()
->from(...)
->where('A = ?', valA)
->orWhere('B = ?', valB)
->whereParenWrap()
->andWhere('C = ?', valC);
Addon for the answer: Brackets in Doctrine query
/*
* This is a simple short-hand wrapper for Doctrine_Query. It provides
* a shorter class name and a few additional functions.
*/
class DQ extends Doctrine_Query
{
/**
* Returns a DQ object to get started
*
* #return DQ
*/
public static function create($conn = null, $class = null) {
return new DQ($conn, $class);
}
/**
* This function will wrap the current dql where statement
* in parenthesis. This allows more complex dql statements
* It can be called multiple times during the creation of the dql
* where clause.
*
* #return $this
*/
public function whereParenWrap() {
$where = $this->_dqlParts['where'];
if (count($where) > 0) {
array_unshift($where, '(');
array_push($where, ')');
$this->_dqlParts['where'] = $where;
}
return $this;
}
}
?>
Slightly modified.
Taken from: https://gist.github.com/888386/634c51993aaf16565690be10da7b8f13a227020a
Now you can use Expr::orX()
/** To combine all the OR conditions in one parenthesis, we will collect the conditions in one array */
$orX = [];
foreach ($dto->userNameSearchPhrases as $key => $userNameSearchPhrase) {
$orX[] = $qb->expr()->like('us.username', ':userNameSearchPhrase' . $key);
$qb->setParameter('userNameSearchPhrase' . $key, $userNameSearchPhrase);
}
/** To pass parameters to the Expr::orX() method from an array, use ReflectionMethod */
$reflectionMethod = new \ReflectionMethod(\Doctrine\ORM\Query\Expr::class, 'orX');
$orXObject = $reflectionMethod->invokeArgs($qb->expr(), $orX);
$qb->andWhere($orXObject);

Categories