SitePoint Sponsor

User Tag List

Results 1 to 14 of 14
  1. #1
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Post DI and/or locator

    Hi,

    I've been messing around with DI and have come up with the following implementation:
    http://www.sitepoint.com/forums/show...7&postcount=15

    Now I feel that it's overkill. I have a very simple router, front controller, filters and page controller setup. I have two issues I'm trying to solve...

    1. How to get common data/objects and settings into my application. This means my controllers, view helpers and possibly models.

    2. How to create objects without using the new keyword, so I'm not hard coding class names. Not sure if this is really so important or not.

    I want to stay as close to KISS as possible.

    For #1, I've thought about adding a filter and then adding a "registry" or "locator" instance to the request object that is passed to each controller. But that only gives the instance to my controllers. The simplest and most obvious solution seems to be a static class. But then I'm dependant on the class name: MyLocator::connection() etc. A problem?

    For #2 I guess as soon as I solve #1, this will be solved.

    I realise that DI would be the best. But maybe not practical because I need the code *yesterday* and I think it's more complicated than I though. A service locator has advantages over DI in some ways, because you can add logic to your finder code and it just seems more simple. For controllers, I don't really need to re-use anything anyway.

    I'm confused! Help! Just want to be practical and use KISS. We're talking a pretty simple admin site for managing products, categories and simple paypal orders.

  2. #2
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    how about

    PHP Code:
    class Config
    {
        function 
    __get($s) {
            return 
    $this->{$s}();
        }

        var 
    $dsn "mysql://user:blah@host";
        var 
    $template_path "/xxx/";

        function 
    db() {
            return new 
    DbClass($this->dsn);
        }
        function 
    model() {
            return new 
    ModelClass($this->db);
        }
        function 
    view() {
            return new 
    ViewClass($this->template_path);
        }
        function 
    app() {
            return new 
    AppClass($this->model$this->view);
        }
    }

    $conf = new Config;
    $app $conf->app;

    $app->run(); 

  3. #3
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I like it. I had something like that going, but I never ran the application from that class. That's more than likely what I'm going to use.

    On a side note... you have app(), and you set a model and view. What is an "app" in your example? A controller? Just curious.

  4. #4
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, I traditionally call my entry point class Application or App. Sounds nicer to me than "Controller".

  5. #5
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I see.

    Just another questions... DI related... How can it solve this problem:
    PHP Code:
    class Tree
    {
      
      public function 
    build($list)
      {
        foreach(
    $list as $item)
        {
          
    $this->_attach(new Node($item));
        }
      }
      

    I mean, there is a clear dependancy here with the "Node" class. But it is inside of a method and loop. And multiple instances. Is this a valid fix: $this->_attach( new $this->node_class($item) ); ??? So before calling build you'd set the node_class property?

    Or would you actually re-design the class to use accept a node locator class? $this->nodeLocator->create($item) - I guess that's using a locator and not DI !

  6. #6
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'd rather use $node->attach_to($tree)

  7. #7
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So, is that "inverse of control"? Some other object would compose the two? Can you give me an example of how this would work?

  8. #8
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, I think "build a tree" cannot be method of "Tree" (replace "Tree" with e.g. "Car" to make it more obvious).

    My point is that "how do I write code without class dependencies" is a wrong question. The correct question is "how do I accurately structure and maintain my class dependencies". For example, "Tree" with hardcoded "Node" class is probably not good, however, "TreeBuilder" depending on both "Tree" and "Node" seems ok to me.

  9. #9
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So:

    $builder = new TreeBuilder( new Tree(), $items );
    $builder->setNodeType('Node_Default');
    $builder->build();

    But still, there is that nested construction within the method / loop. What would you do to specify the class? Or, how would you handle each instantiation of the node?

    - matt

  10. #10
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well, I'd use factory (new TreeBuilder(new Tree(), new NodeFactory(), $items) or a decorator (builder creates NodeDecorators rather than "bare" nodes). Anyways, "new Node" in Builder should be isolated for easy extending/refactoring

    PHP Code:
    class Builder
    {
       function 
    create_element() {
          
    // the Node class is only mentioned here
          // feel free to override me
          
    return new Node();
       }
       function 
    build() {
           foreach(....) 
               
    $tree->attach($this->create_element());


  11. #11
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Got it. That makes sense. I understand your point about what my question about DI should have been. Thanks!

  12. #12
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Related to dependancies... not Tree's :

    I'm trying to figure out ways to get "services" into my page controllers without having to instantiate a global "App" object etc. So I've come up with this:

    $services = Controller_Services();
    $router = new Router();
    $fc = new Controller_Front($services);
    echo $fc->dispatch();

    The $services instance gets passed into each of the page controllers. It has methods like request(), session(), createView($path, $data). Does that seem like a reasonable approach/solution?

    - matt

  13. #13
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by mwmitchell View Post
    Does that seem like a reasonable approach/solution?
    It's basically a service locator, and pretty much the same thing as stereofrog's Config class. In any case, yes, it is a reasonable approach, and you could gradually refactor it into full on Dependency Injection if you ever see the need.

  14. #14
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just an update here... I've decided to go with a simple locator. It's passed into the front controller to start. Once it's passed into the page controller, the page controller "attaches" itself to the locator (Locator::setPage($page)). That way the locator can use the page as a context when finding page related objects (views etc.).

    I'm using the magic __call function in the page controllers to quietly call the locator. That means I can override the method inside of the page controller. I instantiate everything but my page controllers in the locator. And I don't call the locator anywhere directly in my app, so nothing but the startup script knows the name of the class.

    If a model needs something from the locator (for example), remember that the model is being called from a controller somewhere, so that means the model is being created from the locator. So from inside of the locator, I "inject" whatever the model needs:

    function model($class)
    {
    $class = 'Vendor_Namespace_' . $class;
    $model = new $class($this->connection());
    $model->upload_dir = UPLOAD_DIR;
    return $model;
    }

    And to call that from within the page controller, I just do this:

    $model = $this->model('Category')

    It's a nice and simple system!


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
  •