Доктрина - Доступ к метаданным, сохраненным в таблице ссылок - PullRequest
0 голосов
/ 06 марта 2012

У меня есть 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 после первоначального запроса - это единственный способ получить доступ к метаданным без необходимости выполнения другого запроса.

1 Ответ

1 голос
/ 06 марта 2012

Мое предложение:

//one query for fetching an Author, his books and created_at for the each 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 = ?', 1);

$author = $q->fetchOne();

echo 'Author: '.$author->getName().'<br/>';
echo 'Books: <br/>';
foreach ($author->getBooks() as $book)
{
    echo $book->getName().'- '.$book->LinkingAuthorBook[0]->getCreatedAt().'<br/>';
}

Только один запрос, который вам действительно нужен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...