Symfony 2 - Doctrine 2 - Native Sql - Delete Query - php

Instead of removing my entities one by one with
$this->em->remove($price);
I would like to execute a native SQL query to delete all my entities.
Here is what I tried :
$sqlQuery = "delete from mytable where mytable.fieldone_id = ".$fieldoneid." and mytable.fieldtwo_id = ".$fieldtwoid.";";
$query = $this->getEntityManager()->createNativeQuery($sqlQuery);
$query->execute();
It returns the following error :
Catchable fatal error: Argument 2 passed to Doctrine\ORM\EntityManager::createNativeQuery() must be an instance of Doctrine\ORM\Query\ResultSetMapping, none given
It wants me to pass a ResultSetMapping, but it is a delete query...
Can anyone please teach me how to do it?

I use a different way of executing native SQL queries that is much easier, in my opinion. Try something like this (I am also using the PDO method of including variables in the query, which is safer):
$sql = "delete from mytable where mytable.fieldone_id = :fieldoneid and mytable.fieldtwo_id = :fieldtwoid";
$params = array('fieldoneid'=>$fieldoneid, 'fieldtwoid'=>$fieldtwoid);
$em = $this->getDoctrine()->getManager();
$stmt = $em->getConnection()->prepare($sql);
$stmt->execute($params);
// if you are doing a select query, fetch the results like this:
// $result = $stmt->fetchAll();
This works great for me, hope it helps

as per Doctrine 2 Native SQL documentation page:
If you want to execute DELETE, UPDATE or INSERT statements the Native SQL API cannot be used and will probably throw errors.
You can user DQL queries instead.
$query = $em->createQuery("DELETE FROM YourNamespace\YourBundle\Entity\YourEntity e WHERE e.fieldone_id = " .$fieldoneid . " AND e.fieldtwo_id = " . $fieldtwoid);
$query->execute();

If you want to use the native way in doctrine, you can use in the entity repository :
public function deleteUserNative(User $user): void
{
$this->getEntityManager()->getConnection()->delete('user', array('id' => $user->getId()));
}
And just call this in your controller :
$em->getRepository(User::class)->deleteUserNative($user);
Regards,

Related

Converting regular mysql into prepared statements

