SitePoint Sponsor

User Tag List

Page 2 of 3 FirstFirst 123 LastLast
Results 26 to 50 of 58
  1. #26
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    Still, there's nothing wrong with using ActiveRecord along with finder objects, which is an approach suggested in Fowler's PoEAA, and a good fit for PHP's abilities.
    I second that. I always found it really counter-intuitive to have the record and the finder in the same class.

  2. #27
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    The problem is that Rails doesn't use static calls, it uses Ruby's class methods, which are dynamic calls to the class object. The subtle differences between the two means that this approach is a fair bit easier in rails than in php. Still, there's nothing wrong with using ActiveRecord along with finder objects, which is an approach suggested in Fowler's PoEAA, and a good fit for PHP's abilities.
    Not to be rude - but I quote myself:
    Quote Originally Posted by thr
    (the way Rails did it)

  3. #28
    SitePoint Addict
    Join Date
    Aug 2003
    Location
    Toronto
    Posts
    300
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    I second that. I always found it really counter-intuitive to have the record and the finder in the same class.
    That has been my traditional take on it as well; more recently I discovered that it is actually rather convenient to have both in the same class as it means fewer application objects to deal with and gives a more complete encapsulation for an area of responsibility. So far, I'm quite happy with this refactorization in my codebase.

  4. #29
    SitePoint Addict
    Join Date
    Jan 2005
    Location
    Ireland
    Posts
    349
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe using __autoload you can make a hack to work-around this problem. Here is some code in theory that may help (completetly untested in the sense that it is completely unrun, no PHP interpetator immediately handy at the moment).

    PHP Code:
    function __autoload($class)
    {
        
    // Simplified inclusion of class for example.
        
    include($class .'.php');
        
    // Check if  class inherits from ActiveRecord
        
    $class = new ReflectionClass($class);
        if (
    $class->isSubclassOf(new ReflectionClass('ActiveRecord'))) {
            
    // Add a setClassName to ActiveRecord implementation
            
    $class::setClassName($class);
        }

    Now, I can envision possible problems here already (i.e. if there is a chain of classes inheriting from ActiveRecord), and I don't now if even works (never tried it), but it is just an idea.

    For people who a) already use __autoload and b) want to use ActiveRecord as described, adding this in may allow them to do so, then when (and if) the syntax is supported, all they will have to do is change __autoload.

    The question is, though, does it work? I'll try it out myself when I get a chance.

  5. #30
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    For what it's worth, this is the closest approximation I've found:
    PHP Code:
    class ActiveRecord {
        protected static function 
    find($class,$id) {
            
    // get stuff from the database
            
    return new $class(/* stuff */);
        }
    }
    class 
    Post extends ActiveRecord {
        public static function 
    find($id) {
            return 
    parent::find('Post',$id);
        }
    }

    $post Post::find(1);
    print_r($post);


    /*
    Output:

    Post Object
    (
    )
    */ 
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais

  6. #31
    SitePoint Zealot
    Join Date
    Oct 2004
    Location
    Worcester
    Posts
    138
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Quote Originally Posted by mwmitchell
    I wanted to do exactly what you are doing but that was the only thing I couldn't figure out. I decided to go with an "ActiveRecordFinder" class. Having finder methods in a model always seemed a bit bloatalcious anyway.
    That's kinda the point of AR - encaspulate both the values and the logic of a type/object.
    My understanding of ActiveRecord is that a DataMapper becomes an ActiveRecord when you add business logic to it. Conversely, a domain object becomes an ActiveRecord when you add database logic to it.

    Where you put the finder logic doesn't really matter; static find methods or a separate finder are both fine. Of course, I could be wrong

  7. #32
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by akrabat
    My understanding of ActiveRecord is that a DataMapper becomes an ActiveRecord when you add business logic to it.
    It's actually a Row Data Gateway that becomes an ActiveRecord when you add business logic to it.

  8. #33
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Not to be rude - but I quote myself:
    Not sure why you feel a need to defend yourself, I was just pointing out (for those unfamiliar with ruby) the reason why the finder-in-the-same-class approach works better in ruby than in php.

    Quote Originally Posted by jayboots
    That has been my traditional take on it as well; more recently I discovered that it is actually rather convenient to have both in the same class as it means fewer application objects to deal with and gives a more complete encapsulation for an area of responsibility. So far, I'm quite happy with this refactorization in my codebase.
    Fewer objects is definitely a concern of mine; domain models can get pretty complicated, and doubling the number of objects by implementing finder classes for each one seems like an inefficient approach to me.

  9. #34
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    Fewer objects is definitely a concern of mine; domain models can get pretty complicated, and doubling the number of objects by implementing finder classes for each one seems like an inefficient approach to me.
    It's clearly two separate responsibilities, so stuffing them into one place, makes things more complicated than needed. It's bad enough to put persistance and applicationlogic in one place.
    You could have a generic finder, which can be used for the majority of classes, and just write specific finders for thoose special cases.

    Or perhaps it would be a better approach to think the other way around. Have a generic record-class, and just implement a gateway (finder+attributes metadata) for each type. That's not the activerecord ofcourse, but if you want simplicity ?

  10. #35
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    It's clearly two separate responsibilities, so stuffing them into one place, makes things more complicated than needed.
    Doesn't that depend entirely on how its implemented? To take the Ruby example, I wouldn't call it complicated at all because the entire ActiveRecord implementation is 99% transparent because all you have to do is extend the ActiveRecord class. This means you can still focus fully on your business logic without cluttering up your class.

  11. #36
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Luke Redpath
    Doesn't that depend entirely on how its implemented? To take the Ruby example, I wouldn't call it complicated at all because the entire ActiveRecord implementation is 99% transparent because all you have to do is extend the ActiveRecord class. This means you can still focus fully on your business logic without cluttering up your class.
    You're right inasmuch that the userland code extending the baseclass is simpler for the all-in-one solution. I was refering to the usage though. Aslong as the record and the gateway are used close to eachother, there's no problem, but if you have to pass the gateway around, it can easily become rather confusing. From the method's signature you won't be able to tell whether you're getting a finder or an actual record, since they share the same classtype.

  12. #37
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    It's clearly two separate responsibilities, so stuffing them into one place, makes things more complicated than needed. It's bad enough to put persistance and applicationlogic in one place.
    I agree that the responisibilities are seperate, but not that it neccesarily makes things more complicated; the implementation may be more complicated, but on the other hand it's easier to use. That's the appeal of the ActiveRecord approach. Single responsibility for objects is an worthy goal, but I think it's a rule that can be broken in certain cases.


    Quote Originally Posted by kyberfabrikken
    You could have a generic finder, which can be used for the majority of classes, and just write specific finders for thoose special cases.
    That's the approach I'm currently considering, combined with some form of metadata mapping.

    Quote Originally Posted by kyberfabrikken
    Or perhaps it would be a better approach to think the other way around. Have a generic record-class, and just implement a gateway (finder+attributes metadata) for each type. That's not the activerecord ofcourse, but if you want simplicity ?
    In reality, I think the ideal approach lies somewhere in between active record and data mapper, combining the simplicity of the former with the flexibility of the latter. One possibility would be to place persitance logic in a seperate object, but keep that object inside the data class. Something like:

    PHP Code:

    class Model {
        var 
    $mapper;
        var 
    $meta_data;
        var 
    $data;

        function 
    Model($mapper$meta_data) {
            
    $this->mapper $mapper;
            
    $this->meta_data $meta_data;
        }

        function 
    save() {
            
    $this->mapper->save($meta_data$data);

        }



  13. #38
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    You're right inasmuch that the userland code extending the baseclass is simpler for the all-in-one solution. I was refering to the usage though. Aslong as the record and the gateway are used close to eachother, there's no problem, but if you have to pass the gateway around, it can easily become rather confusing. From the method's signature you won't be able to tell whether you're getting a finder or an actual record, since they share the same classtype.
    In Rails the finder is the class itself, and records are instances of the class, so there's no confusion between the two; and since the finder is essentially a singleton, there's no reason to pass it around.

  14. #39
    SitePoint Addict
    Join Date
    Aug 2003
    Location
    Toronto
    Posts
    300
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    I agree that the responisibilities are seperate, but not that it neccesarily makes things more complicated; the implementation may be more complicated, but on the other hand it's easier to use. That's the appeal of the ActiveRecord approach. Single responsibility for objects is an worthy goal, but I think it's a rule that can be broken in certain cases.
    Agreed although I don't even see different responsibilities from the point of view of the application. Say we have a User AR class -- why shouldn't that single class alone be responsible for all access to "User" data? Sure, internally it can be implemented as several distinct and more generalized types but to the application I think it makes sense to unify the interface for data access. Kyb makes a good point, but I find that the general case is to use the two "close-by".

    Quote Originally Posted by 33degrees
    That's the approach I'm currently considering, combined with some form of metadata mapping.

    In reality, I think the ideal approach lies somewhere in between active record and data mapper, combining the simplicity of the former with the flexibility of the latter. One possibility would be to place persitance logic in a seperate object, but keep that object inside the data class.
    More agreement. In fact, my implementation does something very nearly like that and is similarly constructed by model-driven meta-data (both the crud and the available available finders are entirely described that way). So far, I have yet to need to actually make a custom extension of my AR class -- I just need to update my metadata for a given class type and my autoloader automatically extends the AR class for me. Furthermore, this allows me to define transformations, validations and other criteria declaratively, which I find appealing.

  15. #40
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    In Rails the finder is the class itself, and records are instances of the class, so there's no confusion between the two; and since the finder is essentially a singleton, there's no reason to pass it around.
    Quote Originally Posted by jayboots
    Kyb makes a good point, but I find that the general case is to use the two "close-by".
    I wouldn't say it's that far-fetched.

    Say I have a class ListController, which produce a table of records from a datasource. The natural approach for me would be something like this :
    PHP Code:
    $lister =& new ListController($gateway);
    echo 
    $lister->execute(); 
    See the ambiguity if $gateway is an all-in-one type activerecord ?
    I know that I could pass the classname rather than an actual object, and let the ListController instantiate the class itself (or pull it through a static factory/singleton), but that really makes me uncomfortable.

  16. #41
    SitePoint Addict
    Join Date
    Aug 2003
    Location
    Toronto
    Posts
    300
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think I do it a little differently -- my finders return recordsets (plain arrays), not collections of or singular ARs.

    $user = new ADC_User;
    $all_users = $user->findAll();

    If I need a specific user:

    $user->find(array('id'=>'foo'));

    I also have a few reflection methods on the AR class to let me inspect the requirements of the specific AR instance based on the model it is derived from.

    The upshot is that if I had a Lister component, it wouldn't be responsible for acquiring the data -- it gets passed a normal recordset. I'll definately be using some of the arguments in this thread as input when I go to reevaluate my implementation, but so far I find it satisfactory for my needs

  17. #42
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by jayboots
    The upshot is that if I had a Lister component, it wouldn't be responsible for acquiring the data -- it gets passed a normal recordset. I'll definately be using some of the arguments in this thread as input when I go to reevaluate my implementation, but so far I find it satisfactory for my needs
    ORDER BY and LIMIT ?

    Anyway - The point is not as much if such a widget is the best possible implementation, but rather that the ambigouity may cause confusion.

  18. #43
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    From the method's signature you won't be able to tell whether you're getting a finder or an actual record, since they share the same classtype.
    Well in the case of Ruby's ActiveRecord, the finders are class methods, so you wouldn't need to pass around any objects to use them, you'd just call them against the appropriate class. There would never be any need to pass round a finder. Records, on the other hands, would be instantiated instances of this class.

  19. #44
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    I wouldn't say it's that far-fetched.

    Say I have a class ListController, which produce a table of records from a datasource. The natural approach for me would be something like this :
    PHP Code:
    $lister =& new ListController($gateway);
    echo 
    $lister->execute(); 
    See the ambiguity if $gateway is an all-in-one type activerecord ?
    I know that I could pass the classname rather than an actual object, and let the ListController instantiate the class itself (or pull it through a static factory/singleton), but that really makes me uncomfortable.
    Wouldn't it be better to pass the ListController an array of records/AR objects therefore you aren't binding the ListController directly to any gateway/AR class. In Ruby:

    Code:
    posts = Post.find(:all)
    lister = ListController.new(posts)
    lister.execute

  20. #45
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    Anyway - The point is not as much if such a widget is the best possible implementation, but rather that the ambigouity may cause confusion.
    I'm still not sure I can see this ambiguity.

    Code:
    @post = Post.new
    @lots_of_posts = Post.find(:all)
    There is no ambiguity because finders are called against the class itself, not against objects of that class.

  21. #46
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Luke Redpath
    Wouldn't it be better to pass the ListController an array of records/AR objects therefore you aren't binding the ListController directly to any gateway/AR class.
    That depends entirely on what the purpose of the component is. The above example isn't all made-up. I have one such class in an application I'm working on currently. It's basically a specialized controller, for handling pagination/sorting of results from a gateway. The component will read the request for page-offset, column to sort by and direction, and will use theese as arguments when calling the findAll method of the gateway. Since I'm reusing the component for multiple gateways (~finders), I need the gateway to be dynamic - A classmethod would tie the class into working with just one gateway.

  22. #47
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Luke Redpath
    There is no ambiguity because finders are called against the class itself, not against objects of that class.
    In short the problem boils down to this; To distinguish between the finder and the instance, classmethods are used for the former, while instance methods are used for the latter. Classmethods are static calls, and static calls give tight coupling. I like loose coupling.

  23. #48
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    In short the problem boils down to this; To distinguish between the finder and the instance, classmethods are used for the former, while instance methods are used for the latter. Classmethods are static calls, and static calls give tight coupling. I like loose coupling.
    Except that in Ruby, class methods are NOT static calls, they are dynamic, so they're inherited by subclasses, and they can be mixed in from other modules. The coupling is in fact quite loose.

    In any case, it's possible to have your cake and eat it, too. Ergo:

    PHP Code:

    class ActiveRecord {

        function 
    find() {
            
    $args func_get_args();

            
    $finder = new Finder();

            return 
    call_user_func_array(array($finder'find'), $args);

        }




    Of course, this is ignoring the "knowing which class the static call was made on" issue...

  24. #49
    SitePoint Addict
    Join Date
    Jan 2005
    Location
    United Kingdom
    Posts
    208
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've just finished a project at work in which I've used a dynamic ActiveRecord in this way. The AR provides insert, update and delete methods, finder methods are handled by the subclass of AR. The subclass passes some meta data to the AR method, which can generate the queries from the protected properties of the subclass.
    PHP Code:
    class ActiveRecord
    {
      private static 
    $db// ADODB
      
    public function save$table$primary )
      {
        
    // Called from subclass context
        
    $props get_object_vars$this );
        
    $this->db->Execute( ... );
      }
    }
    class 
    ValueObject extends ActiveRecord
    {
      const 
    TABLE 'SOME_TABLE';
      const 
    PRIMARY 'PRIMARY_KEY';
      protected 
    $prop;
      public function 
    findByPrimaryKey$pk )
      {
        
    $row $this->db->Execute( ... );
        
    $this->bless$row ); // Like perl's bless
      
    }
    }
    $vo = new ValueObject();
    $vo->findByPrimaryKey);
    $vo->set'prop');
    $vo->saveValueObject::TABLEValueObject::PRIMARY ); 
    This was a passable solution until objects started using multiple tables, and the need to create sets of objects appeared. Now it feels like a big kludge. - the AR subclass passes a resource back to the client in the case of multiple records. Time to refactor. I've started writing a generic mapper - I'd be interested to see where you go with this idea 33degrees.

  25. #50
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Generic mappers is NOT the way to go imho, because it requires you to put all your special finder-methods in one big class.


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
  •