Obtención con DQL, QueryBuilder y find()
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeAhora nuestra base de datos está llena de brillantes naves estelares ficticias Pero esta página de inicio sigue mostrando las naves codificadas. ¡Qué pena! Es hora de cargarlas desde la base de datos. ¡Eso mejorará la genialidad de nuestra aplicación x10!
Ve a tu terminal. ¿Recuerdas la consulta SQL para seleccionar todas las naves? Ejecútala de nuevo:
symfony console doctrine:query:sql 'select * from starship'
Eso es SQL en bruto, pero el ORM de Doctrine tiene su propio lenguaje de consulta llamado DQL: Lenguaje de Consulta Doctrine Es como SQL, pero en lugar de consultar a partir de tablas, con DQL piensas en términos de consulta a los objetos entidad. Ejecuta la misma consulta anterior pero como DQL:
Escribir DQL manual
Note
Desde DoctrineBundle 3.0, el comando pasó a llamarse symfony console dbal:run-sql
symfony console doctrine:query:dql 'select s from App\Entity\Starship s'
Esto parece un poco raro, pero es PHP volcando nuestros objetos Starship - y hay tres de ellos, igual que en la consulta sin procesar.
Aprovechemos esto en nuestro controlador de página de inicio. Abresrc/Controller/MainController.php y busca el método homepage(). En lugar de inyectar este StarshipRepository (es el antiguo del directorio Model ), sustitúyelo por EntityManagerInterface $em de Doctrine.
| // ... lines 1 - 9 | |
| class MainController extends AbstractController | |
| { | |
| ('/', name: 'app_homepage') | |
| public function homepage( | |
| EntityManagerInterface $em, | |
| ): Response { | |
| // ... lines 16 - 22 | |
| } | |
| } |
EntityManagerInterface
En el último capítulo, vimos que Doctrine pasa un ObjectManager al método AppFixture::load(). Este EntityManagerInterface es un tipo de ObjectManager y es lo que utilizaremos para autocablear el gestor de entidades de Doctrine.
Utilizando createQuery()
A continuación, escribe:$ships = $em->createQuery() y pasa la cadena DQL:SELECT s FROM App\Entity\Starship s. Por último, llama a ->getResult().
| // ... lines 1 - 9 | |
| class MainController extends AbstractController | |
| { | |
| // ... line 12 | |
| public function homepage( | |
| // ... line 14 | |
| ): Response { | |
| $ships = $em->createQuery('SELECT s FROM App\Entity\Starship s')->getResult(); | |
| // ... lines 17 - 22 | |
| } | |
| } |
Esto ejecuta la consulta, coge los datos pero devuelve una matriz de objetos Starship en lugar de los datos sin procesar, ¡lo cual es asombroso!
Deja el resto del método como está.
Gira y actualiza la página de inicio. Está básicamente igual... ¡eso es buena señal! Fíjate bien en la barra de herramientas de depuración web: hay una nueva sección "Doctrine". OooooooOooo.
Perfilador Doctrine
Haz clic para abrir el panel del perfilador "Doctrine". Es genial. Muestra todas las consultas que se ejecutaron durante la última petición. Sólo vemos una: ¡tiene sentido!
Podemos ver una consulta formateada que es más legible, una consulta ejecutable que podemos copiar y pegar en nuestra herramienta SQL favorita, un botón "Explicar consulta" para ver información específica de la base de datos sobre cómo se ejecutó la consulta, y un "Ver rastreo de consultas".
¡Éste es mi favorito! Muestra la pila de llamadas que condujo a esta consulta. Resulta muy útil para averiguar qué código desencadenó la consulta, en este caso, nuestro método homepage().
Utilizar el QueryBuilder
Por suerte, Doctrine tiene un "constructor de consultas". Esta cosa es impresionante: en lugar de escribir la cadena DQL manualmente, la construimos con un objeto. De vuelta a nuestro método homepage(), sustituye $em->createQuery() por$em->createQueryBuilder(). Fuera de él, encadena ->select('s'), luego->from(Starship::class, 's') golpeando la pestaña añade la sentencia use de App\Entity. ¡Bonus! Podemos utilizar Starship::class en lugar de la cadena.
Por último, antes de ->getResult(), llama a ->getQuery().
| // ... lines 1 - 10 | |
| class MainController extends AbstractController | |
| // ... lines 12 - 13 | |
| public function homepage( | |
| // ... line 15 | |
| ): Response { | |
| $ships = $em->createQueryBuilder() | |
| ->select('s') | |
| ->from(Starship::class, 's') | |
| ->getQuery() | |
| ->getResult(); | |
| // ... lines 22 - 27 | |
| } | |
| } |
De vuelta en la aplicación, actualiza la página de inicio... ¡todavía funciona!
Aún tenemos que refactorizar una cosa. Haz clic en una de las naves... ¡oh no!
Nave estelar no encontrada.
Ahh, nuestra acción StarshipController::show() sigue utilizando el antiguo StarshipRepositorycon los datos codificados. ¡Tenemos que arreglarlo!
Abre src/Controller/StarshipController.php y busca el método show(). Como necesitamos consultar los datos, sustituyeStarshipRepository $repository por EntityManagerInterface $em.
| // ... lines 1 - 10 | |
| class StarshipController extends AbstractController | |
| { | |
| ('/starships/{id<\d+>}', name: 'app_starship_show') | |
| public function show(int $id, EntityManagerInterface $em): Response | |
| { | |
| // ... lines 16 - 23 | |
| } | |
| } |
En este caso, la consulta es tan sencilla que hay un método abreviado.
Utilizando find()
Escribe $ship = $em->find(Starship::class, $id).
| // ... lines 1 - 10 | |
| class StarshipController extends AbstractController | |
| { | |
| // ... line 13 | |
| public function show(int $id, EntityManagerInterface $em): Response | |
| { | |
| $ship = $em->find(Starship::class, $id); | |
| // ... lines 17 - 23 | |
| } | |
| } |
El primer argumento de find() es la clase de entidad que quieres recuperar, y el segundo es el ID. ¡Fácil!
Vuelve a la aplicación y... actualiza. ¡Funciona! Mira la barra de herramientas de depuración web: se ha ejecutado una única consulta para obtener la nave estelar.
Ya hemos terminado con nuestro antiguo directorio Model/. Bueno, casi, el StarshipStatusEnum sigue siendo necesario, así que muévelo a Entity/ para mantener las cosas organizadas. PhpStorm se encargará de renombrarlo. Ahora, borra src\Model y ¡celebra! ¡Me encanta borrar código sin usar!
¡Siguiente paso! Vamos a echar un vistazo a los repositorios de entidades como forma de mover la lógica de consulta fuera de nuestros controladores.
11 Comments
okay, got it working
this wasn't enough:
sudo apt install php8.3-fpm
sudo apt install php8.3-pgsql
sudo phpenmod -v 8.3 -s fpm pdo_pgsql pgsql
obviously, I had to restart the symfony server, because that is the one running web app
symfony server:stop
symfony server:start -d
Hey @Martin-C
Thanks for sharing and I'm glad you got it working. It's super weird that
findAll()worked but errored withcreateQuery()...it worked with findAll() because it was just a mock returning instances of Starship
new Starship(1, 'USS LeafyCruiser (NCC-0001)', 'Garden', 'Jean-Luc Pickles', StarshipStatusEnum::IN_PROGRESS, new \DateTimeImmutable('+1 day')),
etc...
Oohh, got it!
php -m | grep pdo
pdo_pgsql
pdo_sqlite
hmm, once I replace dummy findAll() method with real
$ships = $em->createQuery('SELECT s FROM App\Entity\Starship s')->getResult();
I get "An exception occurred in the driver: could not find driver"
How is this is supposed to be running? Docker ps returns only db container, but where is the php service configured?
Obviously, if I run CLI
symfony console doctrine:query:dql 'select s from App\Entity\Starship s'
everything's OK.
Is this example suppose to use any PHPStorm internal docker intepreter?
I solved it like this:
Edit php.ini file.
decommand these:
extension=bz2
extension=curl
extension=gd
extension=gettext
extension=mbstring
extension=exif
extension=mysqli
extension=pdo_mysql
extension=pdo_pgsql
extension=pdo_sqlite
extension=pgsql
I have the same problem. I think there was an update for symfony, after these tutorials were published. Did you managed to fix it by any mean?
Hey @Bart-V!
With the Symfony CLI, PHP is expected to be installed locally on your machine. Docker is only used for the "services" your app requires.
pdo_pgsqlis required for theDATABASE_URLwe're using in this course but isn't always installed by default. I looked at my Homebrew PHP installation and it looks like, for me, pgsql was compiled into PHP.If you got it working I guess it was available, just not enabled.
As a total failsafe, you can always switch your
DATABASE_URLto a DB driver you have available (like SQLite). There's nothing in this course that is Postgres specific. We just wanted to show how it works with SymfonyCLI.--Kevin
Got another solution.
With the first one (edit php.ini); everytime I tried the fixtures:load it kept mentioning:
But if you run the next command, the fixtures:load worked perfectly. Without mentioning the line above.
I found this, because of your mentioning the line
for me, pgsql was compiled into PHPThank you, Kevin!!
You're welcome!
Yeah, when PHP is installed via apt, you need to install pgsql with apt.
This conversation has spurred me to write a blog post about installing PHP natively on different operating systems. So, thank you too!
"Houston: no signs of life"
Start the conversation!