Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Seleccionar campos específicos

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.

Start your All-Access Pass
Buy just this tutorial for $6.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

¡Añadamos más cosas a esta página! ¿Qué tal el número medio de galletas de la suerte impresas para esta categoría? Para ello, vuelve a nuestra consulta: vive en countNumberPrintedForCategory().

... lines 1 - 17
class FortuneCookieRepository extends ServiceEntityRepository
{
... lines 20 - 24
public function countNumberPrintedForCategory(Category $category): int
{
$result = $this->createQueryBuilder('fortuneCookie')
->select('SUM(fortuneCookie.numberPrinted) AS fortunesPrinted')
->andWhere('fortuneCookie.category = :category')
->setParameter('category', $category)
->getQuery()
->getSingleScalarResult();
return (int) $result;
}
... lines 36 - 78
}

Seleccionar el promedio

Para obtener la media, podemos añadir una coma y utilizar la función AVG(). O podemos utilizar addSelect()... que me parece un poco mejor. Queremos el AVG() defortuneCookie.numberPrinted aliasado a fortunesAverage.

Esta vez no he utilizado la palabra AS... sólo para demostrar que la palabraAS es opcional. De hecho, toda la parte fortunesAverage o AS fortunesPrintedes opcional. Pero dándole un nombre a cada una, podemos controlar las claves de la matriz de resultados final, que veremos en un minuto.

... lines 1 - 24
public function countNumberPrintedForCategory(Category $category): int
{
$result = $this->createQueryBuilder('fortuneCookie')
... line 28
->addSelect('AVG(fortuneCookie.numberPrinted) fortunesAverage')
... lines 30 - 35
}
... lines 37 - 81

Ya que estamos aquí, en lugar de imprimir el nombre del objeto $category, veamos si podemos obtener el nombre de la categoría dentro de esta consulta. Diré->addSelect('category.name').

Si ves un problema con esto, ¡tienes razón! Pero ignorémoslo y avancemos a ciegas! dd($result) al final.

... lines 1 - 24
public function countNumberPrintedForCategory(Category $category): int
{
$result = $this->createQueryBuilder('fortuneCookie')
... lines 28 - 29
->addSelect('category.name')
... lines 31 - 34
dd($result);
... lines 36 - 37
}
... lines 39 - 83

Antes, esto sólo devolvía el entero fortunesPrinted. Pero ahora, estamos seleccionando tres cosas. ¿Qué nos devolverá ahora?

La respuesta es... ¡un error gigantesco!

'categoría' no está definida.

Sí, he hecho referencia a category... pero nunca nos hemos unido a ella. Añadámoslo. Estamos consultando desde la entidad FortuneCookie, y ésta tiene una propiedad category, que es un ManyToOne. Así que nos estamos uniendo a un objeto. Hazlo con ->innerJoin() pasando a fortuneCookie.category y dándole el aliascategory.

... lines 1 - 24
public function countNumberPrintedForCategory(Category $category): int
{
$result = $this->createQueryBuilder('fortuneCookie')
... lines 28 - 30
->innerJoin('fortuneCookie.category', 'category')
... lines 32 - 38
}
... lines 40 - 84

Devolución de varias columnas de resultados

Si ahora vamos a actualizar la página... éste es el error que esperaba:

La consulta devolvió una fila que contenía varias columnas.

Este ->getSingleScalarResult() es perfecto cuando devuelves una sola fila y una sola columna. En cuanto devuelvas varias columnas,->getSingleScalarResult() no funcionará. Para solucionarlo, cambia a ->getSingleResult().

... lines 1 - 24
public function countNumberPrintedForCategory(Category $category): array
{
$result = $this->createQueryBuilder('fortuneCookie')
... lines 28 - 34
->getSingleResult();
... lines 36 - 38
}
... lines 40 - 84

Esto básicamente dice

Dame la única fila de datos de la base de datos.

Inténtalo de nuevo. ¡Eso es lo que queremos! ¡Devuelve exactamente las tres columnas que hemos seleccionado!

Y ahora... tenemos que cambiar un poco este método. Actualiza el retorno int a un array... y, aquí abajo, quita el (int) por completo y devuelve $result. También podemos quitar el dd()... y podrías poner el return aquí arriba si quisieras.

... lines 1 - 24
public function countNumberPrintedForCategory(Category $category): array
{
... lines 27 - 36
return $result;
}
... lines 39 - 83

Actualizar nuestro proyecto para utilizar los resultados

