How to execute a pre-saved dql string - symfony

let's say for some reason I have a DQL string saved somewhere (in the DB) along with the necessary parameters, can I set it in a queryBuilder object and execute it?
I expected to be able to so something like
$builder = $entityManager->createQueryBuilder();
$query = $builder->getQuery()
->setDQL($stringDql)
->setParameters($arrayParams);
return $query->iterate();

The Entity manager has ::createQuery(string $dql); Where there string of DQL comes from it would not care.
$dql = 'SELECT u FROM MyProject\Model\User u WHERE u.age > 20';
// $dql = $this->getQueryFromDatabase();
$query = $em->createQuery($dql);

Related

Symfony / Doctrine findBy

Hove to create custom Repository function who query by json field. I have params column in my database who look like this:
"params": {
"product": "stopper",
"itemIdentifier": ""
}
I want to query record by product value. In this case stopper term.
You can achieve this with a classic example :
In your repository :
For one result
public function findOneProduct($value): ?Params
{
return $this->createQueryBuilder('p')
->andWhere('p.product = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
For multiple result
public function findParamsByProduct($value): ?Params
{
return $this->createQueryBuilder('p')
->andWhere('p.product = :val')
->setParameter('val', $value)
->orderBy(/*some field */)
->setMaxResults(/*if needed*/)
->getQuery()
->getResults()
;
}
In your controller:
$stoppers = $entityManager->getRepository(Params::class)->findParamsByProduct('stopper');
If I understood your question correctly, you have a table with a column named params. And inside this mysql column, you store JSON text.
And then you want to query that table and filter by looking into the JSON in your column.
This can be a bit tedious and was also highly discouraged in the past (prior to the JSON Type in Mysql 5.7.8).
Best practices would be to have a NoSQL DB such as MongoDB which is actual JSON stored in a collection(table).
Anyways, there is a solution for you.
Taking into account #AppyGG explained how to make a custom repository function.
First of all, we have to make a query using pure SQL.
It can be done two ways:
1.Return arrays containing your data.
$conn = $this->getEntityManager()->getConnection();
$sql = '
SELECT * FROM product p
WHERE p.price > :price
ORDER BY p.price ASC
';
$stmt = $conn->prepare($sql);
$stmt->execute(['price' => $price]);
// returns an array of arrays (i.e. a raw data set)
return $stmt->fetchAll();
2.Return hydrated Entities
use Doctrine\ORM\Query\ResultSetMappingBuilder;
$rsm = new ResultSetMappingBuilder($entityManager);
$rsm->addRootEntityFromClassMetadata('MyProject\Product', 'p');
$sql = '
SELECT * FROM product p
WHERE p.price > :price
ORDER BY p.price ASC
';
$nql = $this->_em->createNativeQuery( $sql, $rsm );
$nql->setParameter('price', $price);
//Return loaded entities
return $nql->getResult();
Now, knowing how to make make a MySQL query with doctrine, we want to select results filtered in JSON data.
I'm am referencing this beautiful stackoverflow which explains it all:
How to search JSON data in MySQL?
The easiest solution proposed in there requires at least MySQL 5.7.8
Your MySQL query would be as follow:
//With $entity->getParams() == '{"params": {"product":"stopper", "itemIdentifier":""}}'
$conn = $this->getEntityManager()->getConnection();
$sql = '
SELECT * FROM Entity e
WHERE JSON_EXTRACT(e.params, "$.params.product") = :product
';
//Or Like this if the column is of Type JSON in MySQL(Not doctrine, yes check MySQL).
$sql = '
SELECT * FROM Entity e
WHERE e.params->"$.params.product" = :product
';
$stmt = $conn->prepare($sql);
$statement->bindValue("product","stopper");
$stmt->execute();
return $statement->fetchAll();
Hope this helps!
P.S: Note that my example uses a column named 'params' with a Json containing also a named attribute 'params', this can be confusing. The intended purpose is to show how to do multiple level filtering.

Doctrine2 empty parameter on queryBuilder

I am trying to check whether an user email is set or not. I am able to get the ones that are set to NULL but I am missing on the ones that have an empty string as the value. Here is my attempt:
$user = $this->createQueryBuilder('u')
->where('(u.email IS NULL OR u.email = :empty)')
->setParameter('empty', "''")
->getQuery()->getResult()
;
I have no problem getting the NULL emails but I fail to get the empty string emails. Is there any way to accomplish this or is it not supported in DQL?
How about this (EDIT #2):
$user = $this->createQueryBuilder('u')
->where('u.email = NULL')
->orWhere('u.email = \'\'')
->getQuery()->getResult()
;
Does that work?
Worths mention that the Expr Helper from QueryBuilder provides a function for that:
// Example - $qb->expr()->isNull('u.id') => u.id IS NULL
public function isNull($x); // Returns string
So for your case you can do something like:
$qb = $this->createQueryBuilder('u');
$qb
->where(
$qb->expr()->orX(
$qb->expr()->isNull('u.email'),
$qb->expr()->eq('u.email', ':empty'),
)
)
->setParameter('empty', '""');
$users = $qb->getQuery()->getResult();

Doctrine query: delete with limit

I am trying to delete only x objects with a delete query from Doctrine. And since there is no LIMIT in doctrine, we should use $query->setMaxResults($limit) instead. I am using Symfony2.
However it does not work with the following query (with or without $query->setMaxResults($limit), it delete everything instead of deleting the $limit first entities).
$limit = 10;
$query = $entityManager->createQuery(
'DELETE FROM MyProject\Bundle\MyBundle\Entity\MyEntity myEntity
WHERE myEntity.cost = 50'
)
$query->setMaxResults($limit);
$query->execute();
One solution that works is to use native SQL with Doctrine like this (instead of DQL).
$limit = 10;
$sql = 'DELETE FROM my_entity
WHERE cost = 50
LIMIT ' . $limit;
$stmt = $entityManager->getConnection()->prepare($sql);
$stmt->execute();
setMaxResults works only in some cases. Doctrine seems to ignore it if it's not managed.
check the Doctrine doc : https://www.doctrine-project.org/projects/doctrine1/en/latest/manual/dql-doctrine-query-language.html#driver-portability
If it does not work, try in native SQL, like the other solution posted.
Use a sub query so you can use setMaxResults
$qb = $this->em->getRepository(MyClass::class)->createQueryBuilder('x');
$subQb = $this->em->getRepository(MyClass::class)->createQueryBuilder('x_sub');
// We can not use "setMaxResults" on delete query so we need a sub query
$subQb
->select('x_sub.id')
// ... your where clauses
->setMaxResults(500)
;
$qb
->delete()
->andWhere($qb->expr()->in('x.id', ':ids'))
->setParameter('ids', $subQb->getQuery()->getResult())
;

Doctrine - how select fields to return from the query (in Symfony2)

So imagine a basic query:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT x FROM MyBundle:MyEntity x'
);
$result = $query->getResult();
How do I select which fields to return? I was a bit thrown at the SELECT part as this is very different from SQL's SELECT (fields) from table. In fact it looks a bit odd to me, why not just do this similar to SQL?
SELECT field1, field2 FROM MyBundle:MyEntity
Anyway, how would I limit to a set list of fields?
You have to use Doctrine partial object:
$q = $em->createQuery("select partial u.{id,name} from MyApp\Domain\User u");

Is there possible to use createQueryBuilder for insert/update? If not, what function should I use?

For now I succeded to create a function that retrieves data from the database using Doctrine's function createQueryBuilder.
Does anybody know if there is a similar function to insert or update the database? Or how can i use createQueryBuilder?
Doctrine 2 ORM does not support INSERT via DQL or the DQL query builder. For a complete syntax, check the EBNF of DQL.
To handle inserts in ORM, you always manually instantiate an entity and persist it with the entity manager:
$user = new \My\Entity\User();
$entityManager->persist($user);
$entityManager->flush();
You can only handle SELECT, UPDATE and DELETE via DQL in Doctrine ORM:
Select:
SELECT u FROM My\Entity\User u WHERE u.id = :userId
Update:
UPDATE My\Entity\User u SET u.status = 'banned' WHERE u.id = :userId
Delete
DELETE My\Entity\User u WHERE u.id = :userId
You can handle these operations with the QueryBuilder as well:
Select:
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder
->select('u')
->from('My\Entity\User', 'u')
->where($queryBuilder->expr()->eq('u.id', ':userId'));
Delete:
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder
->delete('My\Entity\User', 'u')
->where($queryBuilder->expr()->eq('u.id', ':userId'));
Update:
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder
->update('My\Entity\User', 'u')
->set('u.status', 'banned')
->where($queryBuilder->expr()->eq('u.id', ':userId'));
Another option you have instead using a QueryBuilder, is using Doctrine DBAL prepare and execute functions. Probably is not as flexible as use QueryBuilder, but for do INSERTs in some situations could be useful.
The way to use is getting the Database Connection from the Entity Manager.
$sql = "INSERT INTO table (field1, field2) VALUES ('foo', 'var')";
$stmt = $em->getConnection()->prepare($sql);
$stmt->bindValue(':invoice', $invoiceId);
$result = $stmt->execute();
If you use DBAL queryBuilder, it is possible to insert.
$qb = $connection->createQueryBuilder();
To insert with the queryBuilder it is like this :
$qb->insert('MuBundle:MyClass', 'momc')
->values (array(
'property1 (id for example)' => '?'
'property2 (name for exmaple)' => '?'
))
->setParameter(0, $id)
->setparameter(1, $name)
using QueryBuilder to insert data is not possible unless your are willing to write the DQL or SQL. If you are looking for a way to simply insert data to your database table, you have to first of all make sure the data is loaded into an Entity class for the table in which you want to insert your data. For example $em->persist($entity);

Resources