Search code examples
phpclassoopchain

Chaining methods and passing variables


Let's say I have this:

class "classname"
{

    ....

    public function section($id)
    {
        // variable method name
        $this->section->$id = new stdClass();
        return $this;
    }

    public function subsection()
    {
        // $id is not available here
        $this->section->$id->subsection = array();
        return $this;
    }

    ....

}

When I call:

$classname->section("test")
    ->subsection();

It is not working because $id is not global nor set in the second chainlink. Do I have to pass it manually to ->subsection($id) or is there a more generic/cleaner way to get it there?

What I try to accomplish here is to create an (big) object with multiple sections. In these sections objects and/or array's so there are more (chained) methods involved.


Solution

  • You can act like this way:

    class Foo
    {
        protected $section;
        private $lastUsedId = null;
    
        public function __construct()
        {
           $this->section = new StdClass();
        }
    
        public function section($id)
        {
            // variable method name
            $this->section->$id = new StdClass();
            $this->lastUsedId = $id;
            return $this;
        }
    
        public function subsection()
        {
            // $id is not available here
            $this->section->{$this->lastUsedId}->subsection = array();
            return $this;
        }
    }
    

    so

    $obj = (new Foo())
       ->section('one')
       ->subsection()
       ->section('two')
       ->subsection();
    

    will produce valid result like

    object(Foo)#1 (2) {
      ["section":protected]=>
      object(stdClass)#2 (2) {
        ["one"]=>
        object(stdClass)#3 (1) {
          ["subsection"]=>
          array(0) {
          }
        }
        ["two"]=>
        object(stdClass)#4 (1) {
          ["subsection"]=>
          array(0) {
          }
        }
      }
      ["lastUsedId":"Foo":private]=>
      string(3) "two"
    }
    

    Note, that it isn't a good idea to use chaining like this way - it's difficult to read, and, besides, having method that actually changes data, but looks like getter, is confusing.