由于m:n关系,带有构造函数的无限循环


Infinite loop with constructors due to m:n relation

我有一个包含作者和图书的数据库,m:n作者(id,…)Authors_books (a_id, b_id)Books (b_id,…)

我的问题是,我不能使用构造函数将author/book-data获取到数组中,因为我将得到一个无限循环。

class Book
{
    public $name;
    public $authors;
    public function __construct($name)
    {
        $this->name=$name;
        $this->authors=$this->Get_Authors();
    }
    public function Get_Authors()
    {
        $authors=array();
        /* ... (database) */
        $authors[]=new Author($name_from_db);
        return $authors;
    }
}
class Author
{
    public $name;
    public $books;
    public function __construct($name)
    {
        $this->name=$name;
        $this->books=$this->Get_Books();
    }
    public function Get_Books()
    {
        $books=array();
        /* ... (database) */
        $books[]=new Book($name_from_db);
        return $books;
    }
}

的例子:

new Book('book_1');

->将获取'author_1'并使用Author类的__构造函数

new Author('author_1');

->将获取'book_1并使用Book类的__构造函数…

在PHP类中解决m:n关系的"最佳实践"是什么?

你可以在这里使用延迟加载:

class Book {
    public $name;
    private $_authors = null;
    public function __constructor($name) {
        $this->name = $name;
    }
    public function getAuthors() {
        if ($this->_authors === null) {
            $this->_authors = array();
            /* database */
            $this->_authors[] = new Author(/**/);
        }
        return $this->_authors;
    }
    // You can add some magic getter if you want to access authors as property
    public function __get($key) {
        if ($key === 'authors') {
            return $this->getAuthors();
        }
        throw new Exception('Unknown property '.$key);
    }
}
class Authors {
    public $name;
    private $_books = null;
    public function __constructor($name) {
        $this->name = $name;
    }
    public function getBooks() {
        if ($this->_books === null) {
            $this->_books = array();
            /* database */
            $this->_books[] = new Book(/**/);
        }
        return $this->_books;
    }
    // You can add some magic getter if you want to access books as property
    public function __get($key) {
        if ($key === 'books') {
            return $this->getBooks();
        }
        throw new Exception('Unknown property '.$key);
    }
}

这将导致只在需要时才加载authors/books,并且不会无限循环,但是这里可能会遇到另一个问题:

$author = new Author("Jon Doe");
$book = $author->books[0];
// assuming that book has one author
// $book->authors[0] will not be same object as $author

解决方案是使用第三个对象来加载图书和作者,该对象将存储已经加载的对象并将它们注入适当的位置

class Library {
    private $_books = array();
    private $_authors = array();
    public function getBooksForAuthor($authorId) {
        /* db query... */
        $books = array();
        while ($row = $stmt->fetch()) {
            if (isset($this->_books[$row['id']]) {
                $books[] = $this->_books[$row['id']];
            } else {
                $book = new Book($row);
                $this->_books[$row['id']] = $book;
                $books[] = $book;
            }
        }
        return $books;
    }
    /* and similar authorsForBook() method */
}
class Author {
    private $_data;
    private $_library;
    private $_books = null;
    public function __constructor($data, $library) {
        $this->_data = $data;
        $this->_library = $library;
    }
    public function getBooks() {
        if ($this->_books === null) {
            $this->_books = $this->_library->getBooksForAuthor($this->_data['id']);
        }
        return $this->_books;
    }
    public function __get($key) {
        if ($key === 'books') {
            return $this->getBooks();
        }
        if (isset($this->_data[$key]) {
            return $this->_data[$key];
        }
        throw new Exception('Unknown property '.$key);
    }
}
/* and similar for book */

为什么要加载所有相关对象?使用"延迟加载"——不加载所有相关的对象,直到你需要它们。在你的例子中,它意味着通过它们的getter方法获得相关对象。如果你需要通过属性获取它们,你可以通过魔法方法实现getter。