Help dealing with a complex mixture of dependencies, sorting, filtering, and access control
I've been wrestling with this code for a while, trying to clean it up but it just doesn't feel right. I have a Model, called Library. Libraries can own books, and the Library Model class has a public books
property which is simply an array of Books. The model might look something like this:
Application_Model_Library {
public $books = null;
protected $_id;
protected $_name;
/* Getters, setters, etc. */
}
I also have a Book model. Users can have access to Books which is defined by a many-to-many table, users_books_permissions
.
The library model stores the number of books the current user has access to, which is found using a JOIN
on the books table and COUNT(*)
. Obviously, the library table itself doesn't store this information, so it is a calculated field.
protected $_accessibleBookCount;
All of my models are contained in one file, with (static) mappers built it. For example:
// In the Application_Model_Library class
publi开发者_开发知识库c static function fetchAll(){
$db = Zend_Registry::get("db");
/* etc */
}
However, my pagination plugin needs access to the raw select
statement so it can add the LIMIT
clause. This is also useful for sorting and filtering. To handle this, fetching a list of books actually requires two calls. The first one creates and returns a select
object. This is modified in my controller based on the parameters to add sorting, filtering, and paginating. Then, another method in my Model takes the select object and performs the actual fetching and initialing of objects.
The first method is also responsible for injecting the JOIN
that populates the $_accessibleBookCount
field.
Fetching a single Library involves getting the select
statement from the aforementioned function, adding a WHERE
clause so only the library I want is selected, and then feeding this into the method to actually instantiate the Library model object.
Once I have a library object, fetching its books is handled by a method in the Library model called getBooks
. If $books
is null, then the method populates it. Then, the array is returned. Access to individual books can be achieved because the $books
property is public. Upon fetching, the method also uses a JOIN
to determine whether the user has access to the book.
However, what if I need to fetch a list of all libraries AND list each library's books at the same time? It's two method calls to get the list of libraries, but then getBooks
must be called on each Library. This results in A LOT of repeated queries.
Whew. I want to thank anyone who was able to hold on long enough to get to the end of my post. I'd also like to thank anyone ahead of time for their help.
The only solution I see to fetch all books and libraries in one query is to actually fetch for all books and join with the libraries table. This might or might not be a better solution depending on what you are doing with libraries and books in your program.
Something like this (in Books model):
$select = $this->select();
$select->setIntegrityCheck(false)
->from(array("b" => "books"))
->join(array("l" => "libraries"),"b.lid = l.id",array("libraryName" => "l.name")) // lid = Library ID in Books table
->order("l.name ASC"); // Order by Library Name
You could then do some PHP magic to sort out Libraries and Books (create new objects,arrays, or whatever you want), for instance:
$libraries = array();
foreach($books as $book) {
$libraries[$book->libraryName]["books"][] = $book;
}
You have to see if it's worth the trouble and is faster/more efficient than SQL queries.
精彩评论