5

I think I have more or less managed to get a grasp on OOP/Inheritance, and the basics of method chaining I think I understood as well. However I am still confused about how to actually use some of it.

I wanted to do something that I've seen when working with Magento before: In Magento, I've seen some sort of "selector-function" used in method chaining. It's a little hard to put into words, but it was something along the lines of:

$categoryName = Mage::getModel('catalog/category')->load($categoryId)->getName();

It's the load($categoryId) part that interests me, as in, a function that selects some instance of something and allows me to run a function on that specific instance.

Now, I am writing a module that allows me to configure certain promotions on our website. Seeing as we'll have many different promotions and I want them to be easily configurable and modifiable, I wanted to do something similar.

So, if I wanted to be able to do something like this:

$prm = new Promotion();

$prm->addPromo('xmasPromo');
$prm->addPromo('nyPromo');

$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
$prm->getPromo('nyPromo')->setName('Promotion for New Years!');

echo $prm->getPromo('xmasPromo')->getName(); // returns: Promotion for Xmas!
echo $prm->getPromo('nyPromo')->getName(); // returns: Promotion for New Years!

How would the class definition for that have to look like?

This may be much more simple or much more complicated than I anticipate. In either case, thanks a lot!


Edit: So I did some testing around with the info deceze gave me, but I'm still confused. Bad naming and putting 2 classes in 1 file aside, here's what I did:

class file:

class Promotion {
    private $__arr = array();
    public function addPromo($name) {
        $this->__arr[$name] = new Promo();
    }
    public function getPromo($name) {
        $this->__arr[$name];
    }
}

class Promo {
    private $name;
    public function setName($name) {
        $this->name = $name;
    }
    public function getName() {
        return $name;
    }
}

and the run file:

require_once 'class.php';
error_reporting(E_ALL);

$prm = new Promotion();

$prm->addPromo('xmasPromo');
$prm->addPromo('nyPromo');

$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
$prm->getPromo('nyPromo')->setName('Promotion for New Years!');

echo 'X: '.$prm->getPromo('xmasPromo')->getName(); // returns: Promotion for Xmas!
echo "\n";
echo 'N: '.$prm->getPromo('nyPromo')->getName(); // returns: Promotion for New Years!

This gives me Fatal error: Call to a member function setName() on a non-object in /var/www/test/index.php on line 11. But why? Shouldn't getPromo() give me back the object? Thanks again..


Thanks to the great guys here, it works now. In case anyone were to pass by here with the same or a similar question, here's the final, working code:

Classes:

class Promotion {
    private $__arr = array();
    public function addPromo($name) {
        $this->__arr[$name] = new Promo();
    }
    public function getPromo($name) {
        return $this->__arr[$name];
    }
}

class Promo {
    private $name;
    public function setName($name) {
        $this->name = $name;
    }
    public function getName() {
        return $this->name;
    }
}

Test file:

require_once 'class.php';
error_reporting(E_ALL);

$prm = new Promotion();

$prm->addPromo('xmasPromo');
$prm->addPromo('nyPromo');

$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
$prm->getPromo('nyPromo')->setName('Promotion for New Years!');

echo 'X: '.$prm->getPromo('xmasPromo')->getName(); // returns: Promotion for Xmas!
echo "\n";
echo 'N: '.$prm->getPromo('nyPromo')->getName(); // returns: Promotion for New Years!
Flosi
  • 691
  • 3
  • 9
  • possible duplicate of [PHP method chaining?](http://stackoverflow.com/questions/3724112/php-method-chaining) – kapa Jan 14 '14 at 11:47
  • That's called [fluent interface](http://en.wikipedia.org/wiki/Fluent_interface) - so method will return self instance. In some sense it's an anti-pattern – Alma Do Jan 14 '14 at 11:48
  • 1
    Actually the issue I can see is, you should be returning the `PROMO` instance from the `getPromo` method like `return $this->__arr[$name];` – code-jaff Jan 14 '14 at 13:53
  • deceze beat you to it by a minute or two. You're absolutely right of course, I was just too blind to see that I forgot it. :) – Flosi Jan 14 '14 at 13:54

2 Answers2

1

Method chaining is really simple, all it does is use one particular element of PHP's syntax:

When a function returns an object, you can directly continue with -> after that function.

The longhand version can be:

$bar = $foo->bar();
$baz = $bar->baz();
echo $baz;

$foo->bar() returns an object ($bar) which has a method baz(), and that method returns some value ($baz). This can be written in shorthand like so:

echo $foo->bar()->baz();

$foo->bar() still returns an object which has a method baz(), so you can directly call it without assigning it to an intermediate variable. Maybe this makes it more obvious:

echo ( $foo->bar() )->baz();

You're calling the baz() method on whatever $foo->bar() returns.

$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');

As such, in your above case, all you need to do is to return an object which has the method setName from getPromo. I would assume getPromo is supposed to return some object of, say, the Promo class. If the Promo class has a method setName, you're all set.

deceze
  • 510,633
  • 85
  • 743
  • 889
  • Hi. Thanks for your answer. Having the "Promotion" class and "Promo" class (which has setName and getName methods) is easy. But how do I make the addPromo and getPromo functions in the "Promotion" class create or respectively getriebe the appropriate instance of the Promo object? – Flosi Jan 14 '14 at 12:15
  • 1
    I don't know, that's up to you. When you call `addPromo`, the `Promotion` class creates a new `Promo` object (not good naming BTW) and stores it somewhere internally with the given name. When you call `getPromo`, it retrieves that object. Could be as simple as an array, or could be as complex as a database query. It's your decision what you actually want to do here. – deceze Jan 14 '14 at 12:26
  • I appreciate our help. I tried it with arrays, but I still can't seem to get the hang of it. See original post. – Flosi Jan 14 '14 at 13:39
  • 1
    Maybe because you're not `return`ing the value?! – deceze Jan 14 '14 at 13:48
  • Aw jeez. How could I have been so blind. Also forgot to `$this` in getName. Works now. This answers my question, and I really helped me understanding the concept. Thank you very much! – Flosi Jan 14 '14 at 13:52
1

If you want to chain methods you just need to always return the object like this

class Chain {
    public function firstChain() {
         //do something
         return $this;
    }

    public function secondChain() {
          //do some stuff

          return $this;
    }

}

Than when you have an instance of the class you do like this:

$obj = new Chain();

$obj->fistChain()->secondChain();
gprusiiski
  • 430
  • 4
  • 18