11

I'm designing a web application with CodeIgniter (but I think the question applies generally to the MVC pattern in web applications).

When I design the model class for some database entity (say, a BlogEntry for illustration), I basically have two choices:

The "classic OOP" approach would be, to let the class represent the entity, i.e. one instance of the class is a BlogEntry. In CodeIgniter, this would lead to code like

class Blogentry extends CI_Model {
    function load($id) {
      // Access database
      $this->id = $dbresult.id;
      $this->title = $dbresult.title;
      $this->author = $dbresult.author;
    }
}

To get access to some blog entry, I would do a $this->load->model('blogentry');, call $this->blogentry->load($id) and then work with the model class instance.

The other approach, which I've seen frequently in the wild, is to let the model return the entity in some data structure. In code:

class Blogentry extends CI_Model {
  function get_entry($id) {
    // Access database, yielding $dbresult
    return $dbresult;
}

With this I would write

$this->load->model('blogentry');
$the_entry = $this->blogentry->get_entry($id);

in the controller and work with $the_entry. In this second case, the class is more like a factory or builder class.

Is any of these two approaches considered the "right way" of doing M in MVC? I think, I've seen the second approach more often, but I didn't look at that many MVC-Webapps. Any thoughts of why the first or the second approach might be more appropriate?

MartinStettner
  • 28,719
  • 15
  • 79
  • 106

3 Answers3

13

First and foremost, CodeIgniter is not an MVC framework. The paradigm that fits best is probably MVP or something like ORM-Template-Adapter. So let me break down my answer based on that naming fallacy:

In Classical/traditional MVC (not what frameworks claim is MVC), a model is not a class or a single file, but rather a layer that contains all of the domain business logic, and usually are only made of of things like Domain Objects, Data Mappers, and something to handle the interface between the calling entity and the domain logic, as to keep such code out of the controller (in MVC, for example). Tereško names them "services", which is good enough for a quasi-official name.

So in traditional MVC, requests to the model layer com in through "services," which in turn interact with the domain objects and data mappers.

Obviously all of that has little relevance to what CodeIgniter and other frameworks call "MVC." In this design pattern (I'll call it "CIMVC" for short), a Model is a class that is called and manipulated by the Controller, and interacts with the database directly. Storing data in member variables within the model doesn't make a whole lot of semantic sense, but if you're interested in something similar, check out any of the ORM plugins/libraries written in CIMVC. (Though ORM solutions wouldn't be any better at conforming to traditional MVC...)

As for common practices, most of the CI applications I've seen return "raw" database data back to the controller for handling; This keeps the domain business logic within the model. My personal preference in CIMVC is to minimize/eliminate anything but domain business logic in the models.

tl;dr - "Traditional/proper" MVC in CodeIgniter is basically impossible, so trying to force your CI code to conform to traditional MVC paradigms is silly (and will likely drive you to insanity)

edit: To clarify some earlier confusion about business logic in the Model layer, yes, you should have all of the business logic in the model layer. However if there is extra data processing going on (sorting, manipulation, etc), now we move into dangerous territory for DRY and SRP violations; You want the model to be reusable, so be wary of adding so much or such specialized logic that it becomes unusable in other parts of the application.

Community
  • 1
  • 1
orourkek
  • 2,091
  • 15
  • 22
  • 2
    Disagree about little to no business logic in the model, this is exactly where the business logic should reside (fat models/skinny controllers). This is so the biz logic can be shared anywhere within the application that needs it. – Mike Purcell Aug 06 '12 at 20:11
  • @MikePurcell You're right, I obviously didn't proofread the answer that well... The model will handle all of the domain business logic, but what the point I was trying to drive home was about SOLID programming, specifically the SRP. I'll update the answer to be more clear. Edit: as I read over that paragraph again, I don't know *what* I was thinking where I wrote "business logic"... it completely contradicts my earlier thoughts :P – orourkek Aug 06 '12 at 20:44
  • Lol, no prob, figured someone must have slipped something in your water. – Mike Purcell Aug 06 '12 at 20:52
  • While I've never used CI, I see nothing in the Smalltalk/traditional MVC definition that excludes CakePHP and other MVC frameworks. In Cake the model layer _is_ primarily comprised of a domain object and data mapper. The fact that in web frameworks, the model layer is comprised of model classes is irrelevant. It's still the same architectural pattern. In fact, the bullet list of domain object responsibilities is pretty much a point-for-point description of a standard Cake model. – Lèse majesté Aug 07 '12 at 03:55
  • 1
    @Lèsemajesté , [in CakePHP](https://github.com/cakephp/cakephp/blob/master/lib/Cake/Model/Model.php) the "model" is instance of active record (an ORM), which gets directly exposed to controller. Thus causing the domain business logic to leak in the controllers. There is no *layer* there. Also, could you explain , how you arrived at the conclusion, that php frameworks implement MVC pattern? Where is the view? – tereško Aug 07 '12 at 05:05
  • @teresko: Not sure what you mean by "causing the domain business logic to leak in the controllers." Active Record is designed to encapsulate domain logic and the data it works on--essentially a type of domain object. True, there's nothing to stop a developer from writing domain logic in their controllers, but that's not the Cake way. A typical Cake application would have fat models, skinny controllers, where the controller only indirectly manipulates the data by mostly calling generic model methods. Domain-specific logic like validation and domain behavior resides strictly within models... – Lèse majesté Aug 07 '12 at 05:54
  • ...and model behaviors. As far as the view, CakePHP has a View class which is responsible for rendering view templates and encapsulates presentation logic (some of which in the View class itself, others in Helpers which are DIed into the view instance, and a small amount in the templates and elements). Lastly, the fact that models are directly exposed to controllers doesn't negate the fact that there's a model layer. E.g. a data access layer may be directly exposed to the client, but it's still a "layer". It's a layer that abstracts data access from the client. – Lèse majesté Aug 07 '12 at 05:58
  • @Lèsemajesté , every time you return anything to controller , just to decide what ORM call to make next, you have domain logic in the controller. – tereško Aug 07 '12 at 06:21
  • @Lèsemajesté and there no view classes. There is a single "view" [class](https://github.com/cakephp/cakephp/blob/master/lib/Cake/View/View.php), which renders a template file. I think you do not understand what "presentation logic" is. But then again, you are CakePHP fanatic who is flaming in a topic with *CodeIgniter* tag. – tereško Aug 07 '12 at 06:25
  • The discussion went far beyond of what I was asking, but thanks anyway, very interesting and good starting point for further thoughts :). What helped me was to think of the model as a layer instead of an class, so I'll interpret the metionder `BlogModel` class as interface/facade to this layer and just let it return the data. Thanks to all! – MartinStettner Aug 07 '12 at 17:15
4

This is a generic answer related to the MVC paradigm, as I have 0 experience with CI, but have heavy experience with Symfony, Zend, Propel, and Doctrine.

I have found, while using doctrine, that returning object data within a specific structure it's best to add a method which specifies said structure. For example Doctrine_Record objects have a toArray() method which allows me to convert the object to an array, so I can either use object accessors, or call toArray() and deal with the object as an array.

// Accessing object data through available methods
foreach ($object->categories() as $category) {
    var_dump((string) $category);
}

// Accessing object data as an array
$objectAsArray = $object->toArray(true);

foreach ($objectAsArray as $element) {
    var_dump($element['Category']);
}

At the end of the day how you access the data is based on your preference and completely up to you how you want to manipulate the data to suit your needs. And in some instances, it is faster to deal with data in array format as object hydration tends to be more of a performance hit.

However you access the data, I suggest that you keep the biz logic at the model layer, so that you don't end up repeating code (DRY: Don't Repeat Yourself).

Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • 1
    As far as I know there are no methods like that in the CodeIgniter Model, but you can specify return types from db calls with (for example): `$query->result()` vs `$query->result_array()` – orourkek Aug 06 '12 at 20:50
  • @orourkek: Thanks for the specific usage, my answer was more generic as I don't use CI. Nice to see they allow the same flexibility though. – Mike Purcell Aug 06 '12 at 20:52
3

First thing you have to understand is that CodeIgniter is quite far from proper MVC or MVC-inspired design patterns. There is no official name the pattern which CodeIgniter implements, but it is closely related to Ruby on Rails.

The most serious problem stems from how model layer has been implemented. Throught out documentation it is advised to use active record pattern, which by itself introduces several design issues:

The other major issue is the lack of views. Views should be instances, which contain presentation logic. This includes choosing which templates to use for representation of the information, that has been acquired from model layer. Views are not templates, they are structures which use templates.

The best way to create a proper model layer on top of CodeIgniter would be to ignore CI_Model, because, IMHO, there are no benefits in extending this class. Instead create something like Services. These structures could act as interfaces (not in OO sense) for interaction with model layer. Each service could deal with multiple domain objects and mappers.

Basically services receive commands/messages from controller, which change the state of model layer and provide method for extracting information from model layer. Usually the extraction would happen in view instances, but that is not possible in CodeIgniter. The extraction of information would also happen in "controllers", which the pass the data to templates.

Code would looks something like this:

class Library 
{

