Why bother with repositories

Viewed 176

I feel a bit stupid asking this question since there are a lot of resources talking and explaining mappers and repositories but I can't seem to get my head around it. So I've created some example code to explain my confusion. Please note that I don't know if this code would actually work I wrote this as an example.

This would be the entity / class (Quote.php)

class Quote {
  private $id;
  private $author;
  private $content;

  public function getId() {
    return $this->id;
  }

  public function getAuthor() {
    return $this->author;
  }

  public function getContent() {
    return $this->content;
  }

  public function setId(int $id) {
    $this->id = $id;
  }

  public function getAuthor(string $author) {
    $this->author = $author;
  }

  public function setContent(string $content) {
    $this->content = $content;
  }
}

And this would be the mapper (QuoteMapper.php)

class QuoteMapper {
  private $PDO;

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

  public function find(int $id = null, string $search = null) {
    if (!empty($id) && !empty($search)) {
      //Search for id and search word
      $stmt = $this->PDO->prepare("SELECT `id`, `author`, `content` FROM `quotes` WHERE `id` = :id AND `content` LIKE :search LIMIT 1");
      $stmt->bindParam('search', $search, PDO::PARAM_INT);
      $stmt->bindParam('id', $id, PDO::PARAM_INT);
    else if (!empty($id)) {
      //search for id only
      $stmt = $this->PDO->prepare("SELECT `id`, `author`, `content` FROM `quotes` WHERE `id` = :id AND `content` LIKE :search LIMIT 1");
      $stmt->bindParam('id', $id, PDO::PARAM_INT);
    } else if (!empty($search)) {
      //search for search word only
      $stmt = $this->PDO->prepare("SELECT `id`, `author`, `content` FROM `quotes` WHERE `id` = :id AND `content` LIKE :search LIMIT 1");
      $stmt->bindParam('search', $search, PDO::PARAM_INT);
    }

    $stmt->execute();

    $stmt->bindColumn('id', $id);
    $stmt->bindColumn('author', $author);
    $stmt->bindColumn('content', $content);
    $stmt->fetch();

    $quote = new Image();
    $quote->setId($title);
    $quote->setAuthor($source);
    $quote->setContent($alternative);

    return $image;
  }

  public function save(Quote $quote) {
    //A save function
  }

  public function delete(Quote $quote) {
    //A delete function
  }
}

Last but not least, this would be the repository (QuoteRepository.php)

class ArticleRepository {
  private $articleMapper;

  public function __construct(ArticleMapper $articleMapper) {
    $this->articleMapper = $articleMapper;
  }

  public function find(int $id = null, string $search = null) {
    $article = $this->articleMapper->find($id, $search);
    return $article;
  }

  public function save(Quote $quote) {
    $this->articleMapper->save($user);
  }

  public function delete(Quote $quote) {
    $this->articleMapper->delete($user);
  }
}

As I understand my mapper isn't 'wrong' since the purpose of the mapper is to do things such as get and set data from persistent data storage (such as MySQL)

A Data Mapper is a Data Access Layer that performs bidirectional transfer of data between a persistent data store (often a relational database) and an in-memory data representation (the domain layer). From Wikipedia

But my repository doesn't actually do anything. It just passes the function call along to the mapper? So I can only assume that my mapper contains code that should be in the repository, but what code would that be? Or perhaps I've completely misunderstood how data mappers and repositories would work together.

If there are any other things that I have done that are wrong or considered bad practice I would like to hear it. I'm really trying to figure this out! :)

1 Answers

DataMapper is a layer to isolate an application from a concrete database. It transforms an object into a record of a database and a record into an object. DataMapper gives us the ability to work with Database and be unaware of what RDBMS we use. Example:

interface DataMapperInterface
{

    /**
     * Find objects by a criteria
     *
     * @param array $criteria Search params
     * @return Quote[] Found entities
     */
    public function find(array $criteria);

    /**
     * Insert an object into a database
     *
     * @param Quote $object Object that will be inserted
     */
    public function insert(Quote $object);

    /**
     * Update an object date in a database
     *
     * @param Quote $object Object that will be updated
     */
    public function update(Quote $object);

    /**
     * Remove an object from a database
     *
     * @param Quote $object Object that will be removed
     */
    public function delete(Quote $object);
}

Repository is a layer for encapsulation of logic of a query building. It gives us the ability to work with a collection of objects and be unaware to work with Database anything.

class Repository
{

   /**
    * @var DataMapperInterface Mapper to transform objects
    */ 
   protected $mapper;

   /**
    * Constructor
    *
    * @param DataMapperInterface $mapper Mapper to transform objects
    */
   public function __construct(DataMapperInterface $mapper)
   {
       $this->mapper = $mapper;
   }

   /**
    * Find all objects
    *
    * @return Quote[] Found entities
    */
   public function findAll()
   {
       return $this->mapper->find([]);
   }

   /**
    * Find an object by an identifier
    *
    * @return Quote[] Found entities
    */
   public function findById(integer $id)
   {
       $criteria = ['id' => $id];
       return $this->mapper->find($criteria);
   }

   /**
    * Find objects by an author name
    *
    * @return Quote[] Found entities
    */
   public function findByAuthor($name)
   {
       $criteria = ['author' => $name];
       return $this->mapper->find($criteria);
   }

   /**
    * Save an object into the repository
    */
   public function save(Quote $object)
   {
       if (empty($object->id)) {
           $this->mapper->insert($object);
       } else {
           $this->mapper->update($object);
       }
a    }

   /**
    * Remove an object from the repository
    */
   public function remove(Quote $object)
   {
       $this->mapper->delete($object);
   }
}

This is the very simple instance and all is more difficult in a real application: queries are bigger, repository can cooperate with many other patterns (Query Object to query building, Unit of Work to track changes, Identity Map to avoid repeatedly-load of objects, etc.)

Related