Please provide feedback on the following approach to creating a model layer, which consists of business rules that use Doctrine to access data.
My current approach is based on the notion that a model is a ContainerAware class / object that contains all the non-library, domain business logic.
I find that I have to hammer in Frameworks to do something this way, so part of my brain asks me a question.
I am currently using Symfony 2, which, like all modern PHP MVC frameworks, uses an ORM layer, such as Doctrine 2, and inevitably treats it as a model layer. I assume that the situation will be similar to ZF2, so although my example is written in SF2, consider this as an agnostic framework question.
Specific example
As a specific example, consider the following scenario:
Message Requirement
- As a user, I can create a message that belongs to me.
- As a user, I can update the message that belongs to me.
- As a user, I can archive a message that belongs to me.
Controller
In Symfony2, these requirements are encoded as Controller Level Actions. I will skip the extraneous code that checks if the message really belongs to the user, but obviously this should also be part of the domain logic. In the method "belongs to TOUser" or the like.
// Vendor\MessageBundle\DefaultController public function archiveAction(Request $request) { // ... $em = $this->getDoctrine() ->getManager(); $message = $em->getRepository('MessageBundle:Message'); ->getManager() ->getRepository('MessageBundle:Message') ->find($request->get('id')); $message->setIsArchived(true); $em->persist($entity); $em->flush(); $this->flashMessage('Message has been archived.'); // ... }
Model
If I were placed in a model, it would look like this:
class MessageModel { public function archive($messageId) { // ... $em = $this->getDoctrine() ->getManager(); $message = $em->getRepository('MessageBundle:Message') ->find($messageId); $message->setIsArchived(true); $em->persist($entity); $em->flush(); // ... return true on success, false on fail. } }
Revised controller
My modified controller will now look like this:
// Vendor\MessageBundle\DefaultController public function archiveAction(Request $request) { // ... $model = new MessageModel(); // or a factory. $result = $model->archive($request->get('id')); if($result) { $this->flashMessage('Message has been archived.'); } else { $this->flashMessage('Message could not be archived due to a system error.'); } return array('result'=>$result); // ... }
The remaining two requirements will also be implemented on the model.
My approach in a nutshell
In short, this is my current approach:
- Controller - less logic
- View - remains the same
- Model - is aware of the container and contains all the business logic, refers to Doctrine as access to data.
- ORM . It is considered as part of the model layer, but is not considered as a model layer.
- Service level - if necessary, I can use the service level to work with several layers, but I found that I had to use it in several cases due to the simple nature of the applications that I had to create.
My question (s)
- Is my approach what others do?
- Am I missing something obvious?
- Have you tried something like this and considered it good / bad?
Thanks in advance.