Building the Right Model in MVC Design

I place the ghost here, it turns me on for several years. This is a question about how to build the right model, the right objects.

Let me explain. Suppose I have a class article. The article has a title, rating, body copy and comments.

A class comment has an author, timestamp, text.

An article can contain 0 or more comments. So far, so good. No problem with this concept. But...

  • When showing the article, I show everything. Article properties, including its comments.
  • When showing a list of articles, I show only the title of the article and a few copies of the body.

This is where I got confused because I don’t have to upload comment information, and this can lead to a significant difference in performance when I have a lot of articles and tons of comments.

Should I build two models? One for the article and one for the ArticlesInList? Should I delegate loading comments in lazy mode (is this possible), retrieve them only if necessary?

What is the correct way to solve this problem?

thanks.

+6
source share
3 answers

When you try to model business objects, you need a lot of trade-offs.

Given your example, I can come up with several approaches that mostly revolve around lazy comments. Here is how I would do it if I was sure that things were not going to get complicated:

First, you create entities for the article and commentary that simply represent the data in each table in your database. Write setters and getters. Implement the loadComments () method for the article.

Deploy one or more Collection classes, such as ArticleCollection. You may have a class of service that selects articles that meet certain criteria. ArticleService :: fetchArticles () will return articles without uploaded comments. Then we implement the method loadComments () ArticleCollection, which loads all comments for all articles in the collection. At first, this can simply iterate over the articles calling loadComments, but you can later replace them with a single request implementation.

This is where the article and ArticleCollection begins. If you implement the CommentCollection class, you can use this inside the article to store comments, etc.

<?php /** * Extends a base model class that provides database-related methods -- not ideal, * but trying to stay focused here. */ class Article extends Model { private $_data; private $_fields = array('id','title','body','author'); /** * Constructor can take an array of values to initialize. */ public function __construct($data=null){ if (is_array($data)){ foreach($this->_fields as $field){ $this->_data[$field] = $data[$field]; } } } public function getId(){ return $this->_data['id']; } // more getters, and setters, here. public function loadComments(){ $result = $this->query('SELECT * FROM Comment WHERE article_id = ' . $this->getId()); $this->_comments = array(); foreach($result as $c){ //instantiate a new comment (imagine Comment constructor is very similar to Article's $this->_comments[] = new Comment($c); } } } class ArticleCollection extends Model { /** * An array of Articles, indexed by article_id */ private $_articles = array(); /** * Naive implementation. A better one would grab all article IDs from $this->_articles, and * do a single query for comments WHERE article_id IN ($ids), then attach them to the * right articles. */ public function loadComments(){ foreach($this->_articles as $a){ $a->loadComments(); } } /** * Add article to collection */ public function addArticle(Article $article){ if (empty($article->id)) throw new \Exception('Can\'t add non-persisted articles to articlecollection!'); $this->_articles[$article->id] = $article; } } 

The above is quite simple - you can apply other design patterns to share access to the database so that it is not so closely related, for example. But I'm just trying to describe a strategy for lazily loading your comments here in a reasonable way.

Some final tips: don't fall into the trap that many frameworks adhere to, and believe that there is some divine correlation between the tables in your database and the models. Models are just objects. They can do different things (represent a simple thing, such as a comment or user), or represent things like a service that works with these simple things, or they can be things like groups (collections) of these individual things .

One interesting exercise is to simply write classes and populate them with dummy data. Do your best to completely forget that the database will be involved. Create the objects you need. Then, as soon as you do this, figure out how to save and load data to / from the database.

+4
source

I think there is no better way, but there are ways that are better than others. Anyway, these are my two cents:

First of all, I would like to create 2 classes, but one for articles, and the second for comments. Thus, you (in my opinion) agree with the law of Demeter ( Law of Demeter ). Now in your controller you can get a list of articles (no comments ... ok with efficiency), and when you need, for each article you can use model comment for related comments. Also, you should keep this software development principle in mind: "Low coupling , high cohesion "

It's my opinion. Hope this helps you.

0
source

It depends on the structure, but your business requirements make sense. This is how I would structure things (according to Agile Toolkit logic):

Business logic

Business logic always reflects your real objects, such as articles and comments. Presentation requirements do not affect:

 class Model_Article extends Model_Table { function init(){ parent::init(); $this->addField('title'); $this->addField('rating')->type('int'); $this->addField('body')->type('text'); } function getComments(){ return $this->add('Model_Comment') ->setMasterField('article_id',$this->get('id')); } } class Model_Comment extends Model_Table { function init(){ parent::init(); $this->addField('name'); $this->addField('body')->type('text'); $this->addField('article_id')->refModel('Model_Article'); } } 

User interface logic

In a presentation, the Agile Toolkit is controlled by the Page classes. In your case, you will need 2 pages, although both pages depend on both models:

 class page_article extends Page { function init(){ parent::init(); $m=$this->add('Model_Article')->loadData($_GET['id']); $this->add('View',null,null,array('view/article/body')) ->setModel($m); $this->add('MVCLister',null,null,array('view/article/comments')) ->setModel($m->getComments()); } } class page_article_comment extends Page { $m=$this->add('Model_Comment')->loadData($_GET['id']); $this->add('View',null,null,array('view/comment/header')) ->setModel($m->getRef('article_id')); $this->add('View',null,null,array('view/comment/full')) ->setModel($m); } 

This code is based on 4 HTML templates that contain tags, for example, etc.

0
source

Source: https://habr.com/ru/post/899011/


All Articles