cj5
cj5

Reputation: 795

PHP: OOP design and how to reuse a method across similar classes

I am familiar with extending parent classes, but I'm lost on how to create a class that inherits a working method from a parent, that may change depending on the class it is in. For example I have a model class called Log_Metadata for a database table that stores metadata for logs. I also have a class called Station_Metadata for a table that stores metadata for stations. The table schema is similar except for the control fields for the ID of either the station or the log, but the rest of the schema is the same as each metadata row has an ID, a meta_key column, and a meta_value column.

I basically want to create a common method for these models that will allow me to add/edit/delete the metadata across various models. Would using an abstract class be pertinent in this case or an interface? How do I overwrite the method if something varies on the model end of things?

Upvotes: 1

Views: 1491

Answers (2)

wtfzdotnet
wtfzdotnet

Reputation: 1062

Think for starters this should be your initial setup;

  • Metadata ( interface, define the basics of what metadata is, in any shared condiiton )
  • MetadataAbstract ( implements Metadata, the interface and provide base methods that all metadata share )
  • Metadata\Log ( add properties and log specific methods )
  • Metadata\Station ( add properties and ... )

Furthermore you might want to consider changing your table structure to something more like

  • Metadata ( shared columns )
    • Metadata_Log ( log specific )
    • Metadata_Station ( station specific )

Where both specific tables have a relationship to your metadata table ( 1-1 ). You could then query you metadata_log / metadata_station tables and join in the results from your main metadata table.

As others have suggested implementing something as awesome as Doctrine2 would take care of this with discriminator mapping. However it might be out of range for your application?

I hope this gives you some more insight :-)

Edit 1 _Did this on the quick tour... so bug me if I missed out something, I think this is what you mean. Provided there's no "concrete" implementation of your specifics other than the naming.

<?php
namespace Example;

    interface Metadata {
        function getKey();
        function getValue();
    }

    abstract class AbstractMetadata implements Metadata {
        abstract protected function getKey()
        {
            return 5;
        }

        protected function getValue()
        {
            return 3;
        }
    }

    // use Example\AbstractMetadata;

    class LogMetadata extends AbstractMetadata {
        public function getKey()
        {
            return 1;
        }

        public function getValue()
        {
            return 2;
        }
    }

    // use Example\AbstractMetadata;

    class StationMetadata extends AbstractMetadata {
        public function getKey()
        {
            return 2;
        }

        public function getValue()
        {
            return parent::getValue(); // 3
        }
    }

In the above Example, all child classes of the abstract MUST implement the getKey() method, whereas getValue() is optional.

Then later on however you are going to store this you could:

    if ($object instanceof Metadata) {}

    if ($object instanceof LogMetadata) {}

Upvotes: 0

Matt
Matt

Reputation: 5567

I'm not sure that this is actually how you should be designing this. It sounds like you should abstract the common methods out of the classes that hold the data (perhaps you want to use DataMapper?). But if you think this ActiveRecord pattern with inheritance makes sense, I think you are looking to do something like this?

abstract class Base
{
    public function add($data)
    {
        $something = do_stuff($this->getId($data));
    }

    abstract protected function getId($data);
}

class Derived extends Base
{
    private $idKey = 'DerivedKey';

    protected function getId($data)
    {
        return $data[$this->idKey];
    }
}

http://php.net/manual/en/language.oop5.abstract.php

In your case you would have the general methods (CRUD), but call abstract protected methods to get specific things.

This is the Template Method Pattern http://en.wikipedia.org/wiki/Template_method_pattern

Edit: here's another thing you could try if you think you may have classes that add extra columns/metadata/etc.

abstract class Base
{
    public function add($data)
    {
        $something = array();
        $something[] = do_stuff($this->getId($data));
        $something[] = do_stuff($this->getName($data));
        $something[] = $this->additionalFields($data);
    }

    abstract protected function getId($data);
    abstract protected function getName($data);

    protected function additionalFields($data)
    {
        // no additional fields by default
    }
}

class Derived extends Base
{
    private $idKey = 'DerivedKey';
    private $nameKey = 'DerivedName';
    private $other = 'new thing';

    protected function getId($data)
    {
        return $data[$this->idKey];
    }

    protected function getName($data)
    {
        return $data[$this->nameKey];
    }

    protected function additionalFields($data)
    {
        return do_foo($this->other, $data);
    } 
}

Keep in mind, these are all really contrived examples to show you the OOP pattern. I assume that getId for example could be more complex. If you had something that followed this pattern all the time, a better design might just be:

class Base
{
    protected $metadata = array(
        'id' => 'defaultId',
        'name' => 'defaultName'
    );

    public function add($data)
    {
        foreach ($metadata as $key => $value) {
            do_something($key, $data[$value]);
        }
    }
}

class Derived extends Base
{
    public function __construct()
    {
        $this->metadata['id'] = 'DIfferentId';
    }
}

Again, it's just a pattern that you could adapt to your needs.

Upvotes: 2

Related Questions