Im new to database and i have written a LOT of PHP code that accesses a database using MySQL.
I didnt take into account SQL injection attacks so i have to re-write all that PHP code to use mysql prepared statements.
After looking at videos on how to used prepared SQL statements, to perform just ONE SQL command requires a whole lot of "prepared" statements. My existing code has lots of different SQL statements all over the place, it would be a nightmare to change all that code to pack and unpack all the required preparation for each "prepared" statement command.
Is there some kind of wrapper i can use to prevent turning one line of regular SQL into 6 or 7 lines of prepared statements?
For example use to do this line line of SQL
SELECT * from users where userid=10
needs many more lines of prepared SQL statements, especially if there are lots of other SQL statements too it now becomes very complex.
Is there was some sort of one line wrapper that i can call that accepts the template SQL string, plus the parameters, which also executes the command and returns the result in just one line of wrapper for different types of MYSQL statements it would be great and the code would be much less confusing looking and error prone.
For example
$users=WrapAndExecute($db,"SELECT * from users where userid=?","s",$userid);
$data=WrapAndExecute($db,"UPDATE table SET username=?,city=?","ss",$name,$city);
$result=WrapAndExecute($db,"DELETE from table where id=?","s",$userid);
$result=WrapAndExecute($db,"INSERT into ? (name,address) VALUES(?,?)","ss","users",$name,$address);
Each of those lines above would create a prepared statement template, do the bind, execute it and return the result that a regular MYSQL statement would. This would create minimal impact on existing code.
Anybody knows how to do this or if some easy php library or class already exists to do this, that i can just import and start using it?
Thanks
You don't need to change a query to a prepared statement if it has no PHP variables in it. If it has just constant expressions, it's safe from SQL injection.
$sql = "SELECT * from users where userid=10"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
You don't need to change a query that contains PHP variables, as long as the value of that variable is a constant specified in your code. If it doesn't take its value from any external source, it's safe.
$uid = 10;
$sql = "SELECT * from users where userid=$uid"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
You don't need to change a query that contains PHP variables, as long as you can filter the value to guarantee that it won't risk an SQL injection. A quick and easy way to do this is to cast it to an integer (if it's supposed to be an integer).
$uid = (int) $_GET['uid'];
$sql = "SELECT * from users where userid=$uid"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
That leaves cases where you are using "untrusted" values, which may have originated from user input, or reading a file, or even reading from the database. In those cases, parameters are the most reliable way to protect yourself. It's pretty easy:
$sql = "SELECT * from users where userid=?"; // Safe!
// two lines instead of the one line query()
$stmt = $pdo->prepare($sql);
$stmt->execute([$_GET['uid']]);
$data = $stmt->fetchAll();
In a subset of cases, you need one additional line of code than you would normally use.
So quit your whining! ;-)
Re your comment about doing prepared statements in mysqli.
The way they bind variables is harder to use than PDO. I don't like the examples given in http://php.net/manual/en/mysqli.prepare.php
Here's an easier way with mysqli:
$sql = "SELECT * from users where userid=?"; // Safe!
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('i', $_GET['uid']);
$stmt->execute();
$result = $stmt->get_result();
$data = $result->fetch_all();
I don't like the stuff they do in their examples with bind_result(), that's confusing and unnecessary. Just use get_result(). So with mysqli, you need two more lines of code than you would with PDO.
I've written query wrappers for mysqli that emulate the convenience of PDO's execute() function. It's a PITA to get an array mapped to the variable-arguments style of bind_param().
See the solution in my answers to https://stackoverflow.com/a/15933696/20860 or https://stackoverflow.com/a/7383439/20860
I were in the same boat, and I wrote such a wrapper that works exactly the way you want, save for it's being a class, not a function.
$user = $sdb->getRow("SELECT * from users where userid=?s", $userid);
$sdb->query("UPDATE table SET username=?s, city=?s", $name, $city);
$sdb->query("DELETE from table where id=?s", $userid);
$sdb->query("INSERT into ?n (name,address) VALUES(?s,?s)","users", $name, $address);
The above is a working code, as long as you have somewhere in your bootstrap file
$db = mysqli_connect(...);
...
require 'safemysql.class.php';
$sdb = new SafeMySQL('mysqli' => $db);
Note that none of the other suggestions could do anything like that.
Also note that if I were writing it today, I would have used PDO, as this class is duplicating a lot of functionality already exists in PDO.
Take a look at the PDO extension in PHP - http://php.net/manual/en/intro.pdo.php: it it secure against injections thanks to prepared statements; also, it allows you to connect to many different databases (e.g. MySQL, MSSQL, etc.).
You can then build your own wrapper as you wish to keep it clean; for example your own wrapper could be as follows:
(following example will return user rows as objects)
// connect to DB
$GLOBALS['default_db'] = new DB('localhost','db_name','username','password') ;
// Get users and output results
$query = new DBQuery('SELECT * FROM users WHERE userid = ?',array(10)) ;
var_dump($query -> results()) ;
var_dump($query -> num_rows()) ;
// DB connection
class DB {
public $connection;
public function __construct($host , $dbname , $username , $password) {
$this->connection = new \PDO('mysql:host=' . $host . ';dbname=' . $dbname , $username , $password);
}
}
// Wrapper
class DBQuery {
private $num_rows = 0;
private $results = array();
public function __construct($query , $params = null , $class_name = null , DB $db = null) {
if ( is_null($db) ) {
$db = $GLOBALS['default_db'];
}
$statement = $db->connection->prepare($query);
$statement->execute($params);
$errors = $statement->errorInfo();
if ( $errors[2] ) {
throw new \Exception($errors[2]);
}
$fetch_style = ($class_name ? \PDO::FETCH_CLASS : \PDO::FETCH_OBJ);
$this->results = $class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style);
$this->num_rows += $statement->rowCount();
while ( $statement->nextrowset() ) {
$this->results = array_merge($this->results,$class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style));
$this->num_rows += $statement->rowCount();
}
}
public function num_rows() {
return $this->num_rows;
}
public function results() {
return $this->results;
}
}
Since a key requirement seems to be that you can implement this with minimal impact on your current codebase, it would have been helpful if you had told us what interface you currently use for running your queries.
While you could use PDO:
that means an awful lot of work if you are not already using PDO
PDO exceptions are horrible
Assuming you are using procedural mysqli (and have a good reason not to use mysqli_prepare()) its not that hard to write something (not tested!):
function wrapAndExecute()
{
$args=func_get_args();
$db=array_shift($args);
$stmt=array_shift($args);
$stmt_parts=explode('?', $stmt);
if (count($args)+1!=count($stmt_parts)) {
trigger_error("Argument count does not match placeholder count");
return false;
}
$real_statement=array_shift($stmt_parts);
foreach ($args as $k=>$val) {
if (isnull($val)) {
$val='NULL';
} else if (!is_numeric($val)) {
$val="'" . mysqli_real_escape_string($db, $val) . "'";
}
$real_statement.=$val . array_shift($stmt_parts);
}
return mysqli_query($db, $real_statement);
}
Note that this does not handle IS [NOT] NULL nicely nor a literal '?' in the statement nor booleans (but these are trivial to fix).

Using PHP variables with ADODB