¡Nuestro método está listo! Ahora vamos a arreglar el controlador. Este$fortunesPrinted ya no está bien. Cámbialo por $result en su lugar. Luego... léelo abajo con - $result['fortunesPrinted']. Copia eso, pégalo, y envía una variable fortunesAverage a la plantilla ajustada a la clave fortunesAverage. Pasa también categoryName ajustada a $result['name'].

... lines 1 - 12
class FortuneController extends AbstractController
{
... lines 15 - 30
public function showCategory(int $id, CategoryRepository $categoryRepository, FortuneCookieRepository $fortuneCookieRepository): Response
{
... lines 33 - 38
return $this->render('fortune/showCategory.html.twig',[
'category' => $category,
'fortunesPrinted' => $result['fortunesPrinted'],
'fortunesAverage' => $result['fortunesAverage'],
'categoryName' => $result['name'],
]);
}
}

¡Hora de la plantilla! En showCategory.html.twig, tenemos acceso a todo el objeto$category... que es como estamos imprimiendo category.name. Pero ahora, también tenemos una variable categoryName. Sustituye category.name por categoryName.

... lines 1 - 2
{% block body %}
... lines 4 - 5
<h1 class="text-3xl p-5 text-center my-4 font-semibold"><span class="fa {{ category.iconKey }}"></span> {{ categoryName }} Fortunes</h1>
... lines 7 - 38
{% endblock %}

No hay... ninguna razón real para hacerlo: sólo estoy demostrando que podemos obtener datos adicionales en nuestra nueva consulta. Aunque, si también hubiéramos seleccionado iconKey, entonces podríamos evitar por completo la consulta del objeto Category. Sin embargo, aunque eso podría hacer que nuestra página fuera un poco más rápida, es casi definitivamente una exageración y hace que nuestro código sea más confuso. ¡Utilizar objetos es lo mejor!

Vale, a continuación, para el "Historial de impresión", pulsa "enter" y añade{{ fortunesAverage|number_format }} y luego average.

... lines 1 - 2
{% block body %}
... lines 4 - 9
<thead class="bg-slate-500 text-white">
... lines 11 - 14
<th class="border p-4">
Print History ({{ fortunesPrinted|number_format }} total, {{ fortunesAverage|number_format }} average)
</th>
... line 18
</thead>
... lines 20 - 38
{% endblock %}

Fantástico. ¡Inténtalo de nuevo! Si no me he equivocado... ¡ya está! ¡Todo funciona! Tenemos dos consultas: una para el category que está unido afortune_cookies y la que acabamos de hacer que coge el SUM, AVG, y el name también con un JOIN. ¡Me encanta!

Recibir objetos de entidad completos de Doctrine es la situación ideal porque... es muy agradable trabajar con objetos. Pero al fin y al cabo, si necesitas consultar datos o columnas específicos, puedes hacerlo perfectamente. Y como acabamos de ver, Doctrine devolverá una matriz asociativa.

Sin embargo, podemos ir un paso más allá y pedirle a Doctrine que nos devuelva esos datos concretos dentro de un objeto. Hablemos de ello a continuación.

Leave a comment!

2
Login or Register to join the conversation
Stanislaw Avatar
Stanislaw Avatar Stanislaw | posted hace 29 días

I had an error and had to add ->groupBy('category.name') to make it work.

Reply

Hey @Stanislaw

Could you tell me a bit more about your problem? I wonder what made you to add a "group by" clause

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "beberlei/doctrineextensions": "^1.3", // v1.3.0
        "doctrine/doctrine-bundle": "^2.7", // 2.9.1
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.13", // 2.15.1
        "symfony/asset": "6.2.*", // v6.2.7
        "symfony/console": "6.2.*", // v6.2.10
        "symfony/dotenv": "6.2.*", // v6.2.8
        "symfony/flex": "^2", // v2.2.5
        "symfony/framework-bundle": "6.2.*", // v6.2.10
        "symfony/proxy-manager-bridge": "6.2.*", // v6.2.7
        "symfony/runtime": "6.2.*", // v6.2.8
        "symfony/twig-bundle": "6.2.*", // v6.2.7
        "symfony/webpack-encore-bundle": "^1.16", // v1.16.1
        "symfony/yaml": "6.2.*" // v6.2.10
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.4
        "symfony/maker-bundle": "^1.47", // v1.48.0
        "symfony/stopwatch": "6.2.*", // v6.2.7
        "symfony/web-profiler-bundle": "6.2.*", // v6.2.10
        "zenstruck/foundry": "^1.22" // v1.32.0
    }
}
userVoice