576

I am just getting a grasp on the MVC framework and I often wonder how much code should go in the model. I tend to have a data access class that has methods like this:

public function CheckUsername($connection, $username)
{
    try
    {
        $data = array();
        $data['Username'] = $username;

        //// SQL
        $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE Username = :Username";

        //// Execute statement
        return $this->ExecuteObject($connection, $sql, $data);
    }
    catch(Exception $e)
    {
        throw $e;
    }
}

My models tend to be an entity class that is mapped to the database table.

Should the model object have all the database mapped properties as well as the code above or is it OK to separate that code out that actually does the database work?

Will I end up having four layers?

i alarmed alien
  • 9,412
  • 3
  • 27
  • 40
Dietpixel
  • 9,983
  • 11
  • 28
  • 33
  • 136
    Why are you catching exceptions just to throw them again? – Bailey Parker Jun 08 '12 at 05:46
  • 4
    @Elias Van Ootegem: huh? if it works with rethrow, it means that an upper layer catches the exception. But if there is one, then it would have catched it without that pointless rethrow... (if you still don't get it, please mock up a small test code) – Karoly Horvath Jul 18 '12 at 21:11
  • 3
    @Elias Van Ootegem: I have no idea what you're talking about, not handling an exception on a specific layer doesn't mean it will halt the app. please construct (or more precisely: fail to construct) a code example where that rethrow is necessary. let's stop this offtopic conversation, *please* – Karoly Horvath Jul 18 '12 at 22:01
  • 6
    @drrcknlsn: that's a valid argument, but in that case at least catch the exception you expect to be thrown, the generic `Exception` doesn't have much documentation value. Personally if I went down on that road I would choose PHPDoc's `@exception`, or some similar mechanism, so it shows up in the generated documentation. – Karoly Horvath Aug 02 '12 at 22:27
  • @KarolyHorvath: I don't disagree, necessarily, at least in the cases where only a specific exception could be thrown, but sometimes there could literally be dozens of different possible exceptions, and catching or documenting each of them explicitly would be somewhat impractical if your only intent is to rethrow them. If you're anticipating only one specific exception, or the library you're wrapping is using good exception handling and inheritance itself, then doing so would definitely be preferred. – FtDRbwLXw6 Aug 02 '12 at 22:41
  • 1
    It is perfectly acceptable (and sometimes common) to catch exceptions to re-throw them again as a different exception, depending on what your object api's state are the thrown exceptions. – Jimbo Nov 20 '14 at 10:03

5 Answers5

960

Disclaimer: the following is a description of how I understand MVC-like patterns in the context of PHP-based web applications. All the external links that are used in the content are there to explain terms and concepts, and not to imply my own credibility on the subject.

The first thing that I must clear up is: the model is a layer.

Second: there is a difference between classical MVC and what we use in web development. Here's a bit of an older answer I wrote, which briefly describes how they are different.

What a model is NOT:

The model is not a class or any single object. It is a very common mistake to make (I did too, though the original answer was written when I began to learn otherwise), because most frameworks perpetuate this misconception.

Neither is it an Object-Relational Mapping technique (ORM) nor an abstraction of database tables. Anyone who tells you otherwise is most likely trying to 'sell' another brand-new ORM or a whole framework.

What a model is:

In proper MVC adaptation, the M contains all the domain business logic and the Model Layer is mostly made from three types of structures:

  • Domain Objects

    A domain object is a logical container of purely domain information; it usually represents a logical entity in the problem domain space. Commonly referred to as business logic.

    This would be where you define how to validate data before sending an invoice, or to compute the total cost of an order. At the same time, Domain Objects are completely unaware of storage - neither from where (SQL database, REST API, text file, etc.) nor even if they get saved or retrieved.

  • Data Mappers

    These objects are only responsible for the storage. If you store information in a database, this would be where the SQL lives. Or maybe you use an XML file to store data, and your Data Mappers are parsing from and to XML files.

  • Services

    You can think of them as "higher level Domain Objects", but instead of business logic, Services are responsible for interaction between Domain Objects and Mappers. These structures end up creating a "public" interface for interacting with the domain business logic. You can avoid them, but at the penalty of leaking some domain logic into Controllers.

    There is a related answer to this subject in the ACL implementation question - it might be useful.

The communication between the model layer and other parts of the MVC triad should happen only through Services. The clear separation has a few additional benefits:

  • it helps to enforce the single responsibility principle (SRP)
  • provides additional 'wiggle room' in case the logic changes
  • keeps the controller as simple as possible
  • gives a clear blueprint, if you ever need an external API

 

How to interact with a model?

Prerequisites: watch lectures "Global State and Singletons" and "Don't Look For Things!" from the Clean Code Talks.

Gaining access to service instances

For both the View and Controller instances (what you could call: "UI layer") to have access these services, there are two general approaches:

  1. You can inject the required services in the constructors of your views and controllers directly, preferably using a DI container.
  2. Using a factory for services as a mandatory dependency for all of your views and controllers.

As you might suspect, the DI container is a lot more elegant solution (while not being the easiest for a beginner). The two libraries, that I recommend considering for this functionality would be Syfmony's standalone DependencyInjection component or Auryn.

Both the solutions using a factory and a DI container would let you also share the instances of various servers to be shared between the selected controller and view for a given request-response cycle.

Alteration of model's state

Now that you can access to the model layer in the controllers, you need to start actually using them:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $identity = $this->identification->findIdentityByEmailAddress($email);
    $this->identification->loginWithPassword(
        $identity,
        $request->get('password')
    );
}

