I'm trying to use the MySQL FIELD function in an order by clause in a query. I'm assuming that Doctrine 2 doesn't support the FIELD function out of the box - is that true? If so, how can I use it? Will I have to turn my whole query into a native query? Is there a Doctrine 2 extension that adds this functionality?
Jeremy Hicks, thanks for your extension.
I didn`t know how to connect your function to doctrine, but finally i find answer.
$doctrineConfig = $this->em->getConfiguration();
$doctrineConfig->addCustomStringFunction('FIELD', 'DoctrineExtensions\Query\Mysql\Field');
I need FIELD function to order my Entities that i select by IN expression. But you can use this function only in SELECT, WHERE, BETWEEN clause, not in ORDER BY.
Solution:
$qb
->select("r, field(r.id, " . implode(", ", $ids) . ") as HIDDEN field")
->from("Entities\Round", "r")
->where($qb->expr()->in("r.id", $ids))
->orderBy("field");
To avoid adding field alias into your result row you need put HIDDEN keyword. So this how to be able order values in IN expression in Doctrine 2.2.
You could add support for the FIELD() DQL function but instead implement it as standard SQL CASE .. WHEN expression. This way your function would work both on MySQL and Sqlite, which is particularly useful if you are like me and like to run your unit tests on in-memory sqlite.
This class is largely based on the work by Jeremy Hicks (I simply changed the getSql() method)
class Field extends FunctionNode
{
private $field = null;
private $values = array();
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
// Do the field.
$this->field = $parser->ArithmeticPrimary();
// Add the strings to the values array. FIELD must
// be used with at least 1 string not including the field.
$lexer = $parser->getLexer();
while (count($this->values) < 1 ||
$lexer->lookahead['type'] != Lexer::T_CLOSE_PARENTHESIS) {
$parser->match(Lexer::T_COMMA);
$this->values[] = $parser->ArithmeticPrimary();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
$query = '(CASE ' . $this->field->dispatch($sqlWalker);
for ($i=0, $limiti=count($this->values); $i < $limiti; $i++) {
$query .= ' WHEN ' . $this->values[$i]->dispatch($sqlWalker) . ' THEN ' . $i;
}
$query .= ' END)';
return $query;
}
}
You can write your own DQL extension.
In case that the field that you want to "order by" is an ENUM data type, then ORDER BY will work in the order in which the values were defined for that ENUM field.
For example, I had a filed defined as enum('n','pe','o','ap','c') that was giving a weird ordering. The ordering got fixed after updating the enum to: enum('ap','c','n','o','pe')
Related
I'm working with MySQL for a while and also built an API in NodeJS including mySQL for work
Basically i need a query like "SELECT * FROM table WHERE fieldA=varA AND/OR fieldB=valB"
I'm using "mysql.format(sql, args)" to format my query, so i'm using ? and ?? in my Queries.
I would like to write a basic query, that i could use and feed all the needed fields and values
I tried the following ways
-> "SELECT * FROM table WHERE ?" with "{fieldA: varA, fieldB: varB}" as replacement for ?
that leads to "SELECT * FROM table WHERE fieldA='varA', fieldB='varB'"
-> "SELECT * FROM table WHERE ?? = ?" with "['fieldA', 'fieldB']" and "['varA', 'varB']" as replacements
what leads to "SELECT * FROM table WHERE fieldA, fieldB = 'varA', 'varB'"
For now i "only" need 2 different fields, so i could add fixed "fieldA=? AND/OR fieldB=?" and fill only the values. But i would like a dynamic way and give all the fields i could need in it and also if i use AND or OR in combining.
I didn't find anything like this in the documentation, maybe somebody here had stumbled upon before.
Or might it be the only solution to dynamically add some "AND/OR ?? = ?" to the query and fill the arguments array with fieldName, values one after the other?
Consider theses are the fields that you need to build your dynamic query string.
var fields = [{fieldName:"A",value:"one",operator:"AND"},{fieldName:"B",value:"two",operator:""}];
Now try building your query string with that array. For now I have considered only two fields but you can add as per your needs.
var fields = [{fieldName:"A",value:"one",operator:"AND"},{fieldName:"B",value:"two",operator:""}];
var query = "SELECT * FROM table WHERE ";
fields.forEach((f)=>{
query=query+`${f.fieldName} = ${f.value} `
if(f.operator!="") query=query+f.operator+" "
})
console.log(query)
I have built a method for this now, with hints from Ameers answer. It will generate a query like
SELECT * FROM ?? WHERE ??=? AND/OR ??=? AND/OR ??=?
and then will add the fieldName and values to the arguments array for mysql.format
var fieldValues = [
{ field: 'command', value: command.command, operator: 'OR' },
{ field: 'alias', value: command.command, operator: 'OR' }
];
exists(fieldValues) {
let query = 'SELECT * FROM ??';
let args = ['tablename'];
for (let i = 0; i < fieldValues.length; i++) {
let operator = i == 0 ? 'WHERE' : fieldValues[i].operator;
query += ` ${operator} ?? = ?`;
args.push(fieldValues[i].field);
args.push(fieldValues[i].value);
}
return mysql.query(query, args);
}
mysql.query() here is my wrapper around mysql.connection.query and using promises
Maybe there is a better builtin method, but i didn't find one yet and this way i can still use the builtin mysql-formatter function.
Consider,
that you have fields in DB like first_name, last_name, personal_email, work_email and office_name.
and you want to get all result matching with search fields.
i.e. let search = req.query.search.
It will become easy with ORM like Objection.js
So the query will be like this using objection.js
if (search) {
table_name.where('column_name.last_name', 'like', `%${search}%`)
.orWhere('column_name.first_name', 'like', `%${search}%`)
.orWhere('column_name.personal_email', 'like', `%${search}%`)
.orWhere('column_name.work_email', 'like', `%${search}%`)
.orWhere('column_name.office_name', 'like', `%${search}%`)
}
Note: This code is in nodejs and you can also use table_name you can also add model_name.
for($count = 0; $count < count($_POST["item_sub_category"]); $count++)
{
$data = array(
':item_sub_category_id'
=> SELECT r_name FROM Repair where r_id = $_POST["item_sub_category"][$count]
);
$query = "INSERT INTO Repairlog (description,visitID) VALUES (:item_sub_category_id,'1')";
$statement = $connect->prepare($query);
$statement->execute($data);
}
As far as concerns, your code won't work. The SQL query that you are passing as a parameter will simply be interpreted as a string.
You could avoid the need for a loop by taking advantage of the INSERT INTO ... SELECT ... syntax. The idea is to generate an IN clause that contains all values that are in the array, and then run a single query to insert all records at once.
Consider:
$in = str_repeat('?,', count($_POST["item_sub_category"]) - 1) . '?';
$query = "INSERT INTO Repairlog (description,visitID) SELECT r_name, 1 FROM Repair WHERE r_id IN ($in)";
$statement = $connect->prepare($query);
$statement->execute($_POST["item_sub_category"]);
Note: it is likely that visitID is an integer and not a string; if so, then it is better not to surround the value with single quotes (I removed them in the above code).
TLDR; No.
Your question can be re-framed as: Can I write SQL code in php. The answer is NO. You can write the SQL code within a String type variable (or parameter) in php.
This is a general rule for any programming language, you cannot have multiple languages within the same file, as the language parser will not be able understand which syntax is that.
In order to embed a different language in another language, you need some kind of separator that will define when the new language or special type will start and when it will end.
how to display count() sql function using php
$results = "SELECT count(votesnumber) FROM `votes` WHERE `candidate_id` = '$candidate_id'";
$queryresults = mysqli_query($connect, $results);
if($queryresults) {
$rowresults = mysqli_fetch_assoc($queryresults);
echo $rowresults['votesnumber'];
} else {
echo "error";
}
i want to display the results of sql count() function using php. am counting specific columns WHERE ID = "some value" in phpmyadmin its working but with php its giving me headache . any ideas on how to solve this?
Try this:
$results = "SELECT count(votesnumber) AS VoteNum FROM `votes` WHERE `candidate_id` = '$candidate_id'";
$queryresults = mysqli_query($connect, $results);
if($queryresults) {
$rowresults = mysqli_fetch_assoc($queryresults);
echo $rowresults['VoteNum'];
} else {
echo "error";
}
First, if you want to refer to the column name by a reference, then you need to give it a better name using an alias:
SELECT COUNT(votesnumber) as votesnumber
ROM `votes`
WHERE `candidate_id` = '$candidate_id';
Second, you should not be munging query strings with parameter values. Instead of '$candidate_id', learn to use parameters. This prevents unexpected syntax errors and SQL injection accounts.
Third, if votesnumber is actually a number of votes, then you probably want SUM() rather than COUNT().
You need to add "AS" instruction to your SQL if you want to get this data as a specific index from array (like $rowresults['votes']):
$results = "SELECT count(votesnumber) AS votes FROM `votes` WHERE `candidate_id` = '$candidate_id'";
Remember that you can always print_r() (for arrays) or var_dump() your variable to check if it contains data you want to have.
I'm trying to allow admins to create new columns in a sql table from a form that has a fields for the column title, column type and target table. I'm sure I'm not doing this in the most elegant way possible but I'm trying to use the framework rather than have everyone beat me up for directly querying the database. I've created the following controller that almost completely works, however, when I try to use $new_column rather a hard coded string I get an undefined variable exception.
//Capture variables from view
$type = Input::get('type');
$table_name = Input::get('table');
$proposed_name = Input::get('name');
//Convert proposed name into useable column name
$new_column = strtolower(str_replace(' ', '_', (preg_replace('/[^a-zA-Z0-9_ -%][().][\/]/s', '', $proposed_name))));
if($type == 'string')
{Schema::table($table_name, function($table){$table->string($new_column);});}
elseif($type == 'date')
{Schema::table($table_name, function($table){$table->date($new_column);});}
...
//Flash Success
$message = 'Variable "' . $proposed_name . '"" has been successfully created.';
Session::flash('flash_success', $message);
return Redirect::action('VariableManagerController#getIndex');
Is there a way to make this work through the Larval framework or should I just do a raw query to the database?
For the record this will be utilizing a try catch block but that would only further confuse the code above.
$alldata=Input::all();
$table_name = Input::get('table');
Schema::table($table_name, function($table) use ($alldata)
{
$colname=$alldata['name'];
$coltype=$alldata['type'];
$table->$coltype($colname);});
}
Im trying to get a hold of OOP and PDO. Did some tutorials. In the tutorial i got the query method (so thats not mine...)
but im having troubles with a pdo query
I want to select orders from the database matching a date..... de date comes from a datepicker and returns 2012-12-16 for example therefor
$dateInputQuery = date("Y-m-d", strtotime(Input::get('datepick')));
$data = $order->getAllOrders('order', 'WHERE DATE(orderdate) = DATE({$dateInputQuery})', false, false);
the strange thing is that when i replace the WHERE clause to WHERE DATE(orderdate) = \'2013-12-16\' it returns all the data but when inserting my date like above it does not....
in the db class the method looks like this
public function getAll($table, $where = NULL, $orderSort = NULL, $limit = NULL) {
$this->query("SELECT * FROM {$table} {$where} {$orderSort} {$limit}")->error();
return $this;
}
and query method in db class
public function query($sql, $params = array()) {
//reset error
$this->_error = false;
if ($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if (count($params)) {
foreach ($params as $param) {
$this->_query->bindValue($x,$param);
$x++;
}
}
if ($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
why is this ?
Your immediate problem is caused the fact that $dateInputQuery is unquoted. Date is a string literal and should be quoted. And even though you can easily add quotes around it you really shouldn't do this. See next point.
order is a reserved word in MySQL, therefore the table name should be put in backticks
$data = $order->getAllOrders('`order`', "WHERE DATE(orderdate) = DATE('$dateInputQuery')", false, false);
^ ^ ^ ^
You're not leveraging parameter binding in query() function. Instead on top of it you're using query string interpolation leaving your code vulnerable to sql injections and diminishing the usage of prepared statements. When you use parameter binding you no longer need to quote parameter values.
Your sql query is not index-friendly. You shouldn't apply any functions (in your case DATE()) to the column you're searching on (orderdate). Instead you can rewrite your condition to apply necessary transformations/calculations to the arguments which are constants.
You should avoid using SELECT *. Read Which is faster/best? SELECT * or SELECT column1, colum2, column3, etc and Why is using '*' to build a view bad?
That being said your query should look something like
$sql = "SELECT order_id, orderdate, ...
FROM `order`
WHERE orderdate >= ?
AND orderdate < ? + INTERVAL 1 DAY";
And you should execute it
$this->query($sql, array($dateInputQuery, $dateInputQuery));
Instead of passing whole clauses (e.g. WHERE) you should pass values