Frage

I have a database with authors and books, m:n authors (a_id, ...) authors_books (a_id, b_id) books (b_id, ...)

My problem is, that I can't use the constructors to fetch the author/book-data into an array, because I would get an infinite loop.

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;
    }

}

Example:

new Book('book_1');

-> is going to fetch 'author_1' and uses __constructor of Author class

new Author('author_1');

-> is going to fetch 'book_1 and uses __constructor of Book class ...

What is the "best practice" to resolve a m:n relation in PHP classes?

War es hilfreich?

Lösung

You can use lazy loading here:

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);
    }
}

This will cause that authors/books will be loaded only if you'll need it and won't loop infinitely, but you can reach another problem here:

$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

Solution for that would be to use some third object for loading books and authors, that will store already loaded objects and inject them in proper places

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 */

Andere Tipps

Why do you want to load all related object? Use "lazy loading"- don't load all related objects until you need them. In your case it would mean getting related object through their getter method. If you need to get them through property you can implement getters through magic methods.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top