Accessing Data
A special model is intended for accessing data: the Data Access Object (DAO). A DAO is responsible for creating, managing and deleting records. A brief introduction follows here.
The Data Access Object (DAO)
The Data Access Object is a model. Create the class app/qs/model/BlogDao.php. The basic skeleton looks as follows:
<?php
namespace qs\model;
use n2n\context\RequestScoped;
use n2n\persistence\orm\EntityManager;
use qs\bo\BlogArticle;
use n2n\io\managed\File;
use qs\bo\BlogComment;
class BlogDao implements RequestScoped {
private $em;
private function _init(EntityManager $em) {
$this->em = $em;
}
// further methods follow here...
}
First note the RequestScoped interface. By implementing RequestScoped it is achieved that the class is instantiated only once per request. Besides RequestScoped there is also SessionScoped or ApplicationScoped. Such models are Lookupable. This means that they can be fetched via Dependency Injections in magic methods. Lookupables execute the _init method on instantiation, which must be private.
The _init() method is magic. This means that we can request the EntityManager via a Dependency Injection. With the EntityManager we can access business objects. Because we need it for all our accesses to the business objects, we store it on the object.
Reading Business Objects
For the overview of our module we need a method that returns all BlogArticle to us:
/**
* @return \qs\bo\BlogArticle[]
*/
public function getOnlineBlogArticles() {
$criteria = $this->em->createSimpleCriteria(BlogArticle::getClass(), array('online' => true),
array('id' => 'DESC'));
return $criteria->toQuery()->fetchArray();
}
To do so we create a SimpleCriteria on the EntityManager ($em). It can handle the following parameters:
| Parameter | Parameter | Example |
|---|---|---|
\ReflectionClass | The class of the objects being searched for | BlogArticle::getClass() |
array $matches | Criteria for the selection. Must be passed as an array. | array('online' => true) ensures that only articles marked as online are read out |
array $order | The order in which the objects are returned. | array('id' => 'DESC') ensures that the objects are returned by ID descending. |
$limit | Limit | |
$num | Number of objects | if null, all are returned. |
To get the objects back, we have to convert the criteria into a query (toQuery()) and return all objects in an array (fetchArray()). So with getOnlineBlogArticles() we get an array with all blog articles that are set online. The newest first.
In the detail view we need a single article. But we only know the URL of the article, therefore we have to fetch the article via the property urlPart:
/**
* @param string $urlPart
* @return \qs\bo\BlogArticle
*/
public function getBlogArticleByUrlPart(string $urlPart){
$criteria = $this->em->createSimpleCriteria(BlogArticle::getClass(), array('urlPart' => $urlPart));
return $criteria->toQuery()->fetchSingle();
}
Here too we build a criteria and pass the $urlPart via $matches. Because we expect only one object, we return only the found object via fetchSingle. This way we get a single object back and not an array!
Attention
->fetchSingle()throws an exception if more than one result is found. You may only use the method if you are sure that the query can only have one result!
Question
How could you ensure that the method
getBlogArticleByUrlPart()only returns an article that is also online?
We will also need a method later to fetch a BlogArticle by its ID. Here's how:
/**
* @param int $id
* @return \qs\bo\BlogArticle
*/
public function getBlogArticleById(int $id) {
return $this->em->find(BlogArticle::getClass(), $id);
}
We could of course also have done this via $em->createSimpleCriteria(). But with the find() method an even simpler possibility is available to us here.
Saving Business Objects
Last but not least, we also want to have the possibility to save a comment.
public function saveComment(BlogArticle $blogArticle, string $email, string $content, File $image = null) {
$comment = new BlogComment();
$comment->setBlogArticle($blogArticle);
$comment->setEmail($email);
$comment->setContent($content);
$comment->setImage($image);
$this->em->persist($comment);
}
So that we can save a comment, we have to know for which article we are saving it. That is why we expect a BlogArticle. Furthermore we necessarily expect an $email and some $content. Whether the visitor also wants to save an image we want to leave voluntary, therefore we allow null for $image.
The content of the method consists of creating a new comment and passing the properties via the setter methods. To save the comment we have to persist it ($this->em->persist($comment))
That's all! Now we have defined our business objects and can access them. Next we build the business objects and our DAO into our controller and our views!