Skip to main content
Topic: A Sexy Model (Read 1462 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

A Sexy Model

In the future, it would be great if the model was broken down to classes for the Entity, the Manager, and Helpers. Each with their own separate responsibilities. Each which can be tested easily. I'm going to list the idea out for you.

Entity
The entity is the holder of the data. Each entity can be taken on its own and used anywhere. It can be converted to any format and can be stored in any format. The entity also handles its own validation. An entity isn't self-forming though. It can't be created by itself. It needs a manager. You want to keep the entity pretty minimal, while still ensuring that it can validate itself. Entities must be testable without a Manager.

Manager
The manager is where you do all of your CRUD. It's a factory. The manager interacts with the storage. It also does smaller changes like updating a single field. The manager "hydrates" the entity. It does batch changes. To give you an idea of what would be a manager - any database table that doesn't start with "log" is probably something that needs a manager. Even log tables need a manager, but you might find that they are better suited as another set of methods in another manager. I just keep adding to my managers until I feel they are too big to handle and then break them down. I can then inject Manager A in to Manager B if need be. I often see people storing the entities in the Manager class. I think this is a poor, wasteful design pattern. You should let the controller do that so you have better control. Many methods in the Manager will require functional tests, not just unit tests.

Helpers
Well, duh, they help. I always try to keep these classes to a minimum. If something belongs in multiple places, like validating a common field, chances are I either want to extend another class or it is good to repeat myself. DRY is great, except when a likely change breaks multiple places. Most helpers I have are implemented through a DIC so that I can change them.


Let's take 2 entities and a manager: Topic, Message, and TopicManager. Say I want to get this topic (I haven't posted it yet, so I don't have an id, but I'll use 100). In the TopicController I would have a method: get() which calls $topicManager->getById(100); TopicManager::__construct(DatabaseInterface $db, CacheInterface $cache) already setup $db and $cache properties - which are just instances of each class - for me. TopicManager::getById($id) calls the $cache methods and then the $db methods when that fails. It gets the Topic info and hydrates a Topic object (I don't call them TopicEntity, but whatever). I don't know how Elkarte does it now, but let's say that it is two separate queries to get the Topic and then the respective messages. TopicManager has another method setMessageManager($manager) (or we could pass it in the constructor or we could pass the entire DIC). This could also be separate to keep the dependencies down. The MessageManager could be called from the controller, but I think it makes sense for the TopicManager to contain a MessageManager. So, $topic->setMessages($messageManager->getByTopicId(100)) adds the messages to the topic.

Now we have all of that, what about the users? I want the users to have a separate manager to ensure that they are separately dependent. I didn't put much thought in to this, but $topic->getUsers() returns all of the users' info. It would probably call each{$message->getPoster(); $message->getUpdater()}. Then there would be a $users array in the controller. Each element of that array is an instance of User (entity). This could also (probably better) be in the DIC so that the users can be "used" everywhere.

Managers inherently share a lot of the same methods. Entities not so much. So, Manager should definitely extend a BaseManager (I just call them "Base*" but you can call them Parent or whatever you want. BaseManager should have a $db and $cache property without a doubt. To differentiate the BaseManager from a manager that doesn't talk to the database (think flat files - Settings or searching a la Sphinx), I call it BaseSQLManager. BaseSQLManager must have a Database instance and a Cache instance. Just thinking about this, this might not be warranted for Elkarte. I use a lot of MongoDB and magic methods to make development fast, so this might not be the same for SQL... then again, maybe it is.

Folder structure is pretty simple (* = folder; - = file):
Controller
-- BaseController.php
-- TopicController.php
Model
Entity
--- Topic.php
--- Message.php
--- User.php
--- CurrentUser.php
Manager
--- BaseManager.php
--- TopicManager.php
--- MessageManager.php
--- UserManager.php
** Helper
-- BBCParser.php

Re: A Sexy Model

Reply #1

I read it quickly, but it sounds interesting indeed.

 emanuele marks it unread to keep track and read it again later.