0
\$\begingroup\$

The past few weeks I've been trying to grasp on nearly any Design Pattern ever invented, but the repository pattern is one I could understand and implement it myself in my project. My application uses the MVC pattern where I've got the "V" and the "C" part covered in a very basic way, and I want to get rid of a single model "M" doing everything the view needs. I would like to know if I'm somewhat in the right direction. Along with that I ask some questions on parts of the approach.

Let's get started with some code, the whole operation is to get the latest log events from an external data source (database):

Controller

My controller creates a new Repository object, which takes the Database wrapper and the Data Mapper as its arguments. The Data Mapper takes a Domain Entity.

How would I take care of these 4 require_once statements in my Controller functions?

public function index() {
    // need a way to not have to require these classes all the time
    require_once '../app/domain/repositories/EventRepository.php';
    require_once '../app/domain/interfaces/EventRepositoryInterface.php';
    require_once '../app/domain/mappers/EventMapper.php';
    require_once '../app/domain/entities/Event.php';

    $events = new EventRepository(new Database('db'), new EventMapper(new Event()));

    try {
        $return = $events->fetchAll();
    } catch (DatabaseException $e) {
        $return = $e->getErrorMessage();
    }

    $this->view('/home/dashboard', ['return' => $return]);
}

Repository

class EventRepository implements EventRepositoryInterface {

    protected $dbh;
    protected $eventMapper;

    public function __construct(Database $dbh, EventMapper $eventMapper) {
        $this->dbh = $dbh;
        $this->eventMapper = $eventMapper; 
    }

    public function fetchAll() {
        $data = $this->dbh->sql_select('SELECT datetime, message FROM eventlog');

        return $this->eventMapper->toEvent($data);
    }

}

Repository Interface

interface EventRepositoryInterface {
    public function fetchAll();
}

Data Mapper

I'm using a Data Mapper in my Repository Pattern approach. Not sure if this is a right way to do it, but I've seen examples of implementing Data Mappers with repositories.

class EventMapper {

    protected $collection = [];
    protected $event;

    public function __construct(Event $event) {
        $this->event = $event;
    }

    public function toEvent($data) {
        foreach($data as $value) {
            $this->event->setDateTime($value['datetime']);
            $this->event->setMessage($value['message']);

            $this->collection[] = $this->event;
        }

        return $this->collection;
    }
}

Domain Entity

class Event {

    protected $id;
    protected $dateTime;
    protected $message;
    protected $type;

    public function setDateTime($dateTime) {
         $this->dateTime = $dateTime;
    }

    public function getDateTime() {
         return $this->dateTime;
    }

    public function setMessage($message) {
        $this->message = $message;
    }

    public function getMessage() {
        return $this->message;
    }

}

This code leaves me with an array of objects which I can do other stuff with it. Eventually the data from this array needs to be converted to an HTML table. Where would I put such a class (like TablePrinter.php) in my application?

\$\endgroup\$
  • \$\begingroup\$ One note: The real design patterns from GoF are not "invented"... They were identified. \$\endgroup\$ – oopexpert Jul 1 '16 at 20:58
  • \$\begingroup\$ Are you using Composer and the autoloader? That could help you get rid of those require_once statements! getcomposer.org/doc/01-basic-usage.md#autoloading \$\endgroup\$ – wmichelin Oct 27 '16 at 18:57
1
\$\begingroup\$

The autoloading problem.

Using composer is probably the easiest way to autoload classes in PHP right now. You'll only have to declare namespaces, use them, and everything will work with only including the autoload file from composer once.

About your repository pattern implementation.

"Repository Interface"

Nope. Not true. You created an EventRepositoryInterface. The point of an interface is to expose an interface that can be shared by classes implementing it, to be able to modify them (or create other implementations) without having to change the code using them in the first place. Are you going to create a new EventRepository class ?

I'm pretty sure you actually wanted a RepositoryInterface that EventRepository will implement (as well as your other repositories).

EventRepository

This class should be able to handle the $data to make Events one by one, thanks to EventMapper.

Right now its only use is to avoid duplicating SQL requests through the code.

It should check the $data, loop over it, do whatever is needed to get a standard data format (such as an array). Then it should loop over this formatted data, pass each iteration to EventMapper and return an array of Events.

EventMapper

As said earlier, this class should only receive data for one Event and return only one aswell. Also, even though you should have sanitized $data beforehand, you still should check it in this class too. To end with a little nitpicking there, toEvent($data) is usually called hydrate($data).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.