Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building the right model in MVC design

I'm posting a ghost here, that daunts me for some years. It's a question about how to build the right model, the right objects.

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

The class Comment has author, timestamp, text.

An Article can have 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 article name and a few of body copy.

Here is where I got confused because I don't need to load the comments information, and this can make a significant performance difference when I have lots of articles and tons of comments.

Should I build two models? One for Article and one for ArticlesInList? Should I delegate the load of comments to a lazy mode (is this possible), retrieving them only when necessary?

What is the right way to face and solve this?

Thx.

like image 251
Davis Peixoto Avatar asked Oct 11 '11 01:10

Davis Peixoto


People also ask

What is the model in MVC pattern?

MVC (Model-View-Controller) is a pattern in software design commonly used to implement user interfaces, data, and controlling logic. It emphasizes a separation between the software's business logic and display. This "separation of concerns" provides for a better division of labor and improved maintenance.

What is the correct way to describe MVC architecture?

-MVC is an architectural pattern consisting of three parts: Model, View, Controller. Model: Handles data logic. View: It displays the information from the model to the user. Controller: It controls the data flow into a model object and updates the view whenever data changes.

What is model in MVC with example?

The model contains all the data-related logic that the user works with, like the schemas and interfaces of a project, the databases, and their fields. For example, a customer object will retrieve the customer information from the database, manipulate or update their record in the database, or use it to render data.

What is the role of model in MVC architecture?

Model. The Model component corresponds to all the data-related logic that the user works with. This can represent either the data that is being transferred between the View and Controller components or any other business logic-related data.


1 Answers

There are a lot of trade-offs to be made when trying to model your business objects.

Given your example, I can think of a few approaches, that mostly revolve around lazy-loading comments. Here's how I'd do it, if I were reasonably sure that things weren't going to get more complex:

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

Implement one or more Collection classes, such as ArticleCollection. You might have a service class that fetches articles that match some criteria. ArticleService::fetchArticles() would return articles without comments loaded. Then implement a loadComments() method of ArticleCollection that loads all the comments for all the articles in the collection. At first, this can just iterate over the articles calling loadComments -- but you can replace it later with a single-query implementation.

Here's the beginning of an Article and ArticleCollection. If you implement a CommentCollection class, you could use that inside Article to hold the 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's 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 pretty basic -- you can apply other design patterns to factor out your database-access so it's not so tightly coupled, for instance. But I'm just trying to describe a strategy for lazy-loading your comments here in a sane way.

Some final advice: Don't fall into the trap that many framework do and think there is some divine correlation between tables in your database and models. Models are just objects. They can do different kinds of things (represent a simple thing like a comment, or a user), or represent things like a service that operates on those kinds of simple things, or they can be things like groups (collections) of those individual things.

One fun exercise is to just write up classes, and fill them up with dummy-data. Do your best to completely forget that a database will be involved. Craft objects that support the use-cases you need. Then, once you've got that done, figure out how to save and load the data to/from the DB.

like image 106
timdev Avatar answered Oct 14 '22 19:10

timdev