This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!

Pobieranie informacji z baz(y) danych to jedna z podstawowych czynności, jaką wykonujemy podczas tworzenia różnego rodzaju stron internetowych. Aby uzyskać potrzebne dane w zdecydowanej większości przypadków wystarczy proste zapytanie SQL [w przypadku Doctrine możemy też wykorzystać język DQL]. Niektóre przypadki wymagają jednak potrzeba bardziej ambitnej ekwilibrystyki, aby przygotować odpowiedni zbiór rekordów. W dzisiejszym wpisie chciałbym pokazać jedną z możliwości biblioteki Doctrine - indeksowanie wyników zapytania według samodzielnie wybranego pola.

Fotografia: Sean MacEntee, CC-BY.

Doctrine: Operator INDEX BY - indeksowanie wyników zapytania według wybranego pola.

Rozważmy następującą tabelę w bazie danych:
1
2
3
4
5
6
7
CREATE TABLE IF NOT EXISTS `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `category` int(11) NOT NULL,
  `added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Załóżmy, że mamy już w tej tabeli wystarczająco dużą liczbę rekordów. Operacje na bazie danych są wykonywane poprzez bibliotekę Doctrine, a konkretne zapytanie są formułowane w języku DQL. Przykładowe zapytanie pobierające listę 10 najnowszych artykułów może wyglądać następująco:

1
2
3
4
SELECT a.id, a.name, a.category, a.added
FROM Article a
ORDER BY a.added DESC
LIMIT 10

Zwrócony zbiór danych po przeprowadzeniu procesu hydracji do zwykłych tablic w PHP będzie indeksowany standardowo od 0 do 9. Na potrzeby artykułu załóżmy jednak, że wolelibyśmy, aby tablica była indeksowana np. za pomocą id artykułu. Jak to zrobić?

Większość programistów odparłaby szybko - “przepuśćmy tablicę przez pętlę foreach i podstawmy elementy pod odpowiednie indeksy!”. Oczywiście, jest to sposób, który rozwiązuje problem, jednakże nie w najlepszy możliwy sposób. Po pierwsze - zużyjemy więcej pamięci, niż potrzebujemy, po drugie podczas hydracji rekordy i tak są zapisywane w tablicy poprzez taką właśnie iterację na “pewnym niższym poziomie”, więc nie ma sensu robić tego ponownie. Jak zatem zrobić to “porządnie”?

Doctrine to bardzo zaawansowana biblioteka ORM, także jej twórcy pomyśleli także i o tego typu problemach. Zaoferowali oni w wewnętrznym języku biblioteki - DQL - klauzulę INDEX BY, która według manuala:

The INDEX BY construct is nothing that directly translates into SQL but that affects object and array hydration. After each FROM and JOIN clause you specify by which field this class should be indexed in the result. By default a result is incremented by numerical keys starting with 0. However with INDEX BY you can specify any other column to be the key of your result, it really only makes sense with primary or unique fields though.
"nie przekłada się na żaden fragment zapytania SQL, ale informuje bibliotekę, że powinna indeksować wyniki według wybranego pola". Aby skorzystać z tej możliwości, należy zmodyfikować nasze pierwsze zapytanie w następujący sposób:
1
2
3
4
5
SELECT a.id, a.name, a.category, a.added
FROM Article a
ORDER BY a.added DESC
LIMIT 10
INDEX BY a.id

I to wszystko. W wyniku dostaniemy tablicę indeksowaną właśnie kluczem głównym tabeli, czyli polem id.

Dziękuję za uwagę i zapraszam do lektury kolejnego wpisu już w niedzielę. Oczywiście jak zawsze zapraszam do dyskusji oraz zgłaszania wszelkiego rodzaju uwag w komentarzach.