mvc description. Convenient Approach to Web Development: The MVC Model

Good afternoon, dear colleagues. In this article, I would like to talk about my analytical understanding of the differences between MVC, MVP and MVVM patterns. I was inspired to write this article by the desire to understand modern approaches to the development of large software and the corresponding architectural features. At the current stage of my career ladder, I am not a direct developer, so the article may contain errors, inaccuracies and misunderstandings. Curious about how analysts see what programmers and architects are doing? Then welcome under cat.

Links The first thing I would like to start with are links to external materials that I have been guided by in the process of writing this article:
Introduction In a time when the sun shone brighter and the grass was greener, at that time a team of students, like the author of this article, developed software by writing hundreds of lines of code directly into the product's interface. Sometimes services and managers were used to work with data, and then the solution was obtained using the Document-View pattern. The support of such code required enormous costs, since a new developer had to be trained (told) what code is responsible for what in the product, and there was no question of any unit testing. The development team is 4 people who sit in the same room.
Time passed, jobs changed. The applications being developed became larger and more complex, from one cohesive development team to many different teams of developers, architects, usabilityists, designers and PMs. Now everyone is responsible for their own area: GUI, business logic, components. There was a department of analysis, testing, architecture. The cost of software development has increased hundreds and even thousands of times. Such an approach to development requires a stable architecture that would synchronize different functional areas of the product with each other. Patterns Considering the goal of reducing labor costs for the development of complex software, suppose that it is necessary to use ready-made unified solutions. After all, the pattern of actions facilitates communication between developers, allows you to refer to well-known constructions, and reduces the number of errors.
According to Wikipedia, a pattern (English design pattern) is a repeatable architectural design, which is a solution to a design problem within some frequently occurring context.

Let's start with the first main one - Model-View-Controller. MVC is a fundamental pattern that has found application in many technologies, has given rise to new technologies and makes life easier for developers every day.

The MVC pattern first appeared in the SmallTalk language. The developers had to come up with an architectural solution that would separate the graphical interface from business logic, and business logic from data. Thus, in the classic version, MVC consists of three parts, which gave it its name. Consider them:

Model The Model is usually understood as the part containing the functional business logic of the application. The model should be completely independent from the rest of the product. The model layer doesn't need to know anything about the design elements and how it will be rendered. A result is achieved that allows you to change the presentation of data, how they are displayed, without touching the Model itself.

The model has the following features:

  • The model is the business logic of the application;
  • The model has knowledge about itself and does not know about controllers and views;
  • For some projects, the model is just a layer of data (DAO, database, XML file);
  • For other projects, a model is a database manager, a collection of objects, or simply application logic;
View The View is responsible for displaying the data received from the Model. However, the view cannot directly affect the model. A view can be said to have read-only access to the data.

The view has the following features:

  • The view implements the display of data that is obtained from the model in any way;
  • In some cases, a view may have code that implements some business logic.
Presentation examples: HTML page, WPF form, Windows Form. Differences between MVC & MVVM & MVP The most common types of MVC pattern are:
  • Model-View-Controller
  • Model-View-Presenter
  • Model-View-View Model

Consider and compare each of them.

Model-View-Presenter

This approach allows you to create an abstraction of the view. To do this, you need to highlight the view interface with a specific set of properties and methods. The presenter, in turn, receives a reference to the implementation of the interface, subscribes to view events, and changes the model upon request.

Presenter features:

  • The view interacts directly with the presenter by calling appropriate functions or events on the presenter instance;
  • The Presenter interacts with the View by using a special interface implemented by the View;
  • One presenter instance is associated with one view.

Implementation:
Each view must implement the corresponding interface. The view interface defines a set of functions and events required for user interaction (for example, IView .ShowErrorMessage(string msg)). The presenter must have a reference to the implementation of the corresponding interface, which is usually passed in the constructor.
The presentation logic must have a reference to the presenter instance. All view events are passed to the presenter for processing and are almost never handled by the view logic (including creating other views).

Usage example: Windows Forms.

Model-View-View Model

This approach allows you to associate view elements with properties and events of the View model. It can be argued that each layer of this pattern is unaware of the existence of the other layer.

Features of the View Model:

  • Two way communication with presentation;
  • A view model is an abstraction of a view. Usually means that the properties of the View are the same as those of the View/Model
  • The view model has no reference to the view interface (IView). Changing the state of the View model automatically changes the view and vice versa, since the data binding mechanism (Bindings) is used
  • One instance of the View Model is associated with one display.

Implementation:
When using this pattern, the view does not implement the corresponding interface (IView).
The view must have a link to the data source (DataContex), which in this case is the View model. View elements are bound (Bind) to the corresponding properties and events of the View model.
In turn, the View model implements a special interface that is used to automatically update view elements. An example of such an interface in WPF would be INotifyPropertyChanged.

Usage example: WPF

Model-View-Controller
The main idea of ​​this pattern is that both the controller and the view depend on the model, but the model does not depend on these two components in any way.

Controller Features

  • The controller determines which view should be displayed at the moment;
  • View events can only affect the controller. The controller can affect the model and define a different view.
  • It is possible to have multiple views for just one controller;

Implementation:
The controller intercepts the event from the outside and, in accordance with the logic embedded in it, reacts to this event by changing the Model by calling the appropriate method. After the change, the Model uses the event that it has changed, and all the View events subscribed to this event, having received it, turn to the Model for updated data, after which they are displayed.

Use Case: MVC ASP.NET

Summary The implementation of MVVM and MVP patterns, at first glance, looks quite simple and similar. However, for MVVM, the binding of a view to the View-model is carried out automatically, and for MVP, it is necessary to program
MVC seems to have more options for managing the view. General rules for pattern selection in MVVM
  • Used in a situation where data binding is possible without the need to introduce special view interfaces (i.e. there is no need to implement IView);
  • A common example is WPF technology.
MVP
  • Used in a situation where data binding is not possible (you cannot use Binding);
  • A common example would be using Windows Forms.
MVC
  • Used in a situation where communication between the view and other parts of the application is not possible (and you cannot use MVVM or MVP);
  • A common use case is ASP.NET MVC.
Conclusion In conclusion, the author of this article would like to point out that strictly sticking to only one pattern is not always the best choice. For example, imagine that you would like to use MVVM to develop applications using Windows Forms through the Bindings property of controls. Your goal is to separate the presentation from the business logic and the logic that links them. The application should be easily testable and maintainable, and understandable for analysts (after all, there is only one correct answer to the question “what is the hard drive performance measured in?” - in Joules (an abstract example of Model -> Views)).

Thank you very much for your time, happy reading!

SRC:
O'Reill. ActionScript 3.0 Design Patterns.
Chapter 12


What is the Model-View-Controller pattern?

A Model-View-Controller (MVC) is a composite pattern, or multiple patterns, that work together to implement complex applications. This pattern is most often used to create application interfaces, and as the name suggests, it consists of three elements:

Model(model)
Contains application data and logic to manage the state of that application

Performance(View)
Implements the user interface and state of the application, observable on the screen

Controller(Controller)
Handles user actions that affect the state of the application.

The power of the MVC pattern comes directly from the separation of these three elements in order to avoid overlapping areas of responsibility of each of them. Let's see what each of the elements is responsible for.

Model

The model is responsible for managing the state of the application. The application logic in the model is represented by two important tasks: the model responds to requests for application state, and performs actions in response to a state change request.

Performance

The view is the appearance of the application. The user interacts with the application through the View. An application can contain multiple Views, which can be both an input and an output mechanism. For example, in a portable digital player such as an iPod, the device's screen would be the View. Player buttons are also considered a View. The screen shows the title and duration of the song, album art, and other things that are related to the current state of the device. The view does not have to be visible. In a portable player, the music played in the headphones is also a Performance. For example, pressing a button may cause some audible response in the form of a click in the headphones. Volume changes are also reflected on the audio output channel. The audible feedback is related to the state of the application.

Controller

Although the term Controller implicitly implies the interface through which the application is controlled, in the MVC pattern, a Controller does not contain any user interface elements. As mentioned above, the user interface elements that provide input belong to the View component. The Controller, on the other hand, determines how Views respond to user input.

Let's say our digital player has buttons Louder And Quiet in the View. Sound volume is a state variable. The model will keep track of this variable in order to change the value of this variable in accordance with the application logic. If the sound volume value is graduated from 0 to 10, the Controller will determine how much to increase or decrease the sound when one of these buttons is pressed once. The behavior can tell the Model to increase the volume by 0.5, or by 0.1, or any other value as specified programmatically. In this vein, Controllers are implementation specifics that define how an application will respond to user input.

Although each element in the MVC triad has a separate and unique area of ​​responsibility, they do not function in isolation. In fact, in order to constitute an MVC pattern, each element must communicate with the others. What this means, we will consider below.

Interaction of MVC elements

Each element in the MVC pattern communicates with the rest in very specific ways. Communication is implemented by a sequence of events that are typically triggered by user interaction with the application. The sequence of events looks like this:

  • The user interacts with an interface element (for example, clicks on a button in a View).
  • The View sends a click event to the Controller to decide how to handle the click.
  • The Controller changes the Model based on what it decides about the button being pressed.
  • The Model informs the View that the Model's state has changed.
  • The View reads the state information in the Model and changes itself.
  • This is a very simple diagram of how MVC elements interact. In some cases, the Controller can simply tell the View to change. This is the only case where changes to the View become necessary due to user action and do not require changes to the Model, but merely result in visual changes alone. For example, think about how the user selects a song on a digital player. He selects a song from the list with the scroll buttons. The View needs to tell the Controller that the Scroll Up or Scroll Down buttons are pressed, but the Controller doesn't need to inform the Model. It directly tells the View to scroll the list of songs in the desired direction. Such a user action does not require changes to the Model. However, when the user selects a song from the list and starts playing it, the Controller will change the Model to reflect this change in the value of the currently playing song.

    In addition, changes in the Model are not always initiated by user actions. The model can update itself based on certain events. Take, for example, a stock price indicator. The model is not related to the current value of the shares. However, the cost itself changes and the Model can set a timer to receive periodic updates from the web service. And then, every time the Model updates its share price data, it will inform the View that the state has changed.

    Another feature of the MVC pattern is that each such Model can have more than one View associated with it. For example, in our portable player, volume settings can be shown on the display using a level meter. And besides, the sound level at the audio output correlates with the volume of the sound in the headphones. Both the display and the sound in the headphones are Device Status Representations.

    Take a look at Figure 12-1 and note the direction of the arrows. They show who initiates the interaction between elements. In order for one MVC element to communicate with another, it needs to know about it and own a reference to that element.

    Think of Model, View and Controller as three different classes. Let's see which classes need to have references to other classes:

    Performance
    He needs to have a reference to both the Model and the Controller

    Controller
    He needs to own a reference to the Model

    We started by stating that MVC is a composite pattern that combines multiple patterns. You must be wondering what patterns are included in this compound pattern. Or, more precisely, how can they be represented? The main advantage of using the MVC pattern is the ability to break it down into its constituent three elements. This allows us to link multiple Views to the same Model, replace Models and Controllers without affecting other elements. But some elements in the MVC triad must maintain references to other elements, and also support active communication between them. What can we call such a division? This has to do with the Observer, Strategy, and Composite patterns.

    Injecting Patterns in MVC

    As we've already seen, a Model can be associated with multiple Views. In MVC, the Model needs to inform all the Views associated with it about the changes that are taking place. In addition, this must be done without knowing specific details about the Views, and even without knowing how many Views need to change. This task is best accomplished by using an implementation of the Browser pattern (see Chapter 8).

    Each Model can have multiple Views associated with it. Similarly, Views can be complex, with multiple windows or panels containing other user interface elements. For example, such interface elements as buttons, text fields, lists, sliders, etc. can be grouped into a panel with tabs, and the panel, in turn, can be part of a window along with other panels. Each button or group of buttons can be a View. The same with the collection of text fields. It seems useful to treat a panel or window that contains collections of simple Views in the same way that we treat any other Views. This is where using the Linker pattern will save us a lot of effort (see Chapter 6). Why is the Linker pattern implementation so useful in this context? If Views can be nested, which they are when created using the Composer pattern, the update process is simplified. The update event will automatically bypass all descendant Views. Creating complex Views becomes easier when there is no need to send individual update messages to each nested View.

    Views limit their activity to only an external representation of the state of the Model. They pass UI events to the Controller. Therefore, a Controller is more of an algorithm for how to handle user input in a particular View. Such a delegation encapsulates the implementation of how a particular element of a custom element behaves in the face of a change in the Model. We can pretty easily replace one Controller for the same View to get a different behavior. This is the ideal context for implementing the Strategy pattern.

    Minimalistic MVC Pattern Example

    This simple example tracks keystrokes. When the next key is pressed, this causes the Model to change and informs the View to update itself. The view uses Flash's standard Output window to place the character code that the user has typed into it. The character code is the numeric value of the character in the code table of the current layout. This example explains how the Browser, Strategy, and Builder patterns are integrated into MVC.

    Model as Concrete Subject in Browser Template

    The relationship between Model and View is the relationship between Subject and Browser (See Chapter 8). The model must implement the Subject interface, which is part of the Browser pattern. Fortunately, ActionScript 3.0 has built-in classes that already implement this principle, using the ActionScript event model to inform Browsers of changes.

    EventDispatcher class in ActionScript 3.0

    The EventDispatcher class is provided with the IEventDispatcher interface. Along with other methods, the IEventDispatcher interface defines the following methods that are required for a subject in the Browser template. (See the AS3 documentation for details on all method parameters).

    addEventListener(type :String , listener:Function , useCapture:Boolean = false , priority:int = 0 , useWeakReference:Boolean = false ) :void removeEventListener(type :String , listener:Function , useCapture:Boolean = false ) :void dispatchEvent( event:Event) :Boolean

    In order for a Model to act as a Concrete Subject in the Browser pattern, you must implement the IEventDispatcher interface. However, the easiest way for a particular custom class to get the ability to distribute events is to inherit from the EventDispatcher class.

    The browser registers listener methods to receive notification from EventDispatcher objects using the addEventListener() method.

    Model

    Our Model stores the character code corresponding to the pressed key in a property. You need to implement a setter and getter to make it possible for the View and Controller to access and change this property. Let's define for our Model (Example 12-1).

    Example 12-1. IModel.as

    package ( import flash.events .* ; public interface IModel extends IEventDispatcher ( function setKey(key :uint) :void ; function getKey() :uint; ) )

    The IModel interface, shown in Example 12-1, extends the IEventDispatcher interface and defines a couple of methods for reading and setting the character code of the last key pressed. Because the IModel interface extends IEventDispatcher, any class that implements it must implement all the methods defined in both interfaces. The Model class shown in Example 12-2 implements the IModel interface.

    Example 12-2. Model.as

    package ( import flash.events .* ; public class Model extends EventDispatcher implements IModel ( private var lastKeyPressed:uint=0 ; public function setKey(key :uint) :void ( this .lastKeyPressed =key ; dispatchEvent(new Event(Event.CHANGE ) ) ; // event propagates ) public function getKey() :uint ( return lastKeyPressed; ) ) )

    The Model class extends the EventDispatcher class, which already implements the IEventDispatcher interface. Note that the dispatchEvent() function is called inside the setKey() method. It dispatches a CHANGE event to all registered browsers as soon as the value of lastKeyPressed changes inside the setKey() method.

    Controller as Concrete Strategy in Strategy template.

    The relationship between Controller and View can be represented as a strategy and a context in the Strategy pattern. Each Controller will be a specific strategy that implements the required behavior within the framework of the Strategy interface.

    Controller

    In our minimalistic example, the behavior required from the Controller is just accepting a keypress event. IKeyboardInputHandler is a Strategy interface (Example 12-3) where a single keyPressHandler() method is defined.

    Example 12-3. IKeyboardInputHandler.as

    package ( import flash.events .* ; public interface IKeyboardInputHandler ( function keyPressHandler(event:KeyboardEvent) :void ; ) )

    The concrete Controller would be the Controller class (Example 12-4), which implements the IKeyboardInputHandler interface.

    Example 12-4. Controller.as

    package ( import flash.events .* ; public class Controller implements IKeyboardInputHandler ( private var model:IModel; public function Controller(aModel:IModel) ( this .model =aModel; ) public function keyPressHandler(event:KeyboardEvent) :void ( model. setKey (event.charCode ); // change the model ) ) )

    Note that the Controller has a constructor that takes an instance of the Model as a parameter. This is necessary so that the Controller can communicate with the Model, as shown in Figure 12-1. Therefore, it is necessary to store a reference to the Model.

    The keyPressHandler() method takes a user interface event (in this case, KeyboardEvent) as a parameter and then decides how to handle it. In our example, it simply sets the code of the pressed key in the Model.

    Representation as a Concrete Browser in the Browser template and Context in the Strategy template

    The view is perhaps the most complex element in the MVC pattern. It plays the role of an integrating part in the implementation of both the Browser and the Strategy patterns, which forms the basis of its relationship with the Model and Controller. The View class shown in Listing 12-5 implements a View in a minimalistic example.

    Example 12-5. View.as

    package ( import flash.events .* ; import flash.display .* ; public class View ( private var model:IModel; private var controller:IKeyboardInputHandler; public function View(aModel:IModel,oController:IKeyboardInputHandler,target :Stage ) ( this .model =aModel; this .controller =oController; // subscribes to receive notifications from the Model model.addEventListener (Event.CHANGE ,this .update ) ; // subscribes to receive keystrokes from the scene target .addEventListener (KeyboardEvent.KEY_DOWN ,this .onKeyPress ) ; ) private function update(event:Event) :void ( // getting data from the Model and updating the View trace (model.getKey () ) ; ) private function onKeyPress(event:KeyboardEvent) :void ( // processing is passed to Controller (Strategy) for processing controller.keyPressHandler (event) ; ) ) )

    The View needs both Model and Controller references in order to interact with them, as shown in Figure 12-1. Both the Model instance and the Controller instance are passed to the View in its constructor. In addition, the View in our example needs a reference to the Stage in order to register itself as a receiver of keypress events.

    In addition to drawing the user interface, the View class performs a couple of other important tasks. It registers with the Model to receive update events, and delegates to the Controller to handle user input. In our example, the View has no external visible presence on the scene, but displays the Model's state in the Output window. It needs to receive the keypress event, and registers the onKeyPress() method to receive the KEY_DOWN event from the scene. The second task is to register an update() listener method to receive the CHANGE event from the model. When a change notification is received, the update() method reads the code of the last key pressed from the Model and prints it to the output window using the trace() function.

    Building the MVC Triad

    We have considered the implementation of each of the three parts of the MVC pattern separately. However, there must be a client that will initialize each element and build the MVC model. In fact, there will be no complex construction - everything that needs to be done has already been done when writing the Model, View and Controller classes. Listing 12-6 is a Flash document class that illustrates the elements of MVC.

    Example 12-6. Main.as (main class of the minimalistic example)

    package ( import flash.display .* ; import flash.events .* ; /** * Main Class * @ purpose: Document class for movie */ public class Main extends Sprite ( public function Main() ( var model:IModel=new Model ; var controller:IKeyboardInputHandler=new Controller(model) ; var view:View=new View(model,controller,this .stage ) ; ) ) )

    After the Model, Controller and View are initialized, they will communicate with each other and start working. Pressing a key on the keyboard will display the corresponding character code in the Output window.

    You need to disable shortcuts for testing keystrokes. Otherwise, the Flash UI will intercept keypress events that correspond to shortcuts. To disable keyboard shortcuts, select Disable Keyboard Shortcuts from the Control menu while the video is running.

    Note that the Model instance is passed to the Controller. Similarly, Model and Controller instances are passed to the View. We can simply replace the existing Model and Controller with others, as long as they implement the IModel and IKeyboardInputHandler interfaces. Additional Views can also be painlessly added directly to the Subject-Viewer relationship between Model and View. The Model doesn't know anything about the Views, since it's up to the View to register itself as a listener for the Model's change notifications. This is a big plus of the MVC pattern; Model, View and Controller are separated, loosely coupled, which gives flexibility in their use.

    Nested Views and Template Nodes

    Recall that the View is perhaps the most complex element in the MVC triad, because in the MVC context it is involved in both the implementation of the Browser pattern and the Strategy. Our View elements can be more complex because they can implement a third pattern, the Layoutr (see the Layoutr pattern examples in Chapter 6). Implementing Views as Template Elements The Composite lets you deal with complex user interfaces that contain multiple Views. Nesting Views brings some benefits to the UI update process, as updates can be propagated along the branches of the composite View's structural tree. Composite Views can also add and remove nested Views based on application behavior and user preferences. A good example of a complex interface is the Properties Inspector panel in the Flash authoring environment. The contents of the Properties Inspector are context sensitive, and UI elements appear and disappear depending on which object is selected in the scene.

    Component and Composite View

    The first step is to create the component and composite classes for the View. These classes must be declared abstract, must be subclasses, and must not be instantiated, as shown in Example 12-7.

    Example 12-7. ComponentView.as

    package ( import flash.errors .IllegalOperationError ; import flash.events .Event ; import flash.display .Sprite ; public class ComponentView extends Sprite ( ( protected var model:Object ; protected var controller:Object ; public function ComponentView(aModel:Object , aController:Object =null ) ( this .model =aModel; this .controller =aController; ) public function add (c:ComponentView) :void ( throw new IllegalOperationError("add operation not supported" ) ; ) public function remove(c: ComponentView) :void ( throw new IllegalOperationError("remove operation not supported" ) ; ) public function getChild(n:int ) :ComponentView ( throw new IllegalOperationError("getChild operation not supported" ) ; return null ; ) // ABSTRACT method( must be overridden in the descendant class) public function update(event:Event=null ) :void ( ) ) ) )

    The ComponentView class from Example 12-7 defines an abstract interface for a Component View. This is similar to the classic component class from Chapter 6, but with a few key differences. The ComponentView class holds a reference to the Model and View, and contains a constructor. Not all Views handle user input, and a component View can be constructed by simply passing in an instance of the Model. Therefore, the aController parameter in the constructor is null by default. Also note that the ComponentView class is derived from the Sprite class. This makes sense since most Views draw the user interface on the stage. We can use the properties and methods implemented in the Sprite class to draw and add objects to the display list.

    The update() method must behave like an abstract method. Child Views that are descendants of a ComponentView must override and implement the update() method in order to be able to update their part of the user interface. For this reason, a parameter of type Event is passed to the method. This parameter is also set to null by default, which allows you to call update() without passing an event as a parameter. This approach is useful when the initially rendered UI is in its default state, and our next example illustrates this.

    The CompositeView class extends ComponentView and overrides methods that are responsible for child Views.

    Example 12-8. CompositeView.as

    package ( import flash.events .Event ; // ABSTRACT class (you need to inherit from it without creating an instance of this class) public class CompositeView extends ComponentView ( private var aChildren:Array ; public function CompositeView(aModel:Object ,aController:Object =null ) ( super (aModel,aController) ; this .aChildren =new Array ; ) override public function add (c:ComponentView) :void ( aChildren.push (c) ; ) override public function update(event:Event=null ) :void ( for each (var c:ComponentView in aChildren) ( c.update (event) ; ) ) ) )

    Notice the override update() function of the CompositeView class in Example 12-8. It calls the update method on all child classes. Therefore, calling the update() function at the root of a composite View's structure will propagate the update through the structure and traverse the component's tree, updating all Views. Let's extend the CompositeView and ComponentView classes and create a View structure to see how it works.

    Many people start writing a project to work with a single task, not implying that it can grow into a multi-user management system, let's say, content or, God forbid, production. And everything seems to be great and cool, everything works until you start to understand that the code that is written consists entirely of crutches and hardcode. The code is mixed with layout, requests and crutches, sometimes even unreadable. An urgent problem arises: when adding new features, you have to fiddle with this code for a very long time, remembering “what was written there?” and curse yourself in the past.

    You may have even heard about design patterns and even flipped through these great books:

    • E. Gamma, R. Helm, R. Johnson, J. Vlissides “Object Oriented Design Techniques. Design patterns";
    • M. Fowler "Architecture of Enterprise Software Applications".
    And many, not afraid of huge manuals and documentation, tried to study any of the modern frameworks and, faced with the difficulty of understanding (due to the presence of many architectural concepts cleverly linked to each other), put off the study and application of modern tools on the back burner.

    The presented article will be useful primarily for beginners. In any case, I hope that in a couple of hours you will be able to get an idea of ​​​​the implementation of the MVC pattern that underlies all modern web frameworks, as well as get “food” for further reflection on “how to do it”. At the end of the article, a selection of useful links is provided that will also help you understand what web frameworks (besides MVC) consist of and how they work.

    Hardened PHP programmers are unlikely to find something new for themselves in this article, but their comments and comments on the main text would be very helpful! Because without theory, practice is impossible, and without practice, theory is useless, then at first there will be a little bit of theory, and then we will move on to practice. If you are already familiar with the concept of MVC, you can skip the theory section and jump straight into practice.

    1. Theory The MVC pattern describes a simple way to build an application structure that aims to separate the business logic from the user interface. As a result, the application is easier to scale, test, maintain, and of course implement.

    Consider the conceptual scheme of the MVC template (in my opinion, this is the most successful scheme of those that I have seen):

    In an MVC architecture, the model provides the data and business logic rules, the view is responsible for the user interface, and the controller provides the interaction between the model and the view.

    A typical MVC application workflow can be described as follows:

  • When a user enters a web resource, the initialization script creates an instance of the application and launches it for execution.
    This displays a view of, say, the main page of the site.
  • The application receives a request from the user and determines the requested controller and action. In the case of the main page, the default action is performed ( index).
  • The application instantiates the controller and runs the action method,
    which, for example, contains calls to the model that read information from the database.
  • After that, the action generates a view with the data received from the model and displays the result to the user.
  • Model - contains the business logic of the application and includes methods for sampling (these can be ORM methods), processing (for example, validation rules) and providing specific data, which often makes it very thick, which is quite normal.
    The model should not directly interact with the user. All variables related to the user's request must be processed in the controller.
    The model should not generate HTML or other rendering code, which may change depending on the needs of the user. Such code should be handled in views.
    The same model, for example: the user authentication model can be used both in the user and in the administrative part of the application. In this case, you can move the common code into a separate class and inherit from it, defining methods specific to subapplications in the heirs.

    View - used to set the external display of data received from the controller and model.
    Views contain HTML markup and small inserts of PHP code for crawling, formatting, and displaying data.
    Should not access the database directly. Models should do this.
    Should not work with data received from a user request. This task must be performed by the controller.
    It can directly access the properties and methods of the controller or models to get output-ready data.
    Views are usually divided into a common template that contains markup common to all pages (for example, a header and footer) and template parts that are used to display data output from the model or display data entry forms.

    The controller is the link that connects models, views, and other components into a working application. The controller is responsible for handling user requests. The controller must not contain SQL queries. It is better to keep them in models. The controller must not contain HTML or other markup. It deserves to be brought out into the open.
    In a well-designed MVC application, controllers are usually very thin and contain only a few dozen lines of code. What can not be said about Stupid Fat Controllers (SFC) in CMS Joomla. The logic of the controller is quite typical and most of it is taken out in the base classes.
    Models, on the other hand, are very thick and contain most of the code associated with data processing. the structure of the data and the business logic it contains are usually quite specific to a particular application.

    1.1. Front Controller and Page Controller In most cases, user interaction with a web application takes place through links. Look now at the address bar of the browser - you received this text from this link. Other links, such as those on the right side of this page, will take you to other content. Thus, the link represents a specific command to the web application.

    I hope you have already noticed that different sites can have completely different formats for constructing the address bar. Each format can represent the architecture of a web application. Although this is not always the case, in most cases it is a clear fact.

    Consider two options for the address bar, which show some text and a user profile.

    Approximate processing code in this case:
    switch($_GET["action"]) ( case "about" : require_once("about.php"); // "About Us" page break; case "contacts" : require_once("contacts.php"); // page "Contacts" break; case "feedback" : require_once("feedback.php"); // page "Feedback" break; default: require_once("page404.php"); // page "404" break; )
    I think almost everyone has done this before.

    Using the URL routing engine, you can configure your application to accept requests like this to display the same information:
    http://www.example.com/contacts/feedback

    Here, contacts is a controller, and feedback is a method of the contacts controller that renders a feedback form, and so on. We will return to this issue in the practical part.

    It's also worth knowing that the routers of many web frameworks allow you to create arbitrary URL routes (specify what each part of the URL means) and rules for processing them.
    Now we have sufficient theoretical knowledge to move on to practice.

    2. Practice First, let's create the following file and folder structure:


    Looking ahead, I will say that the base classes Model, View and Controller will be stored in the core folder.
    Their children will be stored in the controllers, models and views directories. The index.php file is the entry point into the application. The bootstrap.php file initiates the loading of the application, including all the necessary modules, etc.

    Let's go sequentially; open the index.php file and fill it with the following code:
    ini_set("display_errors", 1); require_once "application/bootstrap.php";
    There should be no questions here.

    Next, let's jump straight to the bootstrap.php file:
    require_once "core/model.php"; require_once "core/view.php"; require_once "core/controller.php"; require_once "core/route.php"; Route::start(); // start the router
    The first three lines will include currently non-existent kernel files. The last lines include the file with the router class and start it for execution by calling the static start method.

    2.1. Implementing a URL Router For now, let's deviate from the implementation of the MVC pattern and focus on routing. The first step we need to take is to put the following code in .htaccess :
    RewriteEngine On RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule .* index.php [L]
    This code will redirect the processing of all pages to index.php , which is what we need. Remember in the first part we talked about the Front Controller?!

    We will place the routing in a separate route.php file in the core directory. In this file, we will describe the Route class, which will run the methods of the controllers, which in turn will generate the appearance of the pages.

    Route.php file content

    class Route ( static function start() ( // default controller and action $controller_name = "Main"; $action_name = "index"; $routes = explode("/", $_SERVER["REQUEST_URI"]); // get controller name if (!empty($routes)) ( $controller_name = $routes; ) // get action name if (!empty($routes)) ( $action_name = $routes; ) // add prefixes $model_name = " Model_".$controller_name; $controller_name = "Controller_".$controller_name; $action_name = "action_".$action_name; // hook the file with the model class (there may not be a model file) $model_file = strtolower($model_name). ".php"; $model_path = "application/models/".$model_file; if(file_exists($model_path)) ( include "application/models/".$model_file; ) // hook the controller class file $controller_file = strtolower ($controller_name).".php"; $controller_path = "application/controllers/".$controller_file; if(file_exists($controller_path)) ( include "application/controllers/".$controller_file; ) else ( /* it would be correct to throw an exception here, but for simplicity we will immediately redirect to the 404 page */ Route::ErrorPage404(); ) // create a controller $controller = new $controller_name; $action = $action_name; if(method_exists($controller, $action)) ( // call the controller action $controller->$action(); ) else ( // it would also make more sense to throw an exception here Route::ErrorPage404(); ) ) function ErrorPage404( ) ( $host = "http://".$_SERVER["HTTP_HOST"]."/"; header("HTTP/1.1 404 Not Found"); header("Status: 404 Not Found"); header(" Location:".$host."404"); ) )


    I note that the class implements very simplified logic (despite the voluminous code) and perhaps even has security problems. This was done intentionally, because. writing a full-fledged routing class deserves at least a separate article. Let's take a look at the main points...

    The element of the global array $_SERVER["REQUEST_URI"] contains the full address to which the user applied.
    For example: example.ru/contacts/feedback

    Using the function explode the address is divided into components. As a result, we get the name of the controller, for the example given, this is the controller contacts and the name of the action, in our case - feedback.

    Next, the model file is connected (the model may be missing) and the controller file, if any, and finally, the controller instance is created and the action is called, again, if it was described in the controller class.

    Thus, when you go, for example, to the address:
    example.com/portfolio
    or
    example.com/portfolio/index
    The router will do the following:

  • connect the model_portfolio.php file from the models folder containing the Model_Portfolio class;
  • include the controller_portfolio.php file from the controllers folder containing the Controller_Portfolio class;
  • will create an instance of the Controller_Portfolio class and call the default action - the action_index described in it.
  • If the user tries to access the address of a non-existent controller, for example:
    example.com/ufo
    then it will be redirected to the 404 page:
    example.com/404
    The same will happen if the user accesses an action that is not described in the controller.2.2. Returning to the MVC implementation Let's go to the core folder and add three more files to the route.php file: model.php, view.php and controller.php


    Let me remind you that they will contain base classes, which we will now start writing.

    The contents of the model.php file
    class Model ( public function get_data() ( ) )
    The model class contains a single empty data fetch method that will be overridden in descendant classes. When we create descendant classes, everything will become clearer.

    The contents of the view.php file
    class View ( //public $template_view; // here you can specify the default public view. function generate($content_view, $template_view, $data = null) ( /* if(is_array($data)) ( // convert array elements into variables extract($data); ) */ include "application/views/".$template_view; ) )
    It is not difficult to guess that the method generate designed to form a view. The following parameters are passed to it:

  • $content_file - views displaying page content;
  • $template_file - common template for all pages;
  • $data is an array containing the content elements of the page. Usually filled in the model.
  • The include function dynamically connects a common template (view), inside which the view will be embedded
    to display the content of a particular page.

    In our case, the general template will contain a header, menu, sidebar and footer, and the content of the pages will be contained in a separate form. Again, this is done for simplicity.

    Contents of controller.php file
    class Controller ( public $model; public $view; function __construct() ( $this->view = new View(); ) function action_index() ( ) )
    Method action_index is the action called by default, we will override it when implementing descendant classes.

    2.3. Implementing Model and Controller descendant classes, creating View's Now the fun begins! Our business card site will consist of the following pages:
  • home
  • Services
  • Portfolio
  • Contacts
  • And also - page "404"
  • Each page has its own controller from the controllers folder and a view from the views folder. Some pages may use a model or models from the models folder.


    In the previous figure, the template_view.php file is highlighted separately - this is a template containing the markup common to all pages. In the simplest case, it could look like this:
    home
    To give the site a presentable look, we will make up a CSS template and integrate it into our site by changing the structure of the HTML markup and including CSS and JavaScript files:

    At the end of the article, in the "Result" section, there is a link to a GitHub repository with a project in which the integration of a simple template has been done.

    2.3.1. Creating the main page Let's start with the controller controller_main.php , here is its code:
    class Controller_Main extends Controller ( function action_index() ( $this->view->generate("main_view.php", "template_view.php"); ) )
    The method generate instance of the View class, the names of the files of the general template and the view with the content of the page are passed.
    In addition to the index action, the controller can of course contain other actions.

    We discussed the file with a general view earlier. Consider the content file main_view.php :
    Welcome!

    OLOLOSHA TEAM is a team of first-class website development specialists with many years of experience in collecting Mexican masks, bronze and stone statues from India and Ceylon, bas-reliefs and sculptures created by the masters of Equatorial Africa five or six centuries ago...


    This contains simple markup without any PHP calls.
    To display the main page, you can use one of the following addresses:

    An example of using a view that displays data received from the model will be discussed later.

    2.3.2. Creating the Portfolio Page In our case, the Portfolio page is the only page that uses the model.
    The model usually includes data fetching methods, for example:
  • methods of native pgsql or mysql libraries;
  • methods of libraries that implement data abstraction. For example, the PEAR MDB2 library methods;
  • ORM methods;
  • methods for working with NoSQL;
  • and etc.
  • For simplicity, we will not use SQL queries or ORM statements here. Instead, we simulate real data and immediately return an array of results.
    The model_portfolio.php model file will be placed in the models folder. Here is its content:
    class Model_Portfolio extends Model ( public function get_data() ( return array(array("Year" => "2012", "Site" => "http://DunkelBeer.ru", "Description" => "Dark Dunkel beer from the German manufacturer Löwenbraü produced in Russia by the brewing company "SUN InBev"."), array("Year" => "2012", "Site" => "http://ZopoMobile.ru", "Description" => "Russian-language catalog of Chinese Zopo phones based on Android OS and accessories.", // todo); ) )

    The model controller class is contained in the controller_portfolio.php file, here is its code:
    class Controller_Portfolio extends Controller ( function __construct() ( $this->model = new Model_Portfolio(); $this->view = new View(); ) function action_index() ( $data = $this->model->get_data( ); $this->view->generate("portfolio_view.php", "template_view.php", $data); ) )
    into a variable data the array returned by the method is written get_data, which we considered earlier.
    This variable is then passed as a method parameter. generate, which is also passed: the name of the file with the common template and the name of the file containing the view with the page content.

    The view containing the page content is located in the portfolio_view.php file.
    Portfolio

    All projects in the following table are fictitious, so do not even try to follow the links provided.
    YearProjectDescription


    Everything is simple here, the view displays the data received from the model.

    2.3.3. Creating the rest of the pages The rest of the pages are created in the same way. Their code is available in the repository on GitHub, a link to which is given at the end of the article, in the "Result" section.3. Result And here is the result:

    Screenshot of the resulting business card site



    Link to GitHub: https://github.com/vitalyswipe/tinymvc/zipball/v0.1

    But in this version, I sketched the following classes (and their corresponding types):

    • Controller_Login in which a view is generated with a form for entering a login and password, after which the authentication procedure is performed and, if successful, the user is redirected to the admin panel.
    • Contorller_Admin with an index action that checks if the user was previously authorized on the site as an administrator (if so, the admin view is displayed) and a logout action for logging out.
    Authentication and authorization is a different topic, so it is not covered here, but only the link indicated above is given to start from.4. Conclusion The MVC pattern is used as an architectural basis in many frameworks and CMSs that were created to be able to develop qualitatively more complex solutions in a shorter time. This was made possible by increasing the level of abstraction, since there is a limit to the complexity of the structures that the human brain can operate on.

    But using web frameworks such as Yii or Kohana, which consist of several hundred files, when developing simple web applications (for example, business card sites) is not always advisable. Now we can create a beautiful MVC model so as not to mix Php, Html, CSS and JavaScript code in one file.

    This article is more of a starting point for learning CMF than an example of something truly correct that you can take as the basis of your web application. Perhaps it even inspired you and you are already thinking of writing your own microframework or CMS based on MVC. But, before inventing the next wheel with "blackjack and whores", think again, maybe it would be wiser to direct your efforts to the development and to help the community of an existing project?!

    P.S.: The article has been rewritten taking into account some of the comments left in the comments. Criticism has been very helpful. Judging by the response: comments, personal appeals and the number of users who added the post to their favorites, the idea to write this post turned out to be not so bad. Unfortunately, it is not possible to take into account all the wishes and write more and in more detail due to lack of time ... but perhaps those mysterious personalities who minus the original version will do this. Good luck with your projects!

    5. A selection of useful links on the subject The article very often touches on the topic of web frameworks - this is a very extensive topic, because even microframeworks consist of many components that are cleverly linked to each other and it would take more than one article to talk about these components. However, I decided to give here a small selection of links (which I went to while writing this article) that in one way or another relate to the topic of frameworks.

    Tags: Add tags

    In this article, we will understand the concept of MVC, and how, with an example, you can apply this in PHP.

    Concept of MVC

    MVC (Model-View-Controller, " Model-View-Behavior», « Model-View-Controller”) is an application design pattern in which the control logic is divided into three separate components in such a way that modifying one of them has minimal impact on the others.

    The MVC template is good to use when creating complex projects where it is necessary to separate the work of a php programmer (or divide a group of programmers into departments), a designer, a coder, etc.

    The MVC pattern separates presentation, data, and handling of user actions into three separate components:

    MVC Model (Model). The model provides data (usually to the View) and also responds to requests (usually from the controller) by changing its state.

    MVC View. Responsible for displaying information (user interface).

    MVC Behavior (Controller). Interprets user input and informs the model and view to react appropriately.

    For clarity, the scheme of the MVC pattern, an illustration is provided below.

    Components such as presentation and behavior depend on the model, but do not affect it in any way. A model can have multiple views. Maybe the concept of MVC is difficult to understand, but once understood, it becomes indispensable when developing applications in PHP.

    MVC in PHP

    A special feature when using MVC in PHP is that there is one entry point to the php application, which, for example, is achieved as follows. An index.php is created through which all requests will be processed, for this we create a .htaccess file in the folder with the index and place the following code in it:

    RewriteEngine on RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule ^(.*)$ index.php?route=$1

    In the provided code, the first line checks for the existence of the requested file, and if it does not exist, then it is redirected to index.php, otherwise even site image requests will be redirected to the index. The last line of code converts requests like index.php?route=chat/index to like index.php/chat/index. If you are not able to use ModRewrite in your application, then you will have to do the redirect manually.

    PHP Model

    Data about the PHP model is contained in its attributes and can only be changed through special functions. A model can contain multiple views. As a rule, phpmodel is a class that works with the database, more specifically: writing, reading, deleting. Naturally, reading information from the database can be implemented by several views (functions). As an example, the model of articles on the site: you can get a specific article from the database, a list of recent, popular, some category ... these are all representations of the model. For clarity, an example of a php model is provided below.

    PHP Controller (Behavior)

    PHP controllers receive user requests that we sent through index.php, and in accordance with them, adjust the model's work. It would be more correct to say that they control the work of the php application.

    PHP View

    The view tracks the change in the model and creates or changes the application's php interface.

    List of data

    How does this PHP MVC template work?

    When the user accesses the desired url, the corresponding controller is selected, which refers to the view and model, and information is displayed. In other words, the controller in mvc is the link between the model and the view.

    Advantages of the MVC pattern when creating a PHP application

    As mentioned above, this is, first of all, the differentiation of the developers of the php site into departments. The speed of the php application also increases if a large project is created. Well, what concerns directly the php developer himself, this is the correct structuring of the php code (everything is in its place, it's easier to understand).

    MVC example

    We won't get too hung up on the example of how MVC works, as it already exists. I'll just add a couple more diagrams for a deeper understanding.

    Another scheme of the MVC template in PHP, it is more than understandable.

    The Model-View-Controller (MVC) pattern is extremely useful when building applications with complex GUIs or behaviors. But it is also suitable for simpler cases. In this post, we will create a minesweeper game designed around this pattern. Python was chosen as the development language, but there is no particular significance in this. Patterns do not depend on a specific programming language and you can easily transfer the resulting implementation to any other platform.

    Advertisement Briefly about the MVC pattern

    As the name suggests, the MVC pattern has 3 components: Model, View and Controller. Each of the components performs its role and is interchangeable. This means that the components are connected to each other only by some clear interfaces, behind which any implementation can lie. This approach allows you to replace and combine various components, providing the necessary logic of work or the appearance of the application. Let's look at the functions that each component performs.

    Model

    Responsible for the internal logic of the program. Here we can hide the ways of storing data, as well as the rules and algorithms for processing information.

    For example, for one application, we can create several models. One will be debugging, and the other working. The first can store its data in memory or in a file, and the second already uses the database. In fact, this is just a Strategy pattern.

    Performance

    Responsible for displaying Model data. At this level, we only provide an interface for user interaction with the Model. The point of introducing this component is the same as in the case of providing different ways of storing data based on several Models.

    For example, in the early stages of development, we can create a simple console view for our application, and only then add a beautifully designed GUI. Moreover, it remains possible to save both types of interfaces.

    In addition, it should be borne in mind that the responsibility of the View is only to display the state of the Model in a timely manner. The Controller is responsible for processing user actions, which we will talk about now.

    Controller

    Provides a link between the Model and user actions resulting from interacting with the View. Coordinates when the Model and View states are updated. Makes most of the decisions about application transitions from one state to another.

    In fact, for every action that the user can take in the View, a handler must be defined in the Controller. This handler will perform the appropriate manipulations on the model and, if necessary, inform the View about the presence of changes.

    Advertisement Minesweeper game specs

    Enough theory. Now let's move on to practice. To demonstrate the MVC pattern, we will write a simple game: Minesweeper. The rules of the game are quite simple:

  • The playing field is a rectangular area consisting of cells. Mines are randomly placed in some cells, but the player does not know about them;
  • The player can click on any cell of the playing field with the left or right mouse buttons;
  • Clicking the left mouse button causes the cell to be opened. In this case, if there is a mine in the cell, then the game ends with a loss. If there are mines in neighboring cells, next to the open one, then a counter with the number of mines around will be displayed on the open cell. If there are no mines around an open cell, then each adjacent cell will be opened according to the same principle. That is, the cells will open until either they hit the border of the playing field, or they reach the already open cells, or there is a mine next to them;
  • Clicking the right mouse button allows you to make marks on the cells. Clicking on a closed cell marks it with a flag that locks its state and prevents accidental opening. Clicking on a cell marked with a flag changes its flag to a question mark. In this case, the cell is no longer blocked and can be opened with the left mouse button. Clicking on a cell with a question mark returns its unmarked closed state;
  • Victory is determined by the state of the game, in which all cells on the playing field are open, with the exception of mined ones.
  • An example of what we get is shown below:

    Minesweeper UML Diagrams

    Before moving on to writing code, it would be nice to think over the architecture of the application in advance. It should not depend on the implementation language, so UML is best suited for our purposes.

    Game Cell State Diagram

    Any cell on the playing field can be in one of 4 states:

  • The cell is closed;
  • The cage is open;
  • The cell is flagged;
  • The cell is marked with a question mark.
  • Here we have defined only the states that are relevant to the View. Since mines are not displayed during the game, the corresponding state is not provided for in the base set either. Let's define possible transitions from one cell state to another using UML State Diagrams:

    Minesweeper Class Diagram

    Since we decided to build our application based on the MVC pattern, we will have three main classes: MinesweeperModel , MinesweeperView and MinesweeperController , as well as a helper class MinesweeperCell to store the state of the cell. Consider their class diagram:

    The organization of the architecture is quite simple. Here we have simply distributed the tasks to each class in accordance with the principles of the MVC pattern:

  • At the very bottom of the hierarchy is the MinesweeperCell game cell class. It stores the position of the cell, determined by the row row and the column column of the playing field; one of the state states that we described in the previous subsection; information about the presence of mines in the cell (mined) and the counter of mines in neighboring cells counter . In addition, it has two methods: nextMark() to cycle through the right-click mark states, and open() , which handles the left-click event;
  • A little higher is the MinesweeperModel Model class. It is the MinesweeperCell game cell container. Its first startGame() method prepares the playing field for the start of the game. The isWin() method checks the playing field for a win state and returns true if the player has won, otherwise it returns false. A similar method isGameOver() is intended for checking the loss. The openCell() and nextCellMark() methods just delegate actions to the corresponding cells on the playing field, and the getCell() method returns the requested game cell;
  • The MinesweeperView View class includes the following methods: syncWithModel() - provides redrawing of the View to display the current state of the playing field in the Model; getGameSettings() - returns the game settings set by the user; createBoard() - creates a playing field based on Model data; showWinMessage() and showGameOverMessage() respectively display win and loss messages;
  • And finally the MinesweeperController Controller class. It defines only three methods for each possible action of the player: startNewGame() is responsible for clicking on the "New Game" button in the View interface; onLeftClick() and onRightClick() handle clicks on game cells with the left and right mouse buttons, respectively.
  • Implementation of Minesweeper game in Python

    It's time to start implementing our project. We will choose Python as the development language. Then we will write the View class based on the tkinter module.

    But let's start with the Model.

    MinsweeperModel

    The implementation of the model in Python looks like this:

    MIN_ROW_COUNT = 5 MAX_ROW_COUNT = 30 MIN_COLUMN_COUNT = 5 MAX_COLUMN_COUNT = 30 MIN_MINE_COUNT = 1 MAX_MINE_COUNT = 800 class MinesweeperCell: # Possible game cell states: # closed - closed # opened - open # flagged - flagged # questioned - flagged with a question mark def __init__(self , row, column): self.row = row self.column = column self.state = "closed" self.mined = False self.counter = 0 markSequence = [ "closed", "flagged", "questioned" ] def nextMark (self): if self.state in self.markSequence: stateIndex = self.markSequence.index(self.state) self.state = self.markSequence[ (stateIndex + 1) % len(self.markSequence) ] def open(self ): if self.state != "flagged": self.state = "opened" class MinesweeperModel: def __init__(self): self.startGame() def startGame(self, rowCount = 15, columnCount = 15, mineCount = 15) : if rowCount in range(MIN_ROW_COUNT, MAX_ROW_COUNT + 1): self.rowCount = rowCount if columnCount in range(MIN_COLUMN_COUNT, MAX_COLUMN_COUNT + 1): self.columnCount = columnCount if mineCount< self.rowCount * self.columnCount: if mineCount in range(MIN_MINE_COUNT, MAX_MINE_COUNT + 1): self.mineCount = mineCount else: self.mineCount = self.rowCount * self.columnCount - 1 self.firstStep = True self.gameOver = False self.cellsTable = for row in range(self.rowCount): cellsRow = for column in range(self.columnCount): cellsRow.append(MinesweeperCell(row, column)) self.cellsTable.append(cellsRow) def getCell(self, row, column): if row < 0 or column < 0 or self.rowCount

    Related Articles