-1

I have 5 different model classes. Each one handles different types of data, but they all follow the same format.

I want each kind of model to share functions like toJson, toArray, etc.

There's a bunch of hack ways I could do this, and I can figure those out, but there HAS TO be a better way.

Here's my desired effect:

<?
something here{
    function toJson(){return json_encode($this->lastResult);}
}
class applications{
    function findAllFoo(){
        $this->lastResult = $this->db->foo->find();
        return $this;
    }
}

class users{
    function findAllBar(){
        $this->lastResult = $this->db->bar->find();
        return $this;
    }
}
$models = new models();

echo $models->applications->findAllFoo()->toJson();
echo $models->users->findAllBar()->toJson();

I could just do new applications($this) passing the models each an entire set of classes, but I'm sure there's a way within standard practices to do this.

TL;DR - How do I have a set of classes that shares some helper functions, yet won't conflict with the __construct function?

tereško
  • 58,060
  • 25
  • 98
  • 150
Kavi Siegel
  • 2,964
  • 2
  • 24
  • 33
  • Trying to figure out a structure where 5 different types of models can use functions defined outside of themselves, while not extending a parent, as that would connect to the database multiple times. – Kavi Siegel Dec 01 '12 at 13:01
  • Please avoid radical changes to your question unless they actually improve the question and clarify what you're asking for. Currently, your text seems not to talk about the presented code. – Niko Dec 01 '12 at 13:03
  • @KaviSiegel , there is a difference between a "question" and "statement". Though, [this](http://stackoverflow.com/a/11369679/727208) *(shameless plug)* might be what you are actually looking for. – tereško Dec 01 '12 at 13:05
  • 1
    @Niko Please avoid rolling back my edits that were intended to clarify my question. That's my discretion, not yours, and it's terribly rude. I apologize if you don't understand what I'm asking, but hey, sometimes it takes a few edits to phrase things right. – Kavi Siegel Dec 01 '12 at 13:09
  • @tereško reading through that post, it's kind of similar to what MongoDb already does for it's connect method. It's kind of opposite to what I'm looking to do though, I want to share the connection, not re-run the connection code. Thanks for the link though - a good read! – Kavi Siegel Dec 01 '12 at 13:23
  • connection code is executed only **once** in that example, @KaviSiegel – tereško Dec 01 '12 at 13:24
  • @tereško Looks like my organic brain-based php compiler isn't too reliable. I figured each `create()` in there would re-run the `$provider` function – Kavi Siegel Dec 01 '12 at 13:33
  • also, on a different note: please read about *Law of Demeter*. – tereško Dec 01 '12 at 14:42

2 Answers2

3

This is a bad idea. Parents should not know about their children. A class should stand on its own, working as-is. A child may extend a parent and use it, since that relationship is codified and enforced through the x extends y declaration. There's no such relationship for parents to children though, so just looking at a class it's impossible to say whether it will work or not, since it's unknown if the child exists at implementation time.

Rethink what you're doing. Children extend already complete parents, parents don't rely on children.

deceze
  • 510,633
  • 85
  • 743
  • 889
  • one way to "emulate" this would be using PHP 5.4 traits, right? – pocesar Dec 01 '12 at 12:54
  • Fair enough. Pardon my terrible explaining of the situation, the example I had posted was just along the lines of how I thought it could look - seems you took it more literally than intended. I edited the post a bit and clarified what I'm trying to do. I'm trying to make the parent irrelevant, but I want to be able to do toJson in all the models without defining it in each and without connecting to the database multiple times. – Kavi Siegel Dec 01 '12 at 12:58
  • 1
    @Kavi For database dependencies, you should follow the dependency injection principle. Instantiate one database and pass it into the objects that need it at instantiation time. For shared functionality, you can use inheritance where it makes sense. Or traits, where it makes sense. Or interface declarations with custom implementations in each class where it makes sense. I'm not really sure what you're going for, but parents instantiating children is a no no. You're also violating the [Law of Demeter](http://en.wikipedia.org/wiki/Law_Of_Demeter) BTW. – deceze Dec 01 '12 at 13:02
  • @deceze Thanks for the link, and I'll do some research on the other things you mentioned. I'm going from a life of scripted style spaghetti code into trying to structure things right, so I really appreciate it. Passing the object is how I'm actually doing it already, but I thought it was kinda unclean, although I'm not up to par on these types of standards. Inheritance, I think, could work here if I avoid connecting to the DB in the construct and keep the helper functions as JUST that. I'll look into traits and interface declarations now. – Kavi Siegel Dec 01 '12 at 13:16
  • @Kavin http://kunststube.net/static may also be interesting to you. Just try to focus on classes as self-contained entities which get everything *external* they need explicitly passed as dependency. – deceze Dec 01 '12 at 13:49
1

If all models share some functionality, why not have a "Model" base class for them?

abstract class Model {
    protected $lastResult;
    protected $db;

    public function __construct(DB $db) {
        $this->db = $db;
    }

    public function toJson() {
        return json_encode($this->lastResult);
    }
}

class Applications extends Model {
    public function findAllFoo() {
        $this->lastResult = $this->db->foo->find();
        return $this;
    }
}

class ModelBroker {
    public $applications;

    public function __construct() {
        $db = /* ... connection code here ... */;
        $this->applications = new Applications($db); // <-- dependency injection
    }
}

$models = new ModelBroker();
$json = $models->applications->findAllFoo()->toJson();
Niko
  • 26,516
  • 9
  • 93
  • 110
  • Ah, now that makes a lot of sense. Where you broke out `ModelBroker` into a separate class, I for some reason tried to put the connection in the abstract. I'm new to working with this stuff, I appreciate you answering with such a good answer after I was snappy back at ya up there in the comments for editing me! – Kavi Siegel Dec 01 '12 at 13:30
  • Question on how you defined `__construct(DB $db)` - defining it as DB, does it have any significance? What's that called? – Kavi Siegel Dec 01 '12 at 15:15
  • Glad if I can help - and you actually had a point there, the rollback was probably not the best choice. -- That `DB $db` thing is called ["type hinting"](http://php.net/manual/en/language.oop5.typehinting.php) and requires the parameter $db to be an instance of a class named "DB". You will need to adjust that to the actual class name of your database connection (or just strip it completely, it's an optional feature). But usually, it's a good idea to specify what type of object you're expecting. – Niko Dec 01 '12 at 16:03