⬆️ ⬇️

Named scope for Zend Framework

Working with the database, constantly have to write a lot of search methods. Here is a typical scenario:



Suppose we need to display a list of users on the site. Initially this may be the case - $ user_table-> fetchAll (). And if you want to display only girls? Let's write the getFemaleUsers () method. But only those who are not banned and have an avatar? And the output in the admin only girls, but without taking into account the status of the user?



In the end, we get the car methods that partially overlap each other or even do the same thing, and only the sorting is different. But they still need to be tested ...

')





There is nothing wrong with this approach, but it quickly tires. If there are several developers in a project, then sometimes there may appear methods that do the same thing, but are called differently.



This task has a solution and is called it - named scope . This is implemented in rails and looks like this:



 class User <ActiveRecord :: Base
   named_scope: active,: conditions => {: active => true}
   named_scope: inactive,: conditions => {: active => false}
   named_scope: male,: conditions => {: sex => male}
 end




 User.active # selects all active users
 User.inactive # Selects all inactive users
 User.active.male # Selects all active men




A little explanation. Methods in ruby ​​are very often generated, and when they are called, the brackets are almost always omitted. Above comes the call to methods, not properties.



How to implement the same thing in the Zend Framework? Zend_Db_Table_Select will come to our rescue. Unfortunately, ZF does not allow regular means to replace select for tables, so we will act independently.



We analyze on an example of the dbTable_User gateway. First you need to override the select method, and instantiate inside, for example, dbTable_User_Select (a successor of Zend_Db_Table_Select). Then we open this class and write the methods we need.



 class dbTable_User_Select extends Zend_Db_Table_Select
 {
     public function status ($ status)
     { 
         return $ this-> where ('status =?', $ status);
     }
     public function sex ($ sex)
     {
         return $ this-> where ('sex =?', $ sex);
     }
     public function hasAvatar ()
     {
         return $ this-> where ('avatar is not null');
     }
     public function sortById ()
     {
         return $ this-> order ('id DESC);
     }
 }




Everything! You can use.



  $ user_table-> select () -> hasAvatar () -> sex ('male');
 $ user_table-> select () -> status ('active') -> sortById ();




By combining the methods, you can make almost any sample, and only these micro methods can be tested.



As a result, we received objects of type Select, and in order to make a request to the database, they will have to be inserted into the fetchRow or fetchAll gateway. Here we once again cheat and make for all our dbTable_ ... _Select common parent in which we define the following methods:



  class Ext_Db_Table_Select extends Zend_Db_Table_Select
 {
     public function fetchRow ()
     {
         return $ this-> getTable () -> fetchRow ($ this);
     }

     public function fetchRowIfExists ($ message = 'This page is not on the site')
     {
         return $ this-> getTable () -> fetchRowIfExists ($ this, $ message);
     }

     public function fetchAll ($ limit = null, $ offset = null) // Not the most successful method name)
     {
         if ($ limit) {
             $ this-> limit ($ limit, $ offset);
         }

         return $ this-> getTable () -> fetchAll ($ this);
     }

     public function getPaginator ($ page = 1, $ limit = 10, $ pageRange = 7)
     {
         $ adaptee = new Ext_Paginator_AdapterAggregate ($ this);
         $ paginator = Zend_Paginator :: factory ($ adaptee);
         $ paginator-> setItemCountPerPage ($ limit);
         $ paginator-> setCurrentPageNumber ($ page);
         $ paginator-> setPageRange ($ pageRange);

         return $ paginator;
     }

     // These useful methods can also be implemented.
     public function count ()
     public function max ($ field) {};
     public function min ($ field) {};
     public function sum ($ field) {};
     public function exists () {};

     public function random ($ limit = 5) // Pseudo random
     {
         $ count = $ this-> count ();
         $ offset = ($ count> $ limit)?  $ count - $ limit: 0;
         $ this-> limit ($ limit, mt_rand (0, $ offset));

         return $ this-> fetchAll ();
     }
 }




In essence, select starts proxying to the dbTable object that stores inside it. As a bonus, we get a number of convenient methods like count, sum, etc. Now in order to get from the resulting select what you need you can do like this:



  $ select-> fetchRow ();  // Get the string
 $ select-> getPaginator ($ page);  // Paging
 $ select-> fetchAll (5);  // take 5 lines

 // Often needed where, with an empty result, you need to throw 404
 $ select-> fetchRowIfExists ();

Source: https://habr.com/ru/post/98877/



All Articles