    protected $current_document = null;

    public function acquire_document( $id )
    {
        $document = load_class('Document', 'domain_object');
        $document->set_id( $id );

        $mapper = load_class('Document', 'persistence');
        $mapper->fetch( $document );

        $this->current_document = $document;
    }

    public function get_document_information()
    {
        $document = $this->current_document;        

        if ( $document === null )
        {
            return 'empty';
            // you either did not acquire the document
            // or document was not found
        }
        return $document->get_details();
    }
}

MVC design patter is actually combination of two layers: model layer and presentation layer (which should be made mostly from controllers and views).

Controller should never get hands directly on domain objects (which are part of model layer), because this would mean, that you have a leaking abstraction.

So, in conclusion, what you call "model classes" should return the information.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Your description of MVC doesn't really match Trygve Reenskaug's description of MVC, nor does it match the Smalltalk-80 MVC class hierarchy, in which there _are_ model classes/objects that controllers interact with directly. – Lèse majesté Aug 07 '12 at 04:10
  • @Lèsemajesté, yes, I am following Martin Fowler's interpretation [[1](http://martinfowler.com/eaaDev/uiArchs.html), [2](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)] of MVC design pattern. Also you have to keep in mind that this is in the context of web, where anything even close to classical MVC is either impossible or almost impossible. – tereško Aug 07 '12 at 04:27