Doctrine - How to print out the real sql, not just the prepared statement?
Doctrine is not sending a "real SQL query" to the database server : it is actually using prepared statements, which means :
- Sending the statement, for it to be prepared (this is what is returned by
$query->getSql()
) - And, then, sending the parameters (returned by
$query->getParameters()
) - and executing the prepared statements
This means there is never a "real" SQL query on the PHP side — so, Doctrine cannot display it.
How to get the raw sql of doctrine
According to Logging Doctrine SQL queries in Symfony2:
You can use a Doctrine logger such as DebugStack or own implementation of SQLLogger interface.
$logger = new \Doctrine\DBAL\Logging\DebugStack();
/* @var Doctrine\DBAL\Connection $connection */
$connection->getConfiguration()->setSQLLogger($logger);
After execution of some queries you can get query strings and parameters from public property of the logger.
var_dump($logger->queries);
You second question is very different. Btw, as said in documentation:
Objects that were not already loaded from the database are replaced
with lazy load proxy instances. Non-loaded Collections are also
replaced by lazy-load instances that fetch all the contained objects
upon first access. However relying on the lazy-load mechanism leads to
many small queries executed against the database, which can
significantly affect the performance of your application. Fetch Joins
are the solution to hydrate most or all of the entities that you need
in a single SELECT query.
Doctrine 2 DBAL and QueryBuilder - It really works?
Perfectly normal. The essence of prepared statements is that the sql is sent to the database server independent of the actual values. The two are always kept apart, and merged at database level; hence getSQL
from Doctrine is unable to return the fully compiled SQL query.
It is a bit odd that the documentation does not have an actual example of executing the query.
Personally I'm not a big fan of QueryBuilder::setParameter. Instead I do:
$queryBuilder = $conn->createQueryBuilder();
$queryBuilder
->select('id', 'name')
->from('users')
->where('email = ?');
$stmt = $conn->executeQuery($queryBuilder->getSQL(),[$userInputEmail]);
$user = $stmt->fetch();
Just need to send an array of values whose order matches your parameter order. As a bonus, you can send an array for WHERE xxx IN(?). Something which prepared statements don't normally handle.
As far as the insert goes, I think you may be overthinking things. The connection object supports insert,update and delete without the need for a query builder.
$conn->insert('core_users',['username'=>'DTrump','secret='Putin']);
=================================
Here is a working example using setParameter
$qb
->select('projectId','gameNumber','fieldName')
->from('games')
->where('gameNumber = ?')
->setParameter(0,11457);
$stmt = $qb->execute();
dump($stmt->fetchAll());
Doctrine IF Statement
Doctrine does not have IF
expression support by default. Instead CASE
expressions are supported, which can be converted to: CASE t.deleted WHEN 1 THEN 4 ELSE t.status END
Your original query converted to the ORM QueryBuilder would be:
$this->createQueryBuilder('t')
->addSelect('CASE t.deleted WHEN 1 THEN 4 ELSE t.status END AS status')
->where('t.status LIKE :status')
->orderBy('t.createdAt', 'DESC')
->setParameter('status', $status)
->setMaxResults($limit)
->getQuery()
->getResult();
However, this will not modify the value sent to the entity but instead produce a nested array result with the status
value separated.
array
array
[0] => Entity (Object)
['status'] => 4
array
[0] => Entity (Object)
['status'] => 4
// ...
where in no result in doctrine query builder
Try with
use Doctrine\DBAL\Connection;
//....
->setParameter('groles', $users, Connection::PARAM_STR_ARRAY)
Doctrine DBAL 2.13 $statement-execute() returns bool (instead of Result)
Update to doctrine/dbal 2.13.1 (released 4/2021) and use:
$result = $statement->executeQuery();
while (($row = $result->fetchAssociative()) !== false) {
}
Note that executeQuery() should be used to get the Result-object as execute() is now also deprecated. (this is also missing in the release notes)
Excluding overlapped period in doctrine QueryBuilder
I found out that using a subquery with NOT EXISTS ()
instead of inner join is the best way to do:
SELECT *
FROM product p
WHERE NOT EXISTS (
SELECT * FROM booking b WHERE (
(b.startAt between '2022-01-05 00:00:00' and '2022-01-15 00:00:00') OR
(b.endAt between '2022-01-05 00:00:00' and '2022-01-15 00:00:00') OR
('2022-01-05 00:00:00' between b.startAt and b.endAt)
) AND b.product_id = p.id
) IN (1,2);
Here, the subquery find conflicting bookings (thanks @nice_dev in comments).
Doctrine repository:
class ProductRepository extends EntityRepository
{
private EntityRepository $bookingRepository;
public function __construct(BookingRepository $bookingRepository)
{
$this->bookingRepository = $bookingRepository;
}
public function getAvailableProducts(\DateTimeInterface $startAt, \DateTimeInterface $endAt): array
{
$bookingQueryBuilder = $this->bookingRepository->createQueryBuilder('b')
->andWhere('b.startAt between :startAt and :endAt OR b.endAt between :startAt and :endAt OR :startAt between b.startAt and b.endAt')
->andWhere('b.product = p')
;
return $this->createQueryBuilder('p')
->andWhere(sprintf('not exists (%s)', $bookingQueryBuilder->getDQL()))
;
}
}
Need Doctrine Many to Many QueryBuilder Query to Return NOT In Common Rows
I would suggest to break down your logic as
First select prices that belongs to $user->getId()
as
$userPrices = $this->createQueryBuilder("u")
->select("u.prices")
->from("YourBundleName:Prices","p")
->innerJoin('p.users', 'u')
->andWhere('u.id = :user1Id')
->getDQL();
Then get prices for owner which is $owner->getId()
and exclude prices from the subquery for $user->getId()
as
$qb = $this->createQueryBuilder("pr");
$qb->select("pr")
->from("YourBundleName:Price", "pr")
->innerJoin('pr.owner', 'o')
->where(
$qb->expr()->not(
$qb->expr()->in(
"pr.id",
$userPrices
)
)
)
->andWhere('o.id = :ownerId')
->setParameter('owner1Id', $owner->getId())
->setParameter('user1Id', $user->getId())
;
$query = $qb->getQuery();
$result = $query->getResult();
This would be more like to your original query but not the exact one I guess and might need some tweaks as per your mappings, but will give you an idea to move forward with this
References
- Doctrine EXIST and NOT EXIST
- Doctrine Query Builder nested orX and andX conditions with join
Related Topics
How to Query Employee Details and Relate Their Performance Metrics
Can't Install PHPunit via Pear, Requires Pear Installer >= 1.9.2, Can't Upgrade Pear from 1.9.0
Combine PHP Prepared Statments with Like
Applied Coupons Disable Free Shipping Conditionally in Woocommerce
Avoid Public Folder of Laravel and Open Directly the Root in Web Server
When Do I Use Static Variables/Functions in PHP
Programmatically Creating New Order in Woocommerce
Algorithm to Get All Possible String Combinations from Array Up to Certain Length
Batch Script to Replace PHP Short Open Tags with <Php
Download a File from Ftp Using Curl and PHP
Access Joomla 2.5 from External Script to Get Article by Id
How to Dynamically Build Queries with Pdo
Convert Number to Letter with PHP
How to Remove the Leading Character from a String
Laravel Migration Table Already Exists, But I Want to Add New Not the Older