Zend_Db_Table dynamic finders
The recently added Zend_Navigation component uses dynamic finders to find pages e.g. findOneByLabel(‘Home’) to return the first matching page with label Home (and that’s straight from the manual).
It would be nice if Zend_Db_Table could do this too but it can’t. This seems a little inconsistent to me. Why do it for some components and not others?
Anyway, adding it wasn’t that difficult. The first step is to have an (abstract?) class extend Zend_DB_Table and let all your Db-backed models extend that. I’ll call mine App_Db_Table.
003 | abstract class App_Db_Table extends Zend_Db_Table_Abstract { |
017 | public function __call( $method , $args ) { |
018 | $watch = array ( 'findBy' , 'findAllBy' ); |
019 | foreach ( $watch as $found ) { |
020 | if ( stristr ( $method , $found )) { |
021 | $fields = str_replace ( $found , '' , $method ); |
022 | return $this ->{ '_' . $found }( $fields , $args ); |
025 | throw new Exception( "Call to undefined method App_Db_Table::{$method}()" ); |
033 | public function init() { |
034 | $this ->_db = Zend_Registry::get( 'db' ); |
046 | protected function _findBy( $columns , $args ) { |
047 | $stmt = $this ->_buildQuery( $columns , $args ); |
048 | return $this ->fetchRow( $stmt ); |
058 | protected function _findAllBy( $columns , $args ) { |
059 | $stmt = $this ->_buildQuery( $columns , $args ); |
060 | return $this ->fetchAll( $stmt ); |
070 | protected function _buildQuery( $columns , $args ) { |
071 | $fields = explode ( 'And' , $columns ); |
072 | $count = count ( $fields ); |
074 | $where = "{$this->_filterField($fields[0])} = ?" ; |
075 | $where_args = $args [0]; |
078 | $select = $this ->select(); |
079 | $select ->where( $where , $where_args ); |
082 | array_shift ( $fields ); |
083 | foreach ( $fields as $field ) { |
084 | $where = "{$this->_filterField($field)} = ?" ; |
085 | $where_args = array_shift ( $args ); |
086 | $select ->where( $where , $where_args ); |
098 | protected function _underscore( $word ) { |
099 | $word = preg_replace( '/([A-Z]+)([A-Z])/' , '\1_\2' , $word ); |
100 | return strtolower (preg_replace( '/([a-z])([A-Z])/' , '\1_\2' , $word )); |
109 | protected function _filterField( $item ) { |
110 | $item = $this ->_underscore( $item ); |
111 | return strtolower ( $item ); |
Usage
I’ll use a couple of unit tests to drive the class. I’ll assume a User class extends App_Db_Table. The table schema for my test is:
2 | `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , |
3 | `username` VARCHAR ( 32 ) NOT NULL , |
4 | `email` VARCHAR ( 64 ) NOT NULL , |
5 | `city` VARCHAR ( 32 ) NOT NULL , |
6 | `state` VARCHAR ( 32 ) NOT NULL , |
7 | `favourite_colour` VARCHAR ( 16 ) NOT NULL |
And the relevant section of the test class is as shown:
07 | private function _createUser( $attributes = array ()) { |
10 | 'email' => 'john@example.com' |
11 | 'city' => 'Colchester' , |
13 | 'favourite_colour' => 'red' |
15 | $params = array_merge ( $details , $attributes ); |
17 | $user ->createRow( $params )->save(); |
23 | public function testFindByWithOneParameter() { |
27 | $found = $finder ->findByEmail( 'john@example.com' ); |
28 | $this ->assertEquals(1, count ( $found )); |
30 | $found = $finder ->findByEmail( 'smith@example.com' ); |
31 | $this ->assertFalse( $found ); |
37 | public function testFindByWithUnderscoredColumnNames() { |
41 | $found = $finder ->findByFavouriteColour( 'red' ); |
42 | $this ->assertEquals(1, count ( $found )); |
48 | public function testFindByWithMultipleParameters() { |
52 | $found = $finder ->findByStateAndCity( 'Essex' , 'Colchester' ); |
53 | $this ->assertEquals( 'Essex' , $found ->state); |
55 | $found = $finder ->findByStateAndCity( 'Essex' , 'London' ); |
56 | $this ->assertFalse( $found ); |
58 | $found = $finder ->findByStateAndCity( 'London' , 'Colchester' ); |
59 | $this ->assertFalse( $found ); |
65 | public function testFindAllByWithOneParameter() { |
67 | $this ->_createUser( array ( |
68 | 'username' => 'smith' , |
69 | 'email' => 'smith@example.com' |
74 | $found = $finder ->findAllByState( 'Essex' ); |
75 | $this ->assertEquals(2, count ( $found )); |
77 | $found = $finder ->findAllByUsername( 'john' ); |
78 | $this ->assertEquals(1, count ( $found )); |
80 | $found = $finder ->findAllByEmail( 'smith' ); |
81 | $this ->assertEquals(0, count ( $found )); |
No related posts.
This entry was posted in
Zend Framework. Bookmark the
permalink.
Nice write up, am hoping this functionality will be in Jara
Create job, thank you.
Just wanted to say I really liked the site. You have really put a lot of time into your posts and it is just great!