Doctrine won't let me select specific fields - symfony

The symfony framework features an app/console file that can be executed via php to perform some maintenance tasks. It allows users to run DQL queries as well:
# php app/console doctrine:query:dql --hydrate=array \
'SELECT u.id, u.nameFirst, u.nameLast FROM DatabaseBundle:User u'
array
0 =>
array
'id' => string '1' (length=1)
'nameFirst' => string 'jaroslav' (length=8)
'nameLast' => string 'rakhmatoullin' (length=13)
1 =>
array
'id' => string '2' (length=1)
'nameFirst' => string 'Båb Kåre' (length=10)
'nameLast' => string 'Ytrefoss' (length=8)
Observe that I selected three specific columns. The problem I'm having is that a similar query gives me an error when two tables are joined.
# php app/console doctrine:query:dql --hydrate=array \
'SELECT u.id , r FROM DatabaseBundle:User u JOIN u.roles r'
[Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col -1 near 'SELECT u.id ,':
Error: Cannot select entity through identification variables
without choosing at least one root entity alias.
The following returns the whole user joined with his roles:
# php app/console doctrine:query:dql --hydrate=array \
'SELECT u, r FROM DatabaseBundle:User u JOIN u.roles r'
Obviously, I'm missing something.
Any ideas? I would appreciate links to appropriate docs too (on this specific matter).

From the documentation on "Partial Object Syntax":
By default when you run a DQL query in Doctrine and select only a subset of the fields for a given entity, you do not receive objects back. Instead, you receive only arrays as a flat rectangular result set, similar to how you would if you were just using SQL directly and joining some data.
If you want to select partial objects you can use the partial DQL keyword.
php console doctrine:query:dql --hydrate array \
'SELECT partial s.{name ,id}, partial c.{name, id }
FROM DatabaseBundle:ProductCategories c
JOIN c.suppliers s ORDER BY s.name, c.name'

Related

DQL query in Symfony 5 to fetch newest row of subset

I have a database with table called "message" with attributtes received, emitter, text, date. I want to select the last message that a user received, with max date.
With this code I get null value but the user have messages in the table:
$message = $this->getEntityManager()
->createQuery(
'SELECT m FROM App\Entity\Message m WHERE
m.receiver = :user
AND
m.createdAt = (SELECT MAX(m1.createdAt) FROM App\Entity\Message AS m1)
'
)
->setParameters([
'user' => $user
])
->getResult();
Your subquery doesn't include the user-condition; it fetches max(created) of messages, which is not necessarily one of the given user. But the subquery approach seems to overcomplicate things anyway.
An easier way would be: select messages of user order by created and limit to 1
in SQL
SELECT m.* FROM messages WHERE user_id=:user ORDER BY created DESC LIMIT 1
in DQL
$this
->getEntityManager()
->createQuery('SELECT m FROM App\Entity\Message m WHERE
m.receiver = :user
AND
m.createdAt = (SELECT MAX(m1.createdAt) FROM App\Entity\Message AS m1)
ORDER BY m.createdAt DESC LIMIT 1
')
->setParameters([
'user' => $user
])
->getResult();
or even simpler (using doctrine repository interface)
$entityMangager
->getRepository(Message::class)
->findOneBy(['user' => $user], ['created' => 'DESC'])
Also: Probably you want to make sure you have an index over user_id, created on that table.

Nette - database INSERT - number of affected rows

I would like to find out the number of affected (inserted) rows after inserting into the table. I didn't figure out how to do it in the documentation. The update returns the number of affected rows. The insert returns Nette\Database\Table\ActiveRow
How do I get it?
$affected = $context->table('author')->insert([
[
'name' => 'Sansa Stark',
'born' => null
], [
'name' => 'Arya Stark',
'born' => null
]
]);
bdump($affected); // Nette\Database\Table\ActiveRow - I need the number of inserted records
Nette Database Explorer doesn't return count after insert(). It is not useful information as long as you can count data before insert by yourself.
$data = [...];
$count = count($data);
$context->table('author')->insert($data);
It works only with update and delete as is mentioned in documentation.
$count = $context->table('author')
->where('id', 10)
->delete();
It might be possible with getRowCount() over query in Nette Database Core
Nette Database Core is built upon PDO. Alas, the authors tend to create their own objects instead of extending PDO, which makes such elementary operations tedious:
// get Nette ResultSet object
$resultSet = $this->database->query("INSERT/UPDATE/DELETE...");
// get original PDOStatement object
$pdoStatement = $resultSet->getPdoStatement();
// get the affected rows from PDO object (instead of $resultSet->rowCount())
$pdoStatement->rowCount();
A word of warning for those considering Nette for production: There is no real documentation, only cook books and autogenerated PHPDoc which just prints names without any explanation.

Symfony 5 Search on a DateTimeColumn datatables

I am using Omines Symfony DataTable Bundle https://omines.github.io/datatables-bundle/#doctrine-orm to organize my event table.
I am unable to search on my "Début" and "Fin" columns which are of type Datetime Column. I'm guessing because since these are DateTime objects I'm guessing it can't find a match.
mytable
If I type "08/19/2020" it doesn't find any results for me.
Here: https://datatables.net/forums/discussion/44218/how-do-i-search-on-a-datetime-column it advises to format the date on the server side, so I tried that (of course j 've installed the doctrine extensions to be able to use date_format):
->createAdapter(ORMAdapter::class, [
'entity' => Event::class,
'query' => function (QueryBuilder $builder) use ($eventStatus) {
$builder
->select('e')
->addSelect('DATE_FORMAT(e.startDate, "%d/%m/%Y")')
->addSelect('ca')
->addSelect('ci')
->addSelect('u')
->from(Event::class, 'e')
->join('e.category', 'ca')
->join('e.city', 'ci')
->join('e.user', 'u')
->andWhere('e.status = :status')
->setParameter('status', $eventStatus)
->orderBy('e.id', 'DESC')
;
},
])
I also changed my dateStart column to TextColumn:
->add('startDate', TextColumn::class, ['label' => 'Début', 'field' => 'e.startDate', 'render' => function($value, $context) {
return sprintf(
'%s<br>
%s',
$value,
$context->getStartAt()->format('H\hi'),
);
}])
And I have this error:
Uncaught PHP Exception Doctrine \ ORM \ Query \ QueryException: "[Syntax Error] line 0, col 34: Error: Expected StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression, got '"' "
I do not see where the problem is.
Thanks for your help.
It's difficult to tell from the question you are asking, but there are certain things that look problematic in your code.
First, you use a custom query, but you don't use any WHERE clause with the date.
Second, your formatting of the column is not named. The result can't be accessed since it doesn't have a name. You can name it with the keyword AS:
->addSelect('DATE_FORMAT(e.startDate, "%d/%m/%Y") AS startDateFormatted')
Third, you use joins and then you shouldn't use ORMAdapter but FetchJoinORMAdapter (this will help you solve problems with pagination when using joins).
In my opinion, you shouldn't try to format the startDate in the query, but check the documentation and use a Criteria
https://omines.github.io/datatables-bundle/#doctrine-orm

Symfony3.4 / Doctrine : subquery in FROM Clause : Error: Class 'SELECT' is not defined

I'm working on a Symfony 3.4 project and I'm trying to translate an sql query to DQL query but I get an Issue.
Mysql Query:
select sum(montant_paye)
from
(select montant_paye
from vente
where client_id = 1
and montant_paye > 0
order by date ASC
limit 2)
as T;
DQL Query (Error):
return $this->getEntityManager()
->createQuery('
SELECT SUM(montantPaye) as Total
FROM
SELECT v.montantPaye
FROM AppBundle:Vente v
where v.montantPaye > 0
AND v.client = '.$clientId.'
ORDER BY v.date ASC
limit 2
')
->getResult();
Error :
[Semantical Error] line 0, col 71 near 'SELECT v.montantPaye
': Error: Class 'SELECT' is not defined.
Is any one have a solution for a correct DQL query ?
Quoting from Christophe stoef Coevoet (Symfony Core Developer):
DQL is about querying objects. Supporting subselects in the FROM clause means that the DQL parser is not able to build the result set mapping anymore (as the fields returned by the subquery may not match the object anymore).
This is why it cannot be supported (supporting it only for the case you run the query without the hydration is a no-go IMO as it would mean that the query parsing needs to be dependant of the execution mode).
In your case, the best solution is probably to run a SQL query instead
(as you are getting a scalar, you don't need the ORM hydration anyway)
Details here.
add this function to your VenteRepository:
public function sumMontantPaye($clientId)
{
return $this->createQueryBuilder("v")
->select("sum(v.montantPaye) as sum")
->where("v.client = :id")
->andWhere("v.montantPaye > 0")
->setParameter("id", $clientId)
->setMaxResults(2)
->getQuery()->getSingleResult();
}
you can access the sum using $result["sum"] assuming $result is the variable assigned to this function in the controller

MariaDB error with query

I'm getting following error when executing query .
Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''0', '25'' at line 1
here is the query
$sqlData = 'SELECT * FROM users WHERE u_id_id = :UID_ID ORDER BY :ORDER_BY :ORDER_TYPE limit :START, :DATA_LENGTH';
$params = array(
":UID" => $uId,
":ORDER_BY" => $orderBy,
":ORDER_TYPE" => $orderType,
":START" => $start,
":DATA_LENGTH" => $length
);
$queryData = \registry::getDBHandler()->prepare($sqlData);
$queryData->execute($params);
var_dump($queryData->execute($params));
note
here is the var dum output of paramas
array (size=5)
':UID' => string '66' (length=2)
':ORDER_BY' => string 'id' (length=2)
':ORDER_TYPE' => string 'asc' (length=3)
':START' => string '0' (length=1)
':DATA_LENGTH' => string '25' (length=2)
Prepared statements let you bind variables to the WHERE (and I think SELECT) clauses of an SQL query. Unfortunately, they do not let you bind to the ORDER BY or LIMIT (or FROM) clauses. For that, you will need to manually append to the string.
Since those values are not being entered by the user, you should be safe from SQL injection if you just do:
$sqlData = "SELECT * FROM users WHERE u_id_id = :UID_ID ORDER BY $orderBy $orderType LIMIT $start, $length";
(Note the double quotes around the string)
And then your $params array would just be:
$params = array(":UID" => $uId);
If you are worried about SQL injection, then you can use the following to help with that:
For your ORDER BY, you can make sure that your $orderBy is in a hard-coded list of fields and reject it if it is not.
For $orderType, just simply ensure it is equal to either "asc" or "desc" (possibly ignoring case).
With $start and $length, make sure they are integers. You can also try to use intval() to convert them if need be.
If you follow these rules, then it should be safe to append these variables into your SQL query. Since $uId is part of the WHERE, you can use the prepared variable for it and that is fine.

Resources