我有一个包含作者和图书的数据库,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。