SitePoint Sponsor

User Tag List

Results 1 to 7 of 7

Thread: __call orm

  1. #1
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    __call orm

    Wrote this little thing a few days ago. I thought it was pretty neat, anyone seen any projects using this technique?

    PHP Code:
        /**
         * Generate dynamic sql call method used to handle method strings like
         * getById, deleteById, getByNameAndDob, etc. with method arguments
         * which are parsed between the instances of "and" within the string.
         *
         */
        
    public function __call($method$args)
        {
        
            
    $deleteIndex 6;
            
    $getIndex    3;
            
            
    $index 0;
        
            
    $deletePrefix  strtolower(substr($method0$deleteIndex));
            
    $getPrefix     strtolower(substr($method0$getIndex));
            
            if(
    $deletePrefix == 'delete')
            {
            
                
    $index $deleteIndex;
            
            }
            else if(
    $getPrefix == 'get')
            {
            
                
    $index $getIndex;
            
            }
            else
            {
                
                throw new 
    Exception("Invalid method call to '$method'"); 
            
            }
            
            
    $queryArguments strtolower(substr($method$index));
            
            
    // now we need to account for 'by'
            
    $queryArguments substr($queryArguments2);
            
            
    // explode by and
            
    $argumentList   explode('and'$queryArguments);
            
            
    // determine what's legal...
            
    $possibleArgs array_keys($this->vo->export()); // << THIS VO object is just a ValueObject and export() turns it into an array.

    // this line is a little tricky. Basically it's using the two arrays ($argumentList, $possibleArgs) to see if all the arguments in $argumentList exist within $possibleArgs by getting a count of that, and comparing
            
    $intersectionIndex count(array_intersect($argumentList$possibleArgs));
            
            
    $argumentListCount count($argumentList);
            
            
    $tableName $this->vo->getTableName();
            
            if(
    $intersectionIndex != $argumentListCount)
            {
            
                
    $argsToString implode(', '$argumentList);        
                throw new 
    Exception("Nonexistent property attempted as a parameter to dynamic method '$method': $argsToString"); 
            
            }
            else if(
    count($args) != $argumentListCount)
            {
            
                
    $argsToString implode(', '$argumentList);
                throw new 
    Exception("Invalid number of parameters relative to the number of arguments defined within the method name '$method': $argsToString"); 
            
            }
            else if(
    $index == $deleteIndex)
            {
                
                
    $sql 'DELETE FROM ' $tableName ' WHERE ';
            
            }
            else if(
    $index == $getIndex)
            {
            
                
    $sql 'SELECT * FROM ' $tableName ' WHERE ';
            
            }
            else
            {
            
                throw new 
    Exception("Invalid dynamic method type '$method'");
            
            }

            
    $where '';
            
            foreach(
    $argumentList as $key => $arg)
            {
            
                
    $where .= "$arg = :$arg";            
                
    $where .= $argumentListCount == $key '' ' AND ';
            
            }
            
            
    $setString $sql $where;
            
    // USING PDO
            
    $st $this->db->prepare($setString);
        
            
    // now go ahead and set the field values...
            
            
    foreach($argumentList as $key => $arg)
            {
            
                
    $metaVal MetaClass::$arg;            
                
    $type $metaVal['pdo_param_type'];
                
                
    $st->bindValue($arg$args[$key], $type);
            
            }
            
            if(
    $st->execute())
            {
            
                return 
    $st;    
            
            }
            else
            {
            
                return 
    null;
                
            }
            
        } 

  2. #2
    SitePoint Member sry_not4sale's Avatar
    Join Date
    Sep 2004
    Location
    New Zealand
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    After a quick scan, it looks good.

    Just a quick note:

    Quote Originally Posted by illusina
    PHP Code:
            $deleteIndex 6;
              
    $getIndex    3;
              
              
    $index 0;
          
              
    $deletePrefix  strtolower(substr($method0$deleteIndex));
              
    $getPrefix     strtolower(substr($method0$getIndex));
              
              if(
    $deletePrefix == 'delete')
              {
              
                  
    $index $deleteIndex;
              
              }
              else if(
    $getPrefix == 'get')
              {
              
                  
    $index $getIndex;
              
              }
              else
              {
                  
                  throw new 
    Exception("Invalid method call to '$method'"); 
              
              } 
    Could be easily replaced by:


    PHP Code:
            if(strposstrtolower$method ), 'delete' ) === 0)
              {
              
                  
    $index 6;
              
              }
              else if(
    strposstrtolower$method ), 'get' ) === 0)
              {
              
                  
    $index 3;
              
              }
              else
              {
                  
                  throw new 
    Exception("Invalid method call to '$method'"); 
              
              } 

  3. #3
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nice, thanks for the tip . I thought it was pretty neat, the idea of using a domain specific language is pretty neat.

  4. #4
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I have used something similar in my XML library (like finyByattributeName ()). It's definitely an interesting approach...

  5. #5
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I have to say I strongly disagree with this approach to dynamically generating (is it truly generating?) class methods. With a properly designed base class, methods such as "getById" and "deleteById" would be readily available to all your children data object classes, no need for obscure magic and trickery. Other, more class-specific methods, such as "deleteByUsername" or "getByAddress" should be so easy to create in a good ORM, that it would make no sense to dynamically wire them via _call magic.

    Among other things, with this approach you expose yourself to a big hole in the documentation of your model methods (where can I get a list of the available methods in the class? what about IDE features like intellisense / autocomplete? Can I create PHPdoc type documentation from these "ghost" methods?), as well as setting yourself up for a potential disaster the day you accidentaly use "deleteByxxx" instead of "deleteByYYY" when one of the methods should have never existed in the first place.

    Given that the model is arguably the most fundamental part of any system, it should be clean, self-documenting and leave no room for costly mistakes of this nature.
    Garcia

  6. #6
    SitePoint Member
    Join Date
    Apr 2006
    Location
    Stockholm, Sweden
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I agree with ghurtado. I can't help but feel that it's like begging for problems. Besides, what's so bad about just doing $foo->delete('field', $param) instead of spending like 100 lines of code worrying about string management?

  7. #7
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ghurtado
    I have to say I strongly disagree with this approach to dynamically generating (is it truly generating?) class methods. With a properly designed base class, methods such as "getById" and "deleteById" would be readily available to all your children data object classes, no need for obscure magic and trickery. Other, more class-specific methods, such as "deleteByUsername" or "getByAddress" should be so easy to create in a good ORM, that it would make no sense to dynamically wire them via _call magic.
    Why hard code and duplicate the effort of creating these methods in the first place? The approach being taken certainly doesn't provide easily documentable method listings, but it does allow one to rapidly prototype a series of database queries simply by using _natural_ language. The idea is of DSL (Domain Specific Language) being used to solve a particular problem with constructs which meet it's needs. Now, certainly there is a limit within PHP due to it's non-extensible syntax that we not be able to provide a true DSL, but I find that the simple nature of many database calls will make such a technique as the one I proposed worth it's weight in code many times over. The actual generation of the SQL is relatively inexpensive, and provides a dramatic increase in the ability of one to easily model a vast series of database calls without the need to write a single line of code. This is the power of a DSL -- it provides a natural syntax wherein one can "phrase" their code. I currently use an update version of the piece of code I posted. And yes, the method I posted is quite ugly. Consider -- I write that __call method, OR I can go install propel. Which do you think is going to take longer?

    Quote Originally Posted by ghurtado
    ...as well as setting yourself up for a potential disaster the day you accidentaly use "deleteByxxx" instead of "deleteByYYY" when one of the methods should have never existed in the first place.
    I think this is my personal greatest concern to using a dynamic approach with anything. But, given all things, if you had an ORM which could automagically model _any_ call, would you not use it for fear of it backfiring? No, because you would have a perfect syntax, and it would be like writing a book instead of writing code.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •