MVC

Going Through the Model-View-Controller Pattern. — October 2, 2017

I can’t wait to get to the advanced topics such as N-Tier and REST! But they just won’t make any sense - or at least they won’t be so easily digested - until we’re all familiar with some other concepts, such as MVC. Let’s make it quick then: MVC stands for Model-View-Controller and it’s an architectural/design pattern (see an instance of the discussion and a reasonable opinion).

Why MVC?

Consider 30 or 20 years ago: we had a completely different scenario from today, where scripting was acceptable and spaghetti code was a common thing. Unfortunately, some people still put some effort in order to go back to that state. Take a look at these examples. Although it works, this programming style clearly lacks organization and will most likely result in hours of debugging in order to fix simple issues.

MVC defends this idea that once one’s code is separated between three main layers, it becomes much simpler and easier to maintain and improve. Here is a basic idea of the layers:

  • Models - the core, where the domain classes and your business logic are. E.g.: Users, Roles, Posts, Permissions, Tags, Classes, Accounts, Musics, Channels etc. In another words: anything that should be persisted to create your systems logic.
  • Controllers - coordinates models and views. In many modern MVC frameworks, it’s also responsible for handling users requests, authentication, authorization, filtering, security checking, validation etc. That is: allows the users to actually operate the application.
  • Views - containing all the presentation. This is were all the presentation code is (including HTML and CSS, when talking about web), staying completely separate from logic (models).
MVC Architecture diagram.
MVC Architecture diagram.

Some PHP practice won’t hurt…

There is this sequence of 9 short tutorials on YouTube, called Build a PHP MVC Application, which teaches us how to develop a simple structure characteristically present in MVC frameworks. It is specially similar to Laravel. If you have no idea what MVC is, I strongly suggest you to try to catch up with this channel, specially if you like PHP.

MVC Frameworks

Most people don’t want to make their own project structures, as crafting a project from scratch can be hard to accomplish. That’s why we have MVC frameworks! They were created to provide a starting point and let us concentrate on our domain problem. Many of them are similar, as many are deeply inspired on the ideas proposed by Ruby on Rails.

A simple forum application.

In order to demonstrate some of this in practice, let’s create a micro forum application. I’ll use the .NET Framework.

Creating the application

Go to File > New project > ASP.NET Web Application > MVC. Notice that once the project is created, all the MVC structure has been put there for you to use! You don’t have to do anything! Just hit Ctrl+F5 to see your web application working.

Inserting a model

Always start designing your data. Here, I’ll use this strategy called Code First, which allows me to write my classes and export them to a database using automatic migrations. So select your Models folder, hit Ctrl+Shift+A and add a new class called Post. Add the following properties to the class:

using System.ComponentModel.DataAnnotations;

namespace YCodingForum.Models
{
    public class Post
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [MinLength(4)]
        [MaxLength(35)]
        public string Title { get; set; }

        [MaxLength(50)]
        public string Lead { get; set; }

        [MaxLength(4096)]
        public string Content { get; set; }
    }
}

#1 Notice how I’ve defined rules for my model: in order to be considered valid, an instance of Post needs a Title with length greater or equal than 4 and smaller or equal to 35, a Lead smaller or equal to 50 and a Content smaller or equal than 4096.

Scaffolding

Now right click over the Controllers > Add > Controller > MVC 5 Controller with views, using Entity Framework. Select the model class Post (YCodingForum.Models), which you just created, and the default context ApplicationDbContext (YCodingForum.Models). Don’t use asynchronous controllers yet (although they are awesome, we’re going for simplicity). Make sure Generate views is enable and hit Add.

Build the project with Ctrl+Shift+B, run it Ctrl+F5 and use your browser to navigate to localhost:port/Posts and insert some new posts, if you want. That is already using a local database (which was automatically created).

Go back to the PostsController and take a look: each method in a controller class is a action, which can be accessed through the web. Write your own, if you want:

public ActionResult Recent()
{
    var recent = db.Posts
        .OrderByDescending(p => p.Id)
        .Take(10)
        .ToList();

    return View(recent);
}

Now right-click over the action that you’ve just created > Add view > Template: List > Model class: Post (YCodingForum.Models) > Add. Compile the project and go to localhost:port/Posts/Recent. You should be able to see the last 10 posts made (if you added them)!

#2 I know that all you’ve done was basically scaffolding, but look how far you already are! Do you still want to keep scripting php coupled with html code? :-)

Authentication

In order to prevent users that are not authenticated from creating, editing and deleting posts, go to the PostsController and add [Authorize] in each operation Create, Update and Delete:

[Authorize]
public ActionResult Create()
{
    ...
}

[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
    [Bind(Include = "Title,Lead,Content")] Post post)
{
    ...
}

...

Let’s give these posts authors! First, delete all posts previously created. Then go to your Post class and add the following property:

public class Post
{
    ...

    [ForeignKey("Author")]
    public string AuthorId { get; set; }
    public virtual ApplicationUser Author { get; set; }
}

We want the author of a Post to be the currently authenticated user. So go to PostsController and add the following line to the top:

using Microsoft.AspNet.Identity;

Now modify the create post action:

[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
    [Bind(Include = "Title,Lead,Content")] Post post)
{
    if (ModelState.IsValid)
    {
        post.AuthorId = User.Identity.GetUserId();

        db.Posts.Add(post);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(post);
}

Now if you try to compile and run your application, you’ll probably get an error. That happens because you’ve changed your models and the database is still the same. To fix that: Ctrl+Q, type Package Manage Console and write the following commands:

Enable-Migrations -EnableAutomaticMigrations
Update-Database -Force

#3 You do have to run Update-Database command every time you change you models. I know it sucks, but what can we do? This isn’t MongoDB. :-(

Run your application! If you try to create a new post, it’s going to redirect you to the login/registration page. Just make an account and go back to creating your post.

Customizing the views

Now, if we want to show these posts in the main page, we need to feed the Home.Index view with a list of posts. In the HomeController, add the header using System.Data.Entity; add the reference to the ApplicationDbContext, just like it is in PostsController:

private ApplicationDbContext db = new ApplicationDbContext();

Update the Index action:

public ActionResult Index()
{
    var posts = db.Posts
        .Include(post => post.Author)
        .OrderByDescending(p => p.Id)
        .Take(5);

    return View(posts);
}

#4 Notice that we used eager-loading, by adding .Include(post => post.Author).

And finally update the view Views/Home/Index.cshtml as this:

model IEnumerable<YCodingForum.Models.Post>
@{ViewBag.Title = "Home Page";}

<h1>YCodingForum</h1>

<div class="row">
    @foreach (var post in Model)
    {
        <div class="col-md-12">
            <h2>
                @post.Title <br />
                <small>@post.Lead</small>
            </h2>
            <p>@post.Content</p>
            <small>by @post.Author.Email</small>
        </div>
    }
</div>
<div class="text-right">
    <a href="/Posts" class="">more posts...</a>
</div>

What now?

That’s it. You have a really primitive forum application. It would be nice if we could comment on the posts as well! Why don’t you go ahead and try to implement the comments? Hint: they have a relationship many-to-one with ApplicationUser and another many-to-one to Post.

You can found the entire application above at github.com/lucasdavid/YCodingForum. Feel free to clone, fork and play with it. Furthermore, there is still so much to be done! Please visit asp.net/mvc for more information and tutorials on .NET MVC.

Conclusion

Look at what we’ve done: the creation was just a tiny step and we were able to consistently separate what’s logic code and what’s view code pretty easily. The downside is that MVC frameworks require you to understand their functioning and, of course, it takes time for you to get used with it. Until there, you’ll need to constantly search for how to do it on Google. However, it will eventually payoff with much cleaner code and, obviously, better days of debugging. Besides, their structures will eventually become as natural to you as the language that you program with is.

In any case, you can always keep a basic structure created by yourself and use if for all your future projects! This already increases your code’s quality by 100%!

Some references

Bellow there is a list of great MVC frameworks. All of them are currently maintained by a great number of programmers, they have extensive documentation and, of course, are absolutely free.