2008年8月22日星期五

Re: [fw-db] find() returns rowset

I've made a habit of using current() in conjunction with find() like this:

<?php

$row = $table->find($id)->current();
if (null === $row) {
    // record not found
}

?>


I also use composite primary keys when necessary, in which case I pass in two or more arguments to find() but am expecting at most 1 row:

<?php

$row = $table->find($user_id, $profession_id)->current();
if (null === $row) {
    // record not found
}

?>


I personally do not dislike the current way find() works. Just me 2c.

-Hector

Julien Huang wrote:
On Wed, Aug 20, 2008 at 20:36, Bill Karwin <bill@karwin.com> wrote:   
 ah125i wrote:     
I was under the impression we have Zend_Db_Table_Rowset because it implements SeekableIterator and Countable        
Yes.  The Rowset class has some other object methods on it, like toArray(). It's also serializable, so you can put a Rowset in a cache.     
 Ok, but what's the point ? I don't get it, why use another layer when you can use php's native array type ? Every PHP developers knows how to use array, and of course they're naturally serializable and cacheable. The Zend_Db_Table_Rowset is just a collection/list of Zend_Db_Table_Row objects, and doesn't bring "real" functionnality. In my opinion the design at this point is flawed.  I don't think that using an object for the sake of using an object is a good idea. Don't get me wrong, I'm not against OOP, but PHP is not a pure object language, nor a pure procedural programming language. It's nor a procedural language that has evolved in an object language one. PHP is a hybrid language, a mix of Procedural & Object language. PHP != Java.  So why coerce us by strictly using objects ? I thought that the ZF goal was to take the "best of the breed" out there, and in my opinion the great advantage of PHP is its hybrid caracteristic. Accordingly, the most difficult task in PHP is to have a good balance between procedural & object oriented programming when designing an API / lib in my humble opinion.    
The find() method may result in zero, one, or more rows.  When I joined the ZF team, this method  returned either a Row or Rowset object, but I changed it.  It seemed confusing that a method may return two different types, depending on its arguments.  Also, what should it return if the search matches zero rows in the database?  Null?  So now I would have to test the return value for three possible types?  (null/Row/Rowset)     
 The revision of find method you're talking about : http://framework.zend.com/code/browse/Zend_Framework/trunk/library/Zend/Db/Table.php?r=3834#l294  The problem in the old find() was the use of count(). The returned results type shoud be dependant on the type of the argument, not on the content (=> count of elements) of the arguments.  What does the find() method do ? According to DocBook's comment : "Fetches rows by primary key" A primary key by nature is unique in a table, so 1 PK = 1 row.  So if I do : $table->find(42); # I give one PK=42 (scalar value), so it should return the row matching id = 42.  I want to retrieve multiple entries: $table->find(array(42, 21)); # I give an array with 2 PK so it should return an array of rows matching id IN (42, 21).     
Note that some overlap cases exist.  You can give two values (123, 456), so you're asking for multiple rows.  But suppose only a row matching 123 exists, no row matching 456.  Should find() return a Rowset or a Row?  Does the return type depend on what you ask for, or what the result is?     
 Now, what should we do if the rows doesn't exist ? example id=42 doesn't exist : $table->find(42); # I give one PK=42 (scalar value), the row doesn't exist, so it returns false (still scalar value)  $table->find(42, 21); # I give an array with 2 PK, it returns an array of matching rows, in this case, only 1 row exists, the row (Zend_Db_Table_Row) will be returned in an array  Another case, none of the rows exists : $rs = $table->find(42, 21); # I give an array with 2 PK, it returns an array of matching rows, in this case, none of the row exists, so an empty array is returned  Why an empty array ? Because it won't "crash" the following foreach statement and we can easily test the rowset : if ($rs) {   // won't get there, empty array is considered as "false" }  IMO, this behaviour is a lot more intuitive and predictable by the developer. The developer always knows what type of argument he's passing to the find() method, so he knows what type of result he's expecting. By the way, I do not hide from it, these thinkings are a lot inspired by RoR's ActiveRecord (I've never used it !), but I think their design is a lot stronger.  I know, Zend_Db_Table isn't following the Active Record pattern but the Table Data Gateway, but I remember at the beginning there was an attempt to do it (look at the abandoned Zend_Inflector), the failure in trying to do it is due to a PHP limitation : late static binding. But until PHP6, we should approach the behaviour of ActiveRecord.    
I felt the best way to make it "simple" was always to return a Rowset.  That handles all three cases of zero rows, one row, or several rows.  The Rowset has an interface for testing if it's empty, getting the first row, or iterating over multiple rows.  But the point is that you know what that interface is, without having to branch on the object's type.     
 The best way to make it "simple" was in reality a way to don't have headache ? :) I've searched through the mailing lists and ZF Issue trackers, and in fact there has never really been a debate about this issue, you've just settled it.    
If it's a little awkward to call $rowset->current() to get the first row, surely it's much more awkward to write a big if/elsif/else structure to test the result type every time you call find().     
 The use of Zend_Db_Table itself instead of directly writing SQL with Zend_Db is already awkward. So that's not a tiny branch-test in find() that will prevent developers to use Zend_Db_Table :)    

没有评论: