SitePoint Sponsor

User Tag List

Results 1 to 14 of 14
  1. #1
    SitePoint Wizard tgavin's Avatar
    Join Date
    Feb 2003
    Location
    FL
    Posts
    1,051
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    OOP Calling one class from within another

    I have a class for building forms and I want to upgrade it to handle Bootstrap. I'd like to use my current methods to build the Bootstrap elements, but I don't want all of this new Bootstrap code cluttering up my current Forms class, so I'd like to put it into its own class in a separate file.

    Below is a very simple example; obviously this doesn't work and the code is wrong, however, from this you can hopefully see what I'm trying to accomplish.


    PHP Code:
    <?php
    class Forms {
        
        public function 
    input_text($name) {
            return 
    '<input type="text" name="'.$name.'">';
        }
    }


    class 
    Bootstrap {
        
        public function 
    bootstrap_input_text($name,$label) {
            
    $return  '<div class="control-group">';
            
    $return .= '<label class="control-label" for="'.$name.'">'.$label.'</label>';
            
    $return .= '<div class="controls">';
            
            
    // use the input_text() method from the Forms class
            
    $return .= $this->Forms->input_text($name);
            
            
    $return .= '</div>';
            
    $return .= '</div>';
            return 
    $return;
        }
    }


    $form = new Forms();

    echo 
    $form->input_text('name');
    echo 
    $form->bootstrap_input_text('email','Email');

  2. #2
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,387
    Mentioned
    45 Post(s)
    Tagged
    12 Thread(s)
    The way you're trying to do it is (almost) the decorator pattern. What you'd need to do is require an instance of Forms to be passed in to Bootstrap via the constructor, like this:
    PHP Code:
    class Bootstrap {

        protected 
    $forms;
        
        public function 
    __construct(Forms $forms) {
            
    $this->forms $forms;
        }
        
        public function 
    input_text($name$label) {
            
    $return  '<div class="control-group">';
            
    $return .= '<label class="control-label" for="'.$name.'">'.$label.'</label>';
            
    $return .= '<div class="controls">';
            
            
    // use the input_text() method from the Forms class
            
    $return .= $this->forms->input_text($name);
            
            
    $return .= '</div>';
            
    $return .= '</div>';
            return 
    $return;
        }

    }

    $form = new Bootstrap(new Forms);
    echo 
    $form->input_text('email''Email'); 
    Another approach would be to have Bootstrap inherit from Forms. If you kept the method names the same and made the $label parameter optional, you'd have a drop-in replacement:
    PHP Code:
    class Bootstrap extends Forms {
        
        public function 
    input_text($name$label null) {
            if (empty(
    $label)) {
                
    $label ucfirst($name);
            }
            
    $return  '<div class="control-group">';
            
    $return .= '<label class="control-label" for="'.$name.'">'.$label.'</label>';
            
    $return .= '<div class="controls">';
            
            
    // use the input_text() method from the Forms class
            
    $return .= parent::input_text($name);
            
            
    $return .= '</div>';
            
    $return .= '</div>';
            return 
    $return;
        }
    }

    $form = new Bootstrap();
    echo 
    $form->input_text('email''Email'); 
    The method names remain identical, and you use the parent::method syntax to get the results from the original Forms methods.

  3. #3
    SitePoint Wizard tgavin's Avatar
    Join Date
    Feb 2003
    Location
    FL
    Posts
    1,051
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Dude, your second example gave me a great idea to make this whole thing more flexible and with a ton less code to boot. You're a genius!

    Thanks!

  4. #4
    SitePoint Addict Banana Man's Avatar
    Join Date
    Dec 2005
    Posts
    391
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    If a class has already been instantiated you can also get access to it from inside methods of another class by using:

    global $class_object;

  5. #5
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,387
    Mentioned
    45 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by Banana Man View Post
    If a class has already been instantiated you can also get access to it from inside methods of another class by using:

    global $class_object;
    I'd advise against using globals, they are rarely (if ever) a good solution. In this situation you'd be creating a hidden dependency within the calling class.

  6. #6
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,135
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Don't use globals. Globals make code more difficult to maintain and less reusable. Look into dependency injection or a DIC (dependency injection container).
    The only code I hate more than my own is everyone else's.

  7. #7
    SitePoint Addict Banana Man's Avatar
    Join Date
    Dec 2005
    Posts
    391
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the advice. I had been wondering about this myself. Can't remember where I picked it up from but I have been using it in all my classes!

  8. #8
    SitePoint Wizard tgavin's Avatar
    Join Date
    Feb 2003
    Location
    FL
    Posts
    1,051
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by fretburner View Post
    The way you're trying to do it is (almost) the decorator pattern...
    Just wanted to give you an update. I spoke with other people regarding this issue and they all told me to use the decorator pattern. So I looked into it, and messed around with it a little bit; it was total overkill for what I was doing, but the inheritance example you gave worked perfectly, so I'm incorporating that into my class.

    Thanks again

  9. #9
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,058
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by tgavin View Post
    Just wanted to give you an update. I spoke with other people regarding this issue and they all told me to use the decorator pattern. So I looked into it, and messed around with it a little bit; it was total overkill for what I was doing, but the inheritance example you gave worked perfectly, so I'm incorporating that into my class.

    Thanks again
    Keep in mind, that the decorator pattern is by far the best choice here. Inheritance can lead you into a very difficult path here as now your Bootstrap class is dependent on Forms. If anything in Forms is changed, it could have a detrimental affect on your class and it won't be obvious. Inheritance only makes sense if you can complete this phrase: "X is a Y", "Bootstrap is a Form". Does that hold true? If it doesn't, you should go with the dependency injection approach (the decorator pattern).
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  10. #10
    SitePoint Wizard tgavin's Avatar
    Join Date
    Feb 2003
    Location
    FL
    Posts
    1,051
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The problem that I had with the decorator pattern is that, unless i'm mistaken, i have to add every single method that could be used publicly in my Forms class to my Bootstrap class, which was a huge pain in the ass. I kept receiving errors that a method wasn't available, and then after I added it to the Bootstrap class the error would stop. And wouldn't every new decorator class need to have all of the methods added to it as well?

    All i'm doing in the Bootstrap class is wrapping HTML around my form objects.

    I definitely want to do this the right way, so I'm totally open to whatever anybody has to say.

  11. #11
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,058
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    I think you should look into the magic method __call that would reduce what you need to build (if all methods utilize the same bootstrap surrounding html.
    http://www.php.net/manual/en/languag...hp#object.call

    You could effectively have 1 method in your Bootstrap, that takes the method to call for Forms and any additional arguments for that method.
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  12. #12
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,058
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Just want to show proof of concept using __call, I think you'll like this

    test.php
    PHP Code:
    <?php
    require_once('forms.class.php');
    require_once(
    'bootstrap.class.php');

    $forms = new Forms();
    $boot = new Bootstrap($forms);
    echo 
    $boot->input_text('textbox''Name');
    echo 
    $boot->input_hidden('hidden''Spam Detector');
    bootstrap.class.php
    PHP Code:
    <?php
    class Bootstrap {
        private 
    $forms;
        public function 
    __construct($forms)
        {
            
    $this->forms $forms;
        }

        public function 
    __call($member$arguments)
        {
            
    $return  '<div class="control-group">';
            
    $return .= '<label class="control-label" for="'.$arguments[0].'">'.$arguments[1].'</label>';
            
    $return .= '<div class="controls">';

            
    $return .= $this->forms->$member($arguments[0]);

            
    $return .= '</div>';
            
    $return .= '</div>';
            return 
    $return;
        }
    }
    forms.class.php
    PHP Code:
    <?php
    class Forms {
        public function 
    input_text($name) {
            return 
    '<input type="text" name="'.$name.'">';
        }
        public function 
    input_hidden($name) {
            return 
    '<input type="hidden" name="'.$name.'">';
        }
    }
    Generated HTML (formatting added for visual effect)
    HTML Code:
    <div class="control-group">
      <label class="control-label" for="textbox">Name</label>
      <div class="controls">
        <input type="text" name="textbox">
      </div>
    </div>
    <div class="control-group">
      <label class="control-label" for="hidden">Spam Detector</label>
      <div class="controls">
        <input type="hidden" name="hidden">
      </div>
    </div>
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  13. #13
    SitePoint Wizard tgavin's Avatar
    Join Date
    Feb 2003
    Location
    FL
    Posts
    1,051
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    OK, that's beautiful.

    I was looking through various websites and watched a couple video tutorials on magic methods and couldn't wrap my head around what you were proposing, so thank you for the supplied code! That really made a difference.

    Yeah, this looks like the way to go, because I can see a whole bunch of classes for element wrappers. Really simple, easy to use and quickly extendable.

    Thanks!

  14. #14
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,058
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Your welcome. If you run into any issues/questions as you implement it, feel free to ask them. I think you'll find this process will work out very well.
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes


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
  •