Como obter uma matriz escalar unidimensional como resultado de uma consulta dql de doutrina?

116

Eu quero obter uma matriz de valores da coluna id da tabela Leilão. Se este fosse um SQL bruto, eu escreveria:

SELECT id FROM auction

Mas quando eu faço isso no Doctrine e executo:

$em->createQuery("SELECT a.id FROM Auction a")->getScalarResult(); 

Recebo uma matriz como esta:

array(
    array('id' => 1),
    array('id' => 2),
)

Em vez disso, gostaria de obter uma matriz como esta:

array(
    1,
    2
)

Como posso fazer isso usando o Doctrine?

Dawid Ohia
fonte

Respostas:

197

PHP <5.5

Você pode usar array_map, e como só tem um item por array, pode usar elegantemente 'current'como retorno de chamada, em vez de escrever um encerramento .

$result = $em->createQuery("SELECT a.id FROM Auction a")->getScalarResult();
$ids = array_map('current', $result);

Veja a resposta de Petr Sobotka abaixo para informações adicionais sobre o uso de memória.

PHP> = 5,5

Como jcbwlkr respondeu abaixo , a forma recomendada de uso array_column.

Lionel Gaillard
fonte
9
getScalarResult () fornecerá strings - use getArrayResult () se quiser números inteiros
pHoutved em
tão! array_column () é melhor no gerenciamento de memória ou é a maneira foreach, o que devemos fazer?
Dheeraj
151

A partir do PHP 5.5, você pode usar array_column para resolver isso

$result = $em->createQuery("SELECT a.id FROM Auction a")->getScalarResult();
$ids = array_column($result, "id");
jcbwlkr
fonte
98

A melhor solução é usar PDO:FETCH_COLUMN. Para fazer isso, você precisa de um hidratante personalizado:

//MyProject/Hydrators/ColumnHydrator.php
namespace DoctrineExtensions\Hydrators\Mysql;

use Doctrine\ORM\Internal\Hydration\AbstractHydrator, PDO;

class ColumnHydrator extends AbstractHydrator
{
    protected function hydrateAllData()
    {
        return $this->_stmt->fetchAll(PDO::FETCH_COLUMN);
    }
}

Adicione ao Doctrine:

$em->getConfiguration()->addCustomHydrationMode('COLUMN_HYDRATOR', 'MyProject\Hydrators\ColumnHydrator');

E você pode usá-lo assim:

$em->createQuery("SELECT a.id FROM Auction a")->getResult("COLUMN_HYDRATOR");

Mais informações: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#custom-hydration-modes

Ioan Badila
fonte
2
Eu modifiquei isso para que seja compatível com indexBy gist.github.com/ostrolucky/f9f1e0b271357573fde55b7a2ba91a32
gadelat
18

A resposta de Ascarius é elegante, mas cuidado com o uso de memória! array_map()cria uma cópia do array passado e dobra efetivamente o uso de memória. Se você trabalhar com centenas de milhares de itens de matriz, isso pode se tornar um problema. Uma vez que a passagem por referência de tempo de chamada do PHP 5.4 foi removida, você não pode

// note the ampersand
$ids = array_map('current', &$result);

Nesse caso, você pode ir com o óbvio

$ids = array();
foreach($result as $item) {
  $ids[] = $item['id'];
}
Petr Sobotka
fonte
2
Isso pareceu um pouco contra-intuitivo, já que nosso objetivo é "quase copiar" o array em ambos os casos. Tive que testar sozinho para me convencer de que é realmente verdade: gist.github.com/PowerKiKi/9571aea8fa8d6160955f
PowerKiKi
4

Acho que é impossível na Doutrina. Basta transformar a matriz de resultados na estrutura de dados desejada usando PHP:

$transform = function($item) {
    return $item['id'];
};
$result = array_map($transform, $em->createQuery("SELECT a.id FROM Auction a")->getScalarResult());
Minras
fonte