Your controllers have a very clear task: take the user input and, based on this input, change the current state of business logic. In this example the states that are changed between are "anonymous user" and "logged in user".

Controller is not responsible for validating user's input, because that is part of business rules and controller is definitely not calling SQL queries, like what you would see here or here (please don't hate on them, they are misguided, not evil).

Showing user the state-change.

Ok, user has logged in (or failed). Now what? Said user is still unaware of it. So you need to actually produce a response and that is the responsibility of a view.

public function postLogin()
{
    $path = '/login';
    if ($this->identification->isUserLoggedIn()) {
        $path = '/dashboard';
    }
    return new RedirectResponse($path); 
}

In this case, the view produced one of two possible responses, based on the current state of model layer. For a different use-case you would have the view picking different templates to render, based on something like "current selected of article" .

The presentation layer can actually get quite elaborate, as described here: Understanding MVC Views in PHP.

But I am just making a REST API!

Of course, there are situations, when this is a overkill.

MVC is just a concrete solution for Separation of Concerns principle. MVC separates user interface from the business logic, and it in the UI it separated handling of user input and the presentation. This is crucial. While often people describe it as a "triad", it's not actually made up from three independent parts. The structure is more like this:

MVC separation

It means, that, when your presentation layer's logic is close to none-existent, the pragmatic approach is to keep them as single layer. It also can substantially simplify some aspects of model layer.

Using this approach the login example (for an API) can be written as:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $data = [
        'status' => 'ok',
    ];
    try {
        $identity = $this->identification->findIdentityByEmailAddress($email);
        $token = $this->identification->loginWithPassword(
            $identity,
            $request->get('password')
        );
    } catch (FailedIdentification $exception) {
        $data = [
            'status' => 'error',
            'message' => 'Login failed!',
        ]
    }

    return new JsonResponse($data);
}

While this is not sustainable, when you have complicate logic for rendering a response body, this simplification is very useful for more trivial scenarios. But be warned, this approach will become a nightmare, when attempting to use in large codebases with complex presentation logic.

 

How to build the model?

Since there is not a single "Model" class (as explained above), you really do not "build the model". Instead you start from making Services, which are able to perform certain methods. And then implement Domain Objects and Mappers.

An example of a service method:

In the both approaches above there was this login method for the identification service. What would it actually look like. I am using a slightly modified version of the same functionality from a library, that I wrote .. because I am lazy:

public function loginWithPassword(Identity $identity, string $password): string
{
    if ($identity->matchPassword($password) === false) {
        $this->logWrongPasswordNotice($identity, [
            'email' => $identity->getEmailAddress(),
            'key' => $password, // this is the wrong password
        ]);

        throw new PasswordMismatch;
    }

    $identity->setPassword($password);
    $this->updateIdentityOnUse($identity);
    $cookie = $this->createCookieIdentity($identity);

    $this->logger->info('login successful', [
        'input' => [
            'email' => $identity->getEmailAddress(),
        ],
        'user' => [
            'account' => $identity->getAccountId(),
            'identity' => $identity->getId(),
        ],
    ]);

    return $cookie->getToken();
}

As you can see, at this level of abstraction, there is no indication of where the data was fetched from. It might be a database, but it also might be just a mock object for testing purposes. Even the data mappers, that are actually used for it, are hidden away in the private methods of this service.

private function changeIdentityStatus(Entity\Identity $identity, int $status)
{
    $identity->setStatus($status);
    $identity->setLastUsed(time());
    $mapper = $this->mapperFactory->create(Mapper\Identity::class);
    $mapper->store($identity);
}

Ways of creating mappers

To implement an abstraction of persistence, on the most flexible approaches is to create custom data mappers.

Mapper diagram

From: PoEAA book

In practice they are implemented for interaction with specific classes or superclasses. Lets say you have Customer and Admin in your code (both inheriting from a User superclass). Both would probably end up having a separate matching mapper, since they contain different fields. But you will also end up with shared and commonly used operations. For example: updating the "last seen online" time. And instead of making the existing mappers more convoluted, the more pragmatic approach is to have a general "User Mapper", which only update that timestamp.