I am trying to do a SQL query with ADODB, which is used in the OXID framework.
$database = oxDb::getDb();
$sql = 'SELECT oxobjectid FROM oxobject2discount WHERE oxdiscountid = '.$oxdiscountid;
$resultado = $database->execute($sql);
This will always throw and exception error. I know you can use the function Prepare with ADODB to sanitize the statement and get ready to use it. However I am unable to call it in Oxid, it seems.
Anyone knows what to do?
Use " quotes
$sql = 'SELECT oxobjectid FROM oxobject2discount WHERE oxdiscountid = "'.$oxdiscountid;.'"';

Symfony2 MySql: Fetch array and get JSON array without column names

I am trying to figure out how to get JSON array with following format
[
["2016-02-26","5190","1253","425","123"],
["2016-02-27","5209","1114","521","214"],
["2016-02-28","5142","1425","412","156"],
["2016-02-29","5523","1365","632","198"],
["2016-03-01","5125","1452","523","152"],
["2016-03-02","5000","1245","741","286"]
]
But currently I am getting
[{"date":"2016-02-26","visitors":"5190","registered":"1253","downloaded":"425","sticky_activity":"123"},
{"date":"2016-02-27","visitors":"5209","registered":"1114","downloaded":"521","sticky_activity":"214"},
{"date":"2016-02-28","visitors":"5142","registered":"1425","downloaded":"412","sticky_activity":"156"},{"date":"2016-02-29","visitors":"5523","registered":"1365","downloaded":"632","sticky_activity":"198"},
{"date":"2016-03-01","visitors":"5125","registered":"1452","downloaded":"523","sticky_activity":"152"},
{"date":"2016-03-02","visitors":"5000","registered":"1245","downloaded":"741","sticky_activity":"286"}]
Here is my function, if it's gonna help
public function dataAction(Request $request){
$em = $this->getDoctrine()->getEntityManager();
$connection = $em->getConnection();
$sqlQuery = "SELECT DATE_FORMAT(date, \"%Y-%m-%d\") as date,visitors, registered, downloaded, sticky_activity
FROM engagement";
$statement = $connection->prepare($sqlQuery);
$statement->execute();
$queryResult = $statement->fetchAll();
return new JsonResponse($queryResult);
}
Return this:
return new JsonResponse(array_map('array_values', $queryResult));
By the way you should also avoid direct SQL calls in favor of DQL or query builders. These are good practices while using Doctrine. Its goal is to avoid SQL as much as possible.

Execute raw SQL using Doctrine 2

