1

I am stuck as Php natively does not allow extending multiple classes, i am using laravel models to interact with data and want to use 2 different packages to extend it's functionality.

I am trying to use Ardent a validator and Revisionable which maintains update history but i am not sure how to extend these 2 classes within my models.

Any help will be appreciated.

duellsy
  • 8,497
  • 2
  • 36
  • 60
Aman Virk
  • 3,909
  • 8
  • 40
  • 51

3 Answers3

1

As you know, PHP doesn't support multiple inheritance and unfortunately, this is a PHP deficiency and a problem created by ardent and revisionable package developers. Take Sentry as an example, you can use whatever implementation of Eloquent you want, you just have set the one you need in config.php. Laravel unfortunately has its hands tied, since it is the base class of all those packages. In my opinion.

So you have some options:

1) File an issue on both packages git repositories and hope that one of them changes to support inheritance not only from \Eloquent, but any of the user's classes, extending Eloquent Model abstract class.

You can even change the code of one of those packages and send a pull request to the repository. If it's a good change you probably will see it happening.

2) Use the repository pattern, by creating a class extended from Eloquent, having both of your packages as dependency and, inside it, calling whatever you need from those packages. This will not be a piece of cake, because Eloquent is a complex class and, if you use __call(), you'll have to map all methods you need to forward to your packages, so you don't risk breaking Eloquent's own dynamic calls:

use Ardent;
use Revisionable;

class User extends Eloquent {

    private $ardent;

    privave $revisionable;

    public function __construct(Ardent $ardent, Revisionable $revisionable)
    {
        $this->ardent = $ardent;

        $this->revisionable = $revisionable;
    }

    public function doWhatever() 
    {
        return $this->ardent->do();
    }

    public function __call($name, $arguments)
    {
        if($this->isArdentMethod($name))
        {
            return call_user_func_array(array($this->ardent,$name), $arguments);
        }
        else
        if($this->isRevisionableMethod($name))
        {
            return call_user_func_array(array($this->revisionable,$name), $arguments);
        }
        else
        {
            return parent::__call($name, $arguments);
        }
    }
}

Note that this call

return parent::__call($name, $arguments);

Ensures that all of your calls will be redirected to your Eloquent class, if they are not meant to be executed by one of the others.

As a matter of fact you could even choose one of those, like Ardent as your main class:

class User extends Ardent {}

And just have revisionable as a dependency:

public function __construct(Revisionable $revisionable)

And then forward just revisionable calls to the injected dependency. Because, in the end, those classes are all Eloquent and you can have many Eloquent models pointing to the same tables.

If you look at Revisionable source code you'll that the only call you really have to think of is save(), because it overrides it from Eloquent, all the other you can just call Ardent's.

I know it sounds like a bit too much, but it's doable.

Antonio Carlos Ribeiro
  • 86,191
  • 22
  • 213
  • 204
  • Yes that is the issue, coz laravel does a lot stuff behind the scenes , this will become the real mess the moment i will start using relationships – Aman Virk Feb 22 '14 at 17:00
  • Not if you map ardent's and/or revisionable's methods you use. Those classes are made of Eloquent, calling one method from one or another will make no difference, unless it's not an Eloquent method, of course. – Antonio Carlos Ribeiro Feb 22 '14 at 17:12
  • Filed an issue in Revisionable: https://github.com/VentureCraft/revisionable/issues/36 – Antonio Carlos Ribeiro Feb 22 '14 at 17:21
  • That's great but i think laravel should resolve this internally – Aman Virk Feb 22 '14 at 17:25
  • This is a PHP deficiency and a problem created by ardent and revisionable package developers. Take Sentry as an example, you can use whatever implementation of Eloquent you want, you just have set the one you need in config.php. Laravel unfortunately has its hands tied, since it is the base class of all those packages. In my opinion. – Antonio Carlos Ribeiro Feb 22 '14 at 17:33
  • Yes Sentry seems to be lot better, i also tried following this http://blog.motane.lu/2012/01/21/multiple-inheritance-in-php/ , but it says the eloquent ::find method is not defined – Aman Virk Feb 22 '14 at 17:46
  • There's not magic, it's doing basically what I wrote here, using magic methods (__call) to provide multiple inheritance, it's just that he's storing objects in an array of multiple instances. – Antonio Carlos Ribeiro Feb 22 '14 at 17:50
1

I've been putting a lot of work into making Revisionable a trait recently.

So you'll be able to use it, like this:

class User extends Ardent {
    use \Venturecraft\Revisionable\RevisionableTrait;
}

It's currently in the dev branch, but I'd love some help testing it before merging it to master:

https://github.com/VentureCraft/revisionable/issues/36

[EDIT] This is now in the master branch, and available for public use.

Abayomi Israel
  • 589
  • 6
  • 14
duellsy
  • 8,497
  • 2
  • 36
  • 60
  • Sure i will check it out and will start playing with it, as i have a real time idea in my mind i can test it for all scenarios and will report back in case of any issues – Aman Virk Feb 26 '14 at 03:49
  • fantastic, there's quite a bit thread going on in that github issue, so feel free to jump in and get involved. Hopefully this new implementation will also 'solve' this question. – duellsy Feb 26 '14 at 03:51
  • Anyone using the way/database package with venturecraft/revisionable? The two don't seem to gel, even though way/database extends Eloquent like laravelbook/ardent does – Thomas Jul 06 '14 at 21:06
0

As you say, PHP doesn't support multiple inheritance, so you don't have many options. As ugly as it is, you may have to resort to something like this, which uses the __call() magic method.

Community
  • 1
  • 1
George Brighton
  • 5,131
  • 9
  • 27
  • 36