Some additional comments:

  1. Database tables and model

    While sometimes there is a direct 1:1:1 relationship between a database table, Domain Object, and Mapper, in larger projects it might be less common than you expect:

    • Information used by a single Domain Object might be mapped from different tables, while the object itself has no persistence in the database.

      Example: if you are generating a monthly report. This would collect information from different of tables, but there is no magical MonthlyReport table in the database.

    • A single Mapper can affect multiple tables.

      Example: when you are storing data from the User object, this Domain Object could contain collection of other domain objects - Group instances. If you alter them and store the User, the Data Mapper will have to update and/or insert entries in multiple tables.

    • Data from a single Domain Object is stored in more than one table.

      Example: in large systems (think: a medium-sized social network), it might be pragmatic to store user authentication data and often-accessed data separately from larger chunks of content, which is rarely required. In that case you might still have a single User class, but the information it contains would depend of whether full details were fetched.

    • For every Domain Object there can be more than one mapper

      Example: you have a news site with a shared codebased for both public-facing and the management software. But, while both interfaces use the same Article class, the management needs a lot more info populated in it. In this case you would have two separate mappers: "internal" and "external". Each performing different queries, or even use different databases (as in master or slave).

  2. A view is not a template

    View instances in MVC (if you are not using the MVP variation of the pattern) are responsible for the presentational logic. This means that each View will usually juggle at least a few templates. It acquires data from the Model Layer and then, based on the received information, chooses a template and sets values.

    One of the benefits you gain from this is re-usability. If you create a ListView class, then, with well-written code, you can have the same class handing the presentation of user-list and comments below an article. Because they both have the same presentation logic. You just switch templates.

    You can use either native PHP templates or use some third-party templating engine. There also might be some third-party libraries, which are able to fully replace View instances.

  3. What about the old version of the answer?

    The only major change is that, what is called Model in the old version, is actually a Service. The rest of the "library analogy" keeps up pretty well.

    The only flaw that I see is that this would be a really strange library, because it would return you information from the book, but not let you touch the book itself, because otherwise the abstraction would start to "leak". I might have to think of a more fitting analogy.

  4. What is the relationship between View and Controller instances?

    The MVC structure is composed of two layers: ui and model. The main structures in the UI layer are views and controller.

    When you are dealing with websites that use MVC design pattern, the best way is to have 1:1 relation between views and controllers. Each view represents a whole page in your website and it has a dedicated controller to handle all the incoming requests for that particular view.

    For example, to represent an opened article, you would have \Application\Controller\Document and \Application\View\Document. This would contain all the main functionality for UI layer, when it comes to dealing with articles (of course you might have some XHR components that are not directly related to articles).

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Of course, because the business logic of making "Invoice" does not depend on if the data comes from Excel file or from some Oracle mega-cluster. – tereško May 03 '11 at 01:09
  • Do you have a small example of the process your describing above? – Dietpixel May 03 '11 at 01:22
  • well i still think my code is correct it may not be pure mvc has u say but it does it job . u can read about it here http://framework.zend.com/manual/en/zend.db.table.html – Rinzler Jun 12 '12 at 09:04
  • 4
    @Rinzler , you will notice, that nowhere in that link, anything said about Model (except in one comment). It's only *"an object-oriented interface to database tables"*. If you try to mold this in a Model-like thing, you end up violating [**SRP**](http://codingcraft.files.wordpress.com/2011/03/srp.jpg) and [**LSP**](http://codingcraft.files.wordpress.com/2011/03/lsp.jpg). – tereško Jun 12 '12 at 10:11
  • 1
    +1 for your overall explanation, very good. Personal preference but I don't really like using factories to quite that excess as in your example (but appreciate what you were demonstrating). Lastly the section on services (within the Model heading) not too sure I agree totally with all your explanation, as when I think of services I think in terms of a 'Service layer' so hence something which sits 'above' the model layer, not along side it. Also isn't it the job of the Mapper to speak to the domain object and dba layer, or did I miss your point? I like to use services when a complicated... – Steve H Jun 13 '12 at 13:45
  • 1
    domain structure is required (as in a number of Mappers are required, etc). @Rinzler: I think you should do a bit more research for better practices using the Zend framework, for a start have a look at: http://survivethedeepend.com/zendframeworkbook/en/1.0 – Steve H Jun 13 '12 at 13:49
  • @SteveH , as you saw there, I'm going by Fowler's definition of **mappers**, where mapper is responsible to exchange of data between storage (which might be a DB) and a domain object. Kinda like [described here](http://stackoverflow.com/a/10988966/727208). As for **services**, I did not have a better name, but they are structures, which isolate *controller* from business logic. *Controller* is no supposed to receive information or other feedback from Model layer. Should the code from `AuthenticationService::login()` be in the controller? – tereško Jun 13 '12 at 14:15
  • @tereško: I guess the only thing that stuck out was your line "Services are responsible for interaction between Domain Objects and Mappers" which I felt needed some clarification as in my opinion the Mapper should be dealing with the domain object by itself. However I agree with your Auth Service (you're right - keep as a service not in controller) but I personally would rather see the actual Mapper taking care of validation and actually creating the domain object itself. Sorry I'm just nit picking really, here's link for others to view: http://martinfowler.com/eaaCatalog/dataMapper.html – Steve H Jun 13 '12 at 14:44
  • ha just realised you already included that dataMapper link (damn 5 minute comment cut off period!). However here is a different link I found about the Service layer: http://martinfowler.com/eaaCatalog/serviceLayer.html – Steve H Jun 13 '12 at 14:56
  • @SteveH , no-no-no. Domain object must validate themselves. That is not part of storage logic. And each domain object can contain even collections of other domain objects. This would force data mappers to take up factory behavior, thus - violating [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle). Seems to me like bad idea. – tereško Jun 13 '12 at 15:02
  • @SteveH , as for **Service layer**, this is another case of *"can you think of a better name?"*. First of all, the description of it in PoEAA book was kinda .. emm .. murky. And, what Fowler refers to as "services", are things like REST interface or SOAP. I would have used name *"Components"*, but it has already been used in some many different contexts, that it would only succeed in creating more confusion. – tereško Jun 13 '12 at 15:06
  • @tereško: interesting points, but my main point was that validation for a domain object AND a data storage object may require different validation techniques. Granted for many examples there is normally a 1:1 relationship between validation of data in your domain model and storage system but it is quite valid (and logical) that data stored within your domain model (although being valid for the dm) is not valid for use with your storage medium. I would also personally argue that I'd rather keep my domain model's relatively dumb (as in they are not self-aware that the data that they contain... – Steve H Jun 13 '12 at 15:19
  • is 'valid' just that they contain data). I would employ the DataMapper to actually 'create' the domain model and if required validate any data at that stage. One advantage of doing this is the DataMapper is already in charge managing data between the domain model and db / storage layer, so why not have all validation in a central location? I Agree I have seen many people employ the way you have described, and I am definitely not saying that is wrong - far from it. In regards to 'collections' of domain objects, perhaps look to use repositories: http://martinfowler.com/eaaCatalog/repository.html – Steve H Jun 13 '12 at 15:26
  • @SteveH , **storage logic validation** (like ensuring uniqueness) does not happen on the mapper, but the the level of storage itself. That rises an exception in the interface, which was used for communication(like PDO), and is handled inside the Mapper (changing the state of domain object). But mapper itself does not perform any validation. – tereško Jun 13 '12 at 15:30
  • @SteveH , with **collection of domain objects** i meant situation, where one or more `User` objects gets associated to `Group`. Or when you are fetching a collection of documents for a specific `Category`. It is not the responsibility of mapper to create domain objects. Only to ensure the information exchange. Other you end up with situation, when you have to provide mapper with instance of `Document`, so that it can create a `Category` and inject said document. And then execute validation to make sure that `Document` fits the logical constraints. – tereško Jun 13 '12 at 15:35
  • @tereško: No I agree with you, what I was meaning was its the DataMapper's job to 'convert/modify' your domain object to suit the expectations of your storage mechanism, which I would loosely term 'validating' as the DataMapper may have to manipulate the data (so may call such validation methods from the storage layer - although obviously won't do the heavy lifting itself). You could argue that if the domain model knows how data should be validated are you not breaking SRP by doing so as in my mind a domain model should only 'know' one thing - what it is, not how it is to be used, etc. – Steve H Jun 13 '12 at 15:53
  • @tereško It's important to note that each _tier_ within a system can (and should) have a differing representation of the domain you are modelling. Front-end code (e.g. stuff running in a browser), you probably have a differing architectural pattern [`Model-View-ViewModel`](http://en.wikipedia.org/wiki/Model_View_ViewModel). For your persistance layer, you may use [`ActiveRecord`](http://en.wikipedia.org/wiki/Active_record_pattern). Both of these tiers require a mapping of your models. p.s. This is a fantastic answer and I hope it becomes a community favorite. – hafichuk Jun 15 '12 at 14:21
  • 8
    @hafichuk only situations, when it is reasonable to employ [*ActiveRecord*](http://martinfowler.com/eaaCatalog/activeRecord.html) pattern is for prototyping. When you start to write the code that is mean for production, it becomes an anti-pattern, because it mixes storage and business logic. And since *Model Layer* is completely unaware of the other MVC parts. **This does not change depending on variation on original pattern**. Even when using MVVM. **There are no "multiple models" and they are not mapped to anything. Model is a layer.** – tereško Jun 15 '12 at 14:47
  • @tereško My point was that you need to re-evaluate the architectural pattern depending on the tier (not layer). ActiveRecord was just an example for the persistance layer of the application. Use SQL, don't use SQL, it'll affect the pattern you use for persistence (which _is_ the responsibility of the Model layer). The Model layer needs to provide an abstraction for this, typically through a DAO, since we're crossing a boundary and the representation of our data will change from an object graph to a relational or document form. – hafichuk Jun 15 '12 at 17:05
  • 4
    Short Version - Models are [Data Structures](http://heim.ifi.uio.no/~trygver/1979/mvc-2/1979-12-MVC.pdf). – Edward J Beckett Nov 24 '12 at 10:21
  • 2
    @EddieB congratulations. You somehow managed to miss the whole *'There are no "models"`* point in this post. Also, while not bad, the book is about application modeling (as in UML and whatnot). It touches on MVC only in single chapter. – tereško Nov 24 '12 at 10:29
  • 11
    Well seeing that he invented MVC the article may have some merit. – Edward J Beckett Nov 24 '12 at 10:39
  • 1
    @tereško Why should certain `Service` know about the `DataMapperFactory` and `DomainObjectFactory`? Why not to build dependencies in `ServiceFactory` via `DataMapper/DomainObject Factory`and pass them to the constructor of `Service`? Why should `Controller` and `View` know about your `ServiceFactory` instead of their direct dependencies? – mintobit Mar 15 '13 at 07:59
  • @an1zhegorodov .. could you write in a less confusing manner!? Lets see if I understood what you were asking. You pass in factories in he controller, services and views because there are more the one type of each. Also, in controllers and views, the service that they require tend to vary for each method. For example, while to view the comments for an article, you do not need to be authorized, to post a new comment, authorization can be mandatory. But both operations can be handled by same controller-view pair. Basically, injecting factories lets you use lazy loading. – tereško Mar 15 '13 at 10:48
  • @an1zhegorodov the alternative would be to employ DIC within the factories for services, views and controllers. But that would also mean that you are either initializing each dependency every time (even when they are not really used) or you add extra complexity to provide lazy-loading wrappers for each of those dependencies. – tereško Mar 15 '13 at 10:50
  • In my controller, I had the idea to do something like: /* retrieve fields from form */ $firstname = $form->getInput('firstname'); $lastname = $form->getInput('lastname'); /* create Member object */ $member = new Member($firstname, $lastname); /* save Member in DB */ $this->_daoFactory->get('Member')->save($member); So compared to your solution, my controller uses domain objects directly and whenever it needs to store/fetch/update the DB it gets the appropriate DAO object through a Factory. What do you think about that? – Matthew Jun 06 '13 at 08:25
  • 2
    `The model is not a class or any single object.` While I'm confirm with this in my projects, I would not generalize this. In the simplest from the model can be just a single class. – hek2mgl Aug 04 '13 at 11:23
  • 3
    ... or even just a set of functions. MVC does not require to be implemented in an OOP style, although it is mostly implemented that way. The most important thing is to separate layers and establishing the right data and control flow – hek2mgl Aug 04 '13 at 11:36
  • 2
    @hek2mgl , If the entirety of your model can be represented as a single class (assuming you are adhering to SRP), then you should not be using MVC to begin with. It is not a pattern for "hello world" scale application. In such cases you should simply follow the general "good practices" of programming style in that particular application (be it procedural, object-oriented, functional or some other). Applying fully realized MVC in such situations is a waste of resource (unless you are studying). – tereško Aug 04 '13 at 15:06
  • @tereško Do you have any pointers to articles about a good directory structure or PHP framework that supports this way of thinking? – Jonathan Aquino Jun 03 '14 at 20:42
  • @tereško If request is 'GET', view fetches data from Model Layer through Services too (same as with 'POST') or it can skip Services? Thanks – Andrew Jun 30 '14 at 19:26
  • 1
    MVC triad should not care about the source of user input. Only the fact that there is such an input. "HTTP" is just a transport mechanism for this input. That's why the example features an abstracted `Request` instance. Depending on how you implement it, the `Request` instance contains all necessary parameters from `POST` and `GET` queries. Same goes for `PUT` and `DELETE`. When you pass the `$request` to appropriate controller, it should not really care about the origin of a specific parameter. – tereško Jun 30 '14 at 21:43
  • @tereško so basically you are using servicefactory (services sub-layer?) always and for everything? I asked this because of your answer [here](http://chat.stackoverflow.com/transcript/message/5554893#5554893) – Andrew Jul 01 '14 at 16:17
  • 2
    That chat line seems really badly articulated and mostly wrong **(it's also two years old)**. – tereško Jul 01 '14 at 16:36
  • @Andrew also, I would like to note that I haven't been using service factories for some time already. – tereško Jul 01 '14 at 17:39
  • @tereško what are you using instead of service factories now? – Andrew Jul 27 '14 at 08:58
  • @Andrew in the current long-term project I am still using a service factory (because I could not adopt [Auryn](https://github.com/rdlowrey/Auryn) and my own DI Container's implementation is till a "work in progress"). But I will be switching to Container-driven assembly for controllers in my next green-field project, – tereško Jul 28 '14 at 13:25
  • @teresko what was wrong (not enough) with service factories that you decided to use other approaches? – Andrew Jul 28 '14 at 13:45
  • @Andrew the problems begin when you are attempting to use services, that are actually wrappers for 3rd party libraries (for example: `Mailer` service). They have different set of dependencies. Same applies to cases, when you would like to share a number of domain objects between specific services. It boils down to the fact, that factories are limited when it comes to ways how you can instantiate objects. All this points to either choosing a service locator (to pull in dependencies) or use of DI container (to supply dependencies directly). And later seems a better approach. – tereško Jul 28 '14 at 14:02
  • @tereško would you be so kind and add an example of the `if the user is-logged-in` part? I'm having trouble with this part. Thanks for the "article", it's awesome. :) – Jo Smo Aug 12 '14 at 13:43
  • What about view models? Do you recommend to map domain objects to them? – Marian Ban Sep 14 '14 at 21:17
  • 2
    @MajoB "viewmodel" is something you replace the controller with in the MVVM architectural pattern. You are probably referring to [presentation objects](http://martinfowler.com/eaaDev/PresentationModel.html). And those I don't not see as something you would be linking directly with domain objects, because I do not want the domain objects to be exposed to the anything outside of model layer. – tereško Sep 15 '14 at 06:54
  • @tereško << [A view] acquires data from the Model Layer and then, based on the received information, chooses a template and sets values. >> Isn't it the job of controller to choose particular content template? (I am aware of the fact that a resulting web page is usually composed of several of template files) – Mikhail Batcer Dec 09 '14 at 09:40
  • @MikhailBatcer this should explain it: http://stackoverflow.com/a/16596704/727208 – tereško Dec 09 '14 at 09:48
  • so does this apply to modern day objective c and iOS MVC design? – SleepsOnNewspapers Feb 21 '15 at 08:09
  • Its been a while since I've read such a good answer. Trust me I read a lot of them. – Sorter Apr 13 '15 at 10:39
  • That's right, most people(include me) always thought that the model may be just a POJO class ,this is also some mvc tutorial used. While the `Model` means a dynamical progress to interacte with your persisted data, so I think the `Model` should be the `Service+Dao+POJO` in a typical SpringMVC application. – hguser Nov 14 '15 at 13:49
  • In the first code block you provided, the request class reminds me of a domain object. Is this accurate/what functionalities should be built into the request class so it isn't reminiscent of a domain object? – jeremy Mar 26 '16 at 17:29
  • @Jeremy the `Request` class (as I implement it) deals with abstracting the user's input (get and post parameters, cookies, uploaded files and extracted data, when client is pushing data as application/json ContentType .. that sort of things). Also, in by own projects, the `Request` instance (after it's initialization/preparing in the bootstrap stage) doesn't get altered in the MVC triade. – tereško Mar 27 '16 at 04:52
  • why does the controller get an instance of the view passed to it? – jeremy Jul 24 '16 at 21:10
  • @Jeremy it shouldn't. It's a mistake, – tereško Jul 25 '16 at 08:07
  • it seems to me that services should be **changing the state of the view**. should they not? – jeremy Aug 13 '16 at 01:25
  • @tereško in your example of how to interact with the model, I see that you create a response object, but I don't see it reference again until it is passed to the view. I assume that the view will get it's models through some interaction with a DI container. If this is the case then what is the responsibly of the `Response` class/instance? – bassxzero Sep 02 '16 at 20:16
  • @bassxzero a very simple example is [here](http://stackoverflow.com/a/25709651/727208) but that's just on the controller side of things. As for view ... well ... the best I can do is sample on of the projects I am currently working on: https://gist.github.com/teresko/c13a801dadf595d2b515e1b30d1f3b49/04fc2ef90526c60671b8a6c19587ff56e7e7721b (**reposted link, copied from wrong branch**), because the alternative would be simply direct you to [this answer](http://stackoverflow.com/a/16596704/727208), but that's just a wall of text – tereško Sep 02 '16 at 20:43
  • What if my modellayer needs a database and an api connection? Is it a good choice to extend the servicefactory and add the api-class to it? – ninchen Jun 02 '17 at 07:22
  • 1
    These days I would advise against using service factory as whole and instead use DI Containers, with each of controllers having specified services as dependencies in the constructor. But, if you need to add a different data source, it's not the service initialization code, that has to change. Instead you should alter the data mapper factory, so that it can produce persistence abstraction both for database and API. (I should probably update the answer this year) – tereško Jun 02 '17 at 07:34
  • Thx. Yes, i meant this. f.e. `$serviceFactory = new ServiceFactory( new DataMapperFactory(new Database(),new HTTPRequest), new DomainObjectFactory() );` – ninchen Jun 02 '17 at 07:51
  • But i agree, using DI Containers is the better approach. – ninchen Jun 02 '17 at 07:53
  • That's the general approach - yes. But if can fully cover your domain objects with unit test and then don't have any specific dependencies for them, you could get away with just using `new` in services for instantiating those domain objects. – tereško Jun 02 '17 at 08:25
  • The approach in the original example is something of a "what to do in worst case scenario". Also, reading PoEAA book would help a lot. – tereško Jun 02 '17 at 08:26
  • @tereško Your `IdentificationService` is very anemic. Almost every method call within your `loginWithPassword` method takes the `$identity` as an argument. It could easily be refactored so that the `Identity` has the `loginWithPassword( $password ) : string` method itself. This makes more sense anyway right? After all, who/what is logging in? This is the problem with employing `Domain Services`. Often, they work against the ideas of DDD by separating data and behavior. A better example of a `Domain Service` would be one that is responsible for creating the `$cookie`, not "doing" something. – user3347715 Mar 30 '18 at 17:01
  • @user3347715 **no, it does not**. Because login operation interacts with persistence. `Identity` has no awareness of it (and why would it return a string?!). You might not know it, but ActiveRecord is an anti-pattern. Also, this has **nothing** tho do with DDD (it not being mentioned anywhere in the answer, should have given it away already). And if you want to make a "better example" write your own answer. – tereško Mar 30 '18 at 22:56
  • @tereško It needn't return anything. Like I said, a service can create cookies. And whose says it has to interact with persistence? You are confused. I am not advocating for removing the service. Your "service" could be reduced to 3 lines of code: `$identity->login($password); $this->updateIdentityOnUse($identity); return $this->createCookie($identity);`. That is objectively better design. Period. Lower coupling. Higher cohesion. As an aside, a `Domain Service` should not be aware of persistence either. It follows the same rules as all the other domain objects. – user3347715 Mar 31 '18 at 03:34
  • This answer seems far more confusing than explanatory to me, esp. considering that there seem to be a HUGE variety of conflicting and competing explanations for just what this MVC pattern really is - so which is the "most right one"? And furthermore this seems to presuppose a lot of background and not just someone who is learning the pattern fresh (which is where I am at). Is there an easier introduction that nonetheless is actually correct? As I'm having a hard time figuring how all these things work together in detail, e.g. how does the "service" object get (cont'd) – The_Sympathizer Sep 24 '18 at 07:40
  • (cont'd) inside the "domain object" to pull its data out and send it through the "data mapper" so it can be written out (or conversely, read from the data mapper to the domain object), without creating at least _some_ degree of "knowledge" of the "domain object" that it's going to be giving out data _somewhere_ ? Or are "domain objects" entirely stateless, data-less aggregations of functions (so not necessarily even an "object" in the OOP sense) which the services then feed data to and from? – The_Sympathizer Sep 24 '18 at 07:42
  • And moreover, what might a very simple stripped down version of this look like in code (which might help to make it clear) with exactly one of each element? (Also, I'm not so much interested in this from PHP context, but others and am not very familiar with PHP, so the PHP examples are not very useful to me. "Pseudo-code" might be better, with clear indicators/markers pointing at what is a service object, domain object, and data mapper.) – The_Sympathizer Sep 24 '18 at 07:43
  • Right now, the vague idea I have of this is that it looks like a "tee", where that the "service" objects communicate to/from (read data in and out) of the "data mapper" objects, and to/from the "domain objects" (sending data to be crunched, because they're state-less, and receiving the result of that crunching), thus forming a "bar" shape, and then the "tee" comes off in that they also provide an interface to be grabbed onto by the controllers which communicate with Views. Is this right? – The_Sympathizer Sep 24 '18 at 07:47
  • @tereško Very nice answer. After reading all comments, it seems nobody asked this (which might mean it's stupid) : Doesn't the controller contain business logic in your example ? It knows it needs to find the identity, and then try to login with that identity, and then if it fails it should redirect to somewhere else. Doesn't it feel like this is business rule ? – Steve Chamaillard Oct 04 '18 at 08:42
  • You can't make your Service public while it doesn't have any validation... validation must be placed in Service/BL layer. – Yousha Aleayoub Dec 23 '19 at 12:43
  • Just when I thought I got to grips with PHP, I read this answer and now I feel like I'm 5 years old again. – user2924019 Aug 31 '21 at 09:48
  • @tereško Nice detailed answer! In your example of a method for a View, where is the `isUserLoggedIn()` method of the `identification` Service getting that information from? In other words, how/where are the state changes made by the Controller "saved" so that the View can access them later? I would think there would have to be some in-memory structure that both the Controller and View can access/modify. – noG23 Mar 28 '23 at 21:30
38

Everything that is business logic belongs in a model, whether it is a database query, calculations, a REST call, etc.

You can have the data access in the model itself, the MVC pattern doesn't restrict you from doing that. You can sugar coat it with services, mappers and what not, but the actual definition of a model is a layer that handles business logic, nothing more, nothing less. It can be a class, a function, or a complete module with a gazillion objects if that's what you want.

It's always easier to have a separate object that actually executes the database queries instead of having them being executed in the model directly: this will especially come in handy when unit testing (because of the easiness of injecting a mock database dependency in your model):

class Database {
   protected $_conn;

   public function __construct($connection) {
       $this->_conn = $connection;
   }

   public function ExecuteObject($sql, $data) {
       // stuff
   }
}

abstract class Model {
   protected $_db;

   public function __construct(Database $db) {
       $this->_db = $db;
   }
}

class User extends Model {
   public function CheckUsername($username) {
       // ...
       $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE ...";
       return $this->_db->ExecuteObject($sql, $data);
   }
}

$db = new Database($conn);
$model = new User($db);
$model->CheckUsername('foo');

Also, in PHP, you rarely need to catch/rethrow exceptions because the backtrace is preserved, especially in a case like your example. Just let the exception be thrown and catch it in the controller instead.

netcoder
  • 66,435
  • 19
  • 125
  • 142
  • My structure is very similar, I think I just separate it out a bit more. The reason why I was passing around the connection was because I needed to have chunks run in transactions. I wanted to add a user and then add the user to a role, but role back if one failed. The only way I could sort that out was to pass the connection. – Dietpixel May 03 '11 at 00:50
  • 11
    -1: it also happens to be completely wrong. Model is not an abstraction for a table. – tereško Nov 16 '14 at 22:38
  • 1
    The `User` class basically extends the model, but itsn't an object. User should be an object and has properties like: id, name ... You're deploying `User` class is a helper. – TomSawyer Dec 02 '16 at 17:43
  • 1
    I think you understand MVC but don't understand what's OOP. In this scenario, like i said, `User` stands for an object, and it should have properties of an User, not methods like `CheckUsername`, what should you do if you want to create new `User` object? `new User($db)` – TomSawyer Dec 02 '16 at 18:10
  • @TomSawyer OOP doesn't mean objects are required to have properties. What you're describing is a design pattern, one which is irrelevant to the question or an answer to that question. OOP is a language model, not a design pattern. – netcoder Dec 02 '16 at 18:17
  • @tereško `Model is not an abstraction for a table` do you have any ref/source for that? – Yousha Aleayoub Dec 23 '19 at 12:45
  • "Everything that is business logic belongs in a model, whether it is ... calculations" then what are services ever going to be used for other than pointless overkill for the sake of calling model? Services can do calculations and other things with business data, they just shouldn't directly request or alter it – James May 20 '21 at 13:49
20

In Web-"MVC" you can do whatever you please.

The original concept (1) described the model as the business logic. It should represent the application state and enforce some data consistency. That approach is often described as "fat model".

Most PHP frameworks follow a more shallow approach, where the model is just a database interface. But at the very least these models should still validate the incoming data and relations.

Either way, you're not very far off if you separate the SQL stuff or database calls into another layer. This way you only need to concern yourself with the real data/behaviour, not with the actual storage API. (It's however unreasonable to overdo it. You'll e.g. never be able to replace a database backend with a filestorage if that wasn't designed ahead.)

Community
  • 1
  • 1
mario
  • 144,265
  • 20
  • 237
  • 291
7

More oftenly most of the applications will have data,display and processing part and we just put all those in the letters M,V and C.

Model(M)-->Has the attributes that holds state of application and it dont know any thing about V and C.

View(V)-->Has displaying format for the application and and only knows about how-to-digest model on it and does not bother about C.

Controller(C)---->Has processing part of application and acts as wiring between M and V and it depends on both M,V unlike M and V.

Altogether there is separation of concern between each. In future any change or enhancements can be added very easily.

askmish
  • 6,464
  • 23
  • 42
Nagappa L M
  • 1,452
  • 4
  • 20
  • 33
0

In my case I have a database class that handle all the direct database interaction such as querying, fetching, and such. So if I had to change my database from MySQL to PostgreSQL there won't be any problem. So adding that extra layer can be useful.

Each table can have its own class and have its specific methods, but to actually get the data, it lets the database class handle it:

File Database.php

class Database {
    private static $connection;
    private static $current_query;
    ...

    public static function query($sql) {
        if (!self::$connection){
            self::open_connection();
        }
        self::$current_query = $sql;
        $result = mysql_query($sql,self::$connection);

        if (!$result){
            self::close_connection();
            // throw custom error
            // The query failed for some reason. here is query :: self::$current_query
            $error = new Error(2,"There is an Error in the query.\n<b>Query:</b>\n{$sql}\n");
            $error->handleError();
        }
        return $result;
    }
 ....

    public static function find_by_sql($sql){
        if (!is_string($sql))
            return false;

        $result_set = self::query($sql);
        $obj_arr = array();
        while ($row = self::fetch_array($result_set))
        {
            $obj_arr[] = self::instantiate($row);
        }
        return $obj_arr;
    }
}

Table object classL

class DomainPeer extends Database {

    public static function getDomainInfoList() {
        $sql = 'SELECT ';
        $sql .='d.`id`,';
        $sql .='d.`name`,';
        $sql .='d.`shortName`,';
        $sql .='d.`created_at`,';
        $sql .='d.`updated_at`,';
        $sql .='count(q.id) as queries ';
        $sql .='FROM `domains` d ';
        $sql .='LEFT JOIN queries q on q.domainId = d.id ';
        $sql .='GROUP BY d.id';
        return self::find_by_sql($sql);
    }

    ....
}

I hope this example helps you create a good structure.

Community
  • 1
  • 1
Ibu
  • 42,752
  • 13
  • 76
  • 103
  • 12
    "So if I had to change my database from MySQL to PostgreSQL there won't be any problem." Uhhhmmm with above code you would have a huge problem changing anything imo. – PeeHaa Oct 04 '12 at 19:54
  • I see my answer makes less and less sense after edit, and as time goes by. But it should stay here – Ibu Dec 19 '12 at 17:48
  • 2
    `Database` in the example is not a class. It is just a wrapper for functions. Also, how can you have "table object class" without an object? – tereško Dec 19 '12 at 19:03
  • 2
    @tereško I have read many of your posts and they're great. But, I cannot find any complete framework anywhere to study. Do you know of one that "does it right"? Or at least one that does it like you and some others here on SO say to do? Thanks. – johnny Sep 01 '14 at 20:12
  • I may be way late, but i'd like to point out that PDO almost solves the issue of having to create a DB 'layer' in order to facilitate future changes. – Matthew Goulart Mar 03 '16 at 15:44