I want to execute raw SQL using Doctrine 2
I need to truncate the database tables and initialize tables with default test data.
Here's an example of a raw query in Doctrine 2 that I'm doing:
public function getAuthoritativeSportsRecords()
{
$sql = "
SELECT name,
event_type,
sport_type,
level
FROM vnn_sport
";
$em = $this->getDoctrine()->getManager();
$stmt = $em->getConnection()->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();
}
//$sql - sql statement
//$em - entity manager
$em->getConnection()->exec( $sql );
I got it to work by doing this, assuming you are using PDO.
//Place query here, let's say you want all the users that have blue as their favorite color
$sql = "SELECT name FROM user WHERE favorite_color = :color";
//set parameters
//you may set as many parameters as you have on your query
$params['color'] = blue;
//create the prepared statement, by getting the doctrine connection
$stmt = $this->entityManager->getConnection()->prepare($sql);
$stmt->execute($params);
//I used FETCH_COLUMN because I only needed one Column.
return $stmt->fetchAll(PDO::FETCH_COLUMN);
You can change the FETCH_TYPE to suit your needs.
Most of the answers here are now deprecated since Doctrine DBAL 2.13. For example, execute is deprecated and fetchAll will be removed in 2022.
/**
* BC layer for a wide-spread use-case of old DBAL APIs
*
* #deprecated This API is deprecated and will be removed after 2022
*
* #return list<mixed>
*/
public function fetchAll(int $mode = FetchMode::ASSOCIATIVE): array
It's no longer recommended to use execute and then fetchAll since both are deprecated.
* #deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead
* #deprecated Result::fetchAll is deprecated, and will be removed after 2022
So we have to be more specific when executing raw SQL as well as fetching result.
Instead of using Statement::execute(), we need to use executeQuery or executeStatement.
executeQuery return object Result:
Executes the statement with the currently bound parameters and return
result.
executeStatement return int:
Executes the statement with the currently bound parameters and return affected rows.
Instead of using Result::fetchAll(), we need to use fetchAllNumeric or fetchAllAssociative (and more).
To get a simple result, you would have to do:
public function getSqlResult(EntityManagerInterface $em)
{
$sql = "
SELECT firstName,
lastName
FROM app_user
";
$stmt = $em->getConnection()->prepare($sql);
$result = $stmt->executeQuery()->fetchAllAssociative();
return $result;
}
And with parameters:
public function getSqlResult(EntityManagerInterface $em)
{
$sql = "
SELECT firstName,
lastName,
age
FROM app_user
where age >= :age
";
$stmt = $em->getConnection()->prepare($sql);
$stmt->bindParam('age', 18);
$result = $stmt->executeQuery()->fetchAllAssociative();
return $result;
}
How to execute a raw Query and return the data.
Hook onto your manager and make a new connection:
$manager = $this->getDoctrine()->getManager();
$conn = $manager->getConnection();
Create your query and fetchAll:
$result= $conn->query('select foobar from mytable')->fetchAll();
Get the data out of result like this:
$this->appendStringToFile("first row foobar is: " . $result[0]['foobar']);
I found out the answer is probably:
A NativeQuery lets you execute native
SQL, mapping the results according to
your specifications. Such a
specification that describes how an
SQL result set is mapped to a Doctrine
result is represented by a
ResultSetMapping.
Source: Native SQL.
I had the same problem. You want to look the connection object supplied by the entity manager:
$conn = $em->getConnection();
You can then query/execute directly against it:
$statement = $conn->query('select foo from bar');
$num_rows_effected = $conn->exec('update bar set foo=1');
See the docs for the connection object at http://www.doctrine-project.org/api/dbal/2.0/doctrine/dbal/connection.html
In your model create the raw SQL statement (example below is an example of a date interval I had to use but substitute your own. If you are doing a SELECT add ->fetchall() to the execute() call.
$sql = "DELETE FROM tmp
WHERE lastedit + INTERVAL '5 minute' < NOW() ";
$stmt = $this->getServiceLocator()
->get('Doctrine\ORM\EntityManager')
->getConnection()
->prepare($sql);
$stmt->execute();
You can't, Doctrine 2 doesn't allow for raw queries. It may seem like you can but if you try something like this:
$sql = "SELECT DATE_FORMAT(whatever.createdAt, '%Y-%m-%d') FORM whatever...";
$em = $this->getDoctrine()->getManager();
$em->getConnection()->exec($sql);
Doctrine will spit an error saying that DATE_FORMAT is an unknown function.
But my database (MySQL) does know that function, so basically what is happening is Doctrine is parsing that query behind the scenes (and behind your back) and finding an expression that it doesn't understand, considering the query to be invalid.
So if like me you want to be able to simply send a string to the database and let it deal with it (and let the developer take full responsibility for security), forget it.
Of course you could code an extension to allow that in some way or another, but you just as well off using mysqli to do it and leave Doctrine to its ORM business.

Why doesn't this prepare statement work in MYSQLI?

I created this code:
$statement = $db->prepare("SELECT * FROM phptech_contact");
$statement->execute();
$result = $statement->result_metadata();
$object = $result->fetch_object();
print_r( $object );
When I run it, it doesn't work. Can anybody tell me why it doesn't work?
I have 20 rows in this table so data should be returned.
From http://ch.php.net/manual/en/mysqli-stmt.result-metadata.php
Note: The result set returned by mysqli_stmt_result_metadata() contains only metadata. It does not contain any row results. The rows are obtained by using the statement handle with mysqli_stmt_fetch().
As long as you don't need this meta data you don't need to call this method.
$statement = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$statement->execute();
$stmt->bind_result($fld1, $fld2);
while ($stmt->fetch()) {
echo "$fld1 and $fld2<br />";
}
But I really dislike the mysqli extension. PDO is much cooler ... ;-)
$db = new PDO('...');
$stmt = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$stmt->execute();
while ($obj = $stmt->fetchObject()) {
// ...
}
or
$objs = stmt->fetchAll(PDO::FETCH_OBJ);
if you're trying to get the rows from the database, the function you need is mysqli_stmt::fetch(), not mysqli_stmt::fetch_metadata()
You're also missing a few steps. When using prepared statements, you must specify the fields you would like to return instead of using the star wildcard, and then use mysqli_stmt::bind_result() to specify which variables the database fields should be placed in.
If you're more familiar with the original MySQL extension, prepared statements have a different process to use. If your select statement has a parameter (eg., "WHERE value=?") prepared statements are definitely recommended, but for your simple query, mysqli:query() would be sufficient, and not very different from the process of mysql_query()
I believe the problem is that mysqli_stmt::result_metadata() returns a mysqli_result object without any of the actual results — it only holds metadata.
So what you want to do is use $result = $statement->bind_result(...) and then call $result->fetch() repeatedly to get the results.
One of the comments under the bind-result() article shows how to do this for a query like yours, where you don't necessarily know all of the columns being returned.

Categories