У меня есть 3 таблицы:
# schema.yml
Author:
connection: store-rw-library
tableName: lib_author
actAs: { Timestampable: ~ }
columns:
id:
type: integer(4)
unsigned: 1
primary: true
autoincrement: true
name:
type: string(50)
notnull: true
Book:
connection: store-rw-library
tableName: lib_book
actAs: { Timestampable: ~ }
columns:
id:
type: integer(4)
unsigned: 1
primary: true
autoincrement: true
name:
type: string(50)
notnull: true
relations:
Author:
class: Author
foreignAlias: Books
refClass: LinkingAuthorBook
LinkingAuthorBook:
connection: store-rw-library
tableName: lib_linking_author_book
columns:
author_id:
type: integer(4)
unsigned: 1
primary: true
book_id:
type: integer(4)
unsigned: 1
primary: true
created_at:
type: timestamp(25)
notnull: true
relations:
Author:
foreignAlias: AuthorBooks
Book:
foreignAlias: AuthorBooks
В соответствии с примечаниями через Doctrine docs я установил отношения как M: M между Автором и Книгой, используя таблицу LinkingAuthorBook.
Теперь я пытаюсь получить все книги, написанные конкретным автором, в одном запросе, что-то вроде:
class AuthorTable
{
public function getAuthor($id)
{
$q = Doctrine_Query::create()
->select('a.id AS author_id')
->addSelect('a.name AS author_name')
->addSelect('b.AuthorBooks')
->from('Author a')
->innerJoin('a.Books b')
->where('a.id = ?', $id);
$result = $q->fetchArray();
}
}
Результирующий запрос из вышеупомянутой конструкции DQL:
SELECT
m.id AS m__id,
m.name AS m__1,
m2.id AS m2__id,
m2.name AS m2__1,
m.id AS m__0,
m.name AS m__1
FROM
lib_author m
INNER JOIN
lib_linking_author_book m3 ON (m.id = m3.author_id)
INNER JOIN
lib_book m2 ON m2.id = m3.book_id
WHERE
(m.id = '163')
Из приведенного выше запроса я вижу, что он правильно выполняет объединения, но как мне получить доступ к столбцу метаданных LinkingAuthorBook.created_at
, установленному в моем файле schema.yml?
Единственным способом получить доступ к столбцу метаданных было добавление явного innerJoin
к LinkingAuthorBook
(с соответствующим псевдонимом book_link), но это привело к другому соединению в результирующем SQL. Что не имеет смысла, потому что у него есть доступ к нужным ему данным из исходного запроса.
- Обновление (3.7.2012) -
Проблема все еще возникает, если я настрою цикл для итерации по всем книгам, принадлежащим автору, я не смогу объединить метаданные из таблицы LinkingAuthorBook или таблицы Book без принудительного выполнения другого запроса.
Обновлен запрос:
$q = Doctrine_Query::create()
->from('Author a')
->innerJoin('a.Books b')
->innerJoin('b.LinkingAuthorBook ab ON a.id = ab.author_id AND b.id = ab.book_id')
->where('a.id = ?', $id);
Пример цикла из LinkingAuthorBook -> Book:
foreach ($author->getLinkingAuthorBook() as $link) {
// Works fine, no extra query
var_dump($link->getCreatedAt());
// Forces an extra query, even though 'name' is the column name
// So even though doctrine uses lazy-load, it should be able to
// get this data from the original query
var_dump($link->getBook()->getName());
}
И то же самое для следующего цикла Book -> LinkingAuthorBook:
foreach ($author->getBook() as $book) {
// Forces extra query
var_dump($book->getLinkingAuthorBook()->getCreatedAt());
// No extra query
var_dump($book->getName());
}
Моя работа вокруг:
class Book
{
public $meta;
}
class AuthorTable
{
public function getAuthor($id)
{
$q = Doctrine_Query::create()
->from('Author a')
->innerJoin('a.Books b')
->innerJoin('b.LinkingAuthorBook ab ON a.id = ab.author_id AND b.id = ab.book_id')
->where('a.id = ?', $id);
$author = $q->fetchOne();
// Manually hydrate Book Meta
foreach ($author->getLinkingAuthorBook() as $authorBook) {
$authorBooks[$authorBook->getId()] = $authorBook;
}
foreach ($author->getBook() as $book) {
$book->meta = $authorBooks[$book->getId()];
}
}
}
Итак, теперь я могу перебирать книги с мета, без форсирования дополнительного запроса:
foreach ($author->getBook() as $book) {
// No extra query
var_dump($book->meta->getCreatedAt());
// No extra query
var_dump($book->getName());
}
- Обновление (3.8.2012) -
Итак, я смог доказать это в следующем коде:
foreach ($author->getBooks() as $book)
{
echo $book->getName().'- '.$book->LinkingAuthorBook[0]->getCreatedAt().'<br/>';
}
Каждая итерация вызвала новый запрос для получения значения createdAt
. Я сделал это, введя следующие команды в MySQL:
mysql> set global log_output = 'FILE';
mysql> set global general_log = 'ON';
mysql> set global general_log_file = '/var/log/mysql/queries.log';
Затем я похитил /var/log/mysql/queries.log
и смог увидеть, как генерируются дополнительные запросы. Итак, насколько я могу судить, ручное увлажнение объекта Book :: Meta после первоначального запроса - это единственный способ получить доступ к метаданным без необходимости выполнения другого запроса.