Home

Introduction

Welcome to the webmvcframework WIKI.
The package webmvcframework, with the acronym of WebMVC, is a powerful object-oriented PHP framework, suitable as a web design and development tool.
You can use WebMVC to effectively create small and medium-sized web projects regarding data-intensive web applications, sites, mobile web apps and web APIs.

WebMVC concepts

WebMVC defines:

  • A set of assumptions, models, and practices constituting a way for building and compounding software application
  • A software system that is intended to be instantiated for:
    • defining the architecture for a family of sub-systems and providing the basic building blocks to create them
    • defining the places where adaptations for specific functionality should be made and run

WebMVC features

WebMvcFramework provides a comprehensive set of features that emphasize the principles of the Model-View-Controller (MVC) architecture, Separation of Concerns (SOC), and Hierarchical Model-View-Controller (HMVC). Here's a summary of the key features:

  1. MVC Architecture:

    • WebMVC follows the Model-View-Controller architecture for organizing applications, separating concerns related to business logic, user interface, and processing responsibilities.
  2. Subsystem Organization:

    • The framework allows for organizing applications into subsystems, which can be beneficial in managing the complexity of larger projects.
  3. HMVC Support:

    • WebMVC extends the basic MVC architecture by providing Hierarchical and Composition capabilities (HMVC). This approach facilitates the development of complex web pages by dividing them into smaller and simpler MVC sections.
  4. Technology Unmixing:

    • WebMVC aims to avoid mixing client-side programming languages (e.g., HTML, CSS, JavaScript) with server-side programming languages (PHP, SQL). This separation helps in collaboration among individuals with different skills required for building complex projects.
  5. Standard Web Technologies:

    • The framework utilizes standard web technologies without introducing new syntax in client-side and server-side programming languages.
  6. ORM Code Generation:

    • WebMVC offers automatic generation of Object-Relational Mapping (ORM) code by reverse engineering a given MySQL database schema.
  7. Ready-to-Run Components:

    • The framework includes a set of pre-built and customizable software components that address common challenges in web development.
  8. Facilities for Rapid Development:

    • WebMVC provides facilities and pre-built solutions for the rapid development of recurring functionalities in web applications, such as internationalization, SEO-friendly URLs, authentication, user management, and role-based access control.

This feature set suggests that WebMVC aims to provide developers with tools and conventions to streamline the development of web applications while adhering to established architectural principles.

Why WebMVC

The outlined principles and guidelines for developing WebMVC provide a clear insight into the philosophy and goals of the framework. Let's break down each of these principles:

  1. Object-Oriented Programming (OOP):

    • WebMVC is designed to treat web pages as classes, allowing for the application of fundamental OOP principles such as extension, override, reuse, and composition. This approach aims to make the development process more modular and maintainable.
  2. Avoid Mixing of Programming Languages:

    • The framework emphasizes the decoupling of server-side technologies from client-side ones. This separation is intended to prevent the mixing of languages, making it easier to manage and understand code related to GUI design without intertwining it with server-side logic.
  3. Decomposition:

    • WebMVC supports the simultaneous decomposition of an application and web pages using various engineering principles, including MVC, sub-systems, internationalization, page contents, and access roles. This allows for a structured and organized development process.
  4. Component-Based Development:

    • The framework promotes a component-based development approach, offering reusable, customizable, and useful server-side components. These components are designed as MVC parts, streamlining the implementation of recurring patterns in data-intensive web applications.
  5. Naming Convention Over Configuration:

    • WebMVC adopts a naming convention over configuration approach. This involves using an OOP-like, simple, and intuitive naming notation for logical representations of MVC classes and sub-systems. It also simplifies the instantiation of controllers by using HTTP requests without the need for manual configuration of routing.
  6. Internationalization Support:

    • The framework provides a simple and useful mechanism for applying language translations in a software application. This ensures that the application can easily support multiple languages.
  7. Tools-Aided Framework:

    • WebMVC includes a set of software tools to facilitate prototyping and automatic code generation. These tools are intended to streamline the process of building web pages and interacting with MySQL, providing developers with aids for efficient development.

These principles collectively aim to create a framework that not only adheres to sound software engineering practices but also provides developers with a toolset and conventions that simplify and enhance the development of web applications.

Summary

By putting all these guidelines and principles of WebMVC in order:

  • COMPONENT BASED DEVELOPMENT
  • AVOID MIXING OF PROGRAMMING LANGUAGES
  • NAMING CONVENTION OVER CONFIGURATION
  • DECOMPOSITION
  • OOP
  • INTERNATIONALIZATION SUPPORT
  • TOOLS AIDED FRAMEWORK

we could imagine using the acronym "CAN DO IT" to indicate the criteria by which a WebMVC application could be designed and developed.

Whats next

Follow this wiki to learn and to start using the framework!. Let's start with the Setup of WebMVC

Setup

Introduction

This section provides you with basic information for download, setup and configure WebMVC framework.

Software requirements

In order to develop using WebMVC framework you need:

  • Operating System: Linux, Mac or Windows
  • Server: Apache web server with mod_rewrite enabled
  • Database: MySQL (from 5.0 to the latest version)
  • Programming language: PHP (from 5.3 to the latest version) with DOM, MySQLi and GD extensions enabled

Skills requirements

The technical skills you need for developing with WebMVC are:

  • Good knowledge of PHP programming language and OOP
  • Basic knowledge of HTML, JavaScript, and CSS
  • Basic knowledge of MySQL database

Setup and configuration

To install the framework:

  1. Download it from Github
  2. Create a project folder in the root folder of your web server
  3. Copy all the directories downloaded from Github into the project folder
  4. Run sql/mrp.sql to install database sample tables. See the comments inside sql/mrp.sql to see default users and passwords
  5. Go into the project folder and modify the following lines of config/application.config.php according to your MySQL database and Apache Web Server configuration `php /**
    • MySQL User */ define("DBUSER","PUT_YOUR_USERNAME");

/**

  • MySQL Password */ define("DBPASSWORD","PUT_YOUR_PASSWORD");

/**

  • MySQL Database */ define("DBNAME","PUT_YOUR_DB_NAME");

/**

  • MySQL Port */ define('DBPORT', '3306');

/**

/**

  • Defines a constant for indicating the default login controller
  • You can use URL notation for specifying your custom Controller */ define("DEFAULT_LOGIN_PAGE", "common/login");

/**

  • Instructs framework if MySQL uses FULL_GROUP_BY SQL mode.
  • On MySQL > 5.7 FULL_GROUP_BY is enabled by default.
  • When MySQL FULL_GROUP_BY is ON set this constant to true
  • else false.
  • Note:
  • FULL_GROUP_BY ON reduces the Paginator component performances.
  • So is highly recommended to configure your MySQL and set it
  • to OFF. */ define ("MYSQL_MODE_FULL_GROUP_BY",false);

.......

/**

  • Defines a constant for a temporary folder */ define("APP_TEMP_PATH", "D:\gitmvc\temp"); `

Finally, make sure you set it properly Apache configuration for enabling mod_rewrite. Look here for details

Whats next

In the next section, we show you how to configure some advanced option. Alternatively, you can start understanding how WebMVC works.

Apache mod rewrite

Prerequisites

To follow this tutorial, you will need:

One Debian 10 server with Apache installed

Step 1 - Enabling mod_rewrite

First we need to activate mod_rewrite. It’s already installed, but it’s disabled on a default Apache installation. Use the a2enmod command to enable the module:

sudo a2enmod rewrite

This will activate the module or alert you that the module is already enabled. To put these changes into effect, restart Apache:

sudo systemctl restart apache2

mod_rewrite is now fully enabled.

Step 2 - Configure Apache2 Virtual Host

Before you start using mod_rewrite you’ll need to set up and secure a few more settings.

By default, Apache prohibits using rewrite (with is configured by the framework into .htaccess file) so first, you need to allow changes to the site configuration. Open the default Apache configuration file using nano or your favorite text editor:

sudo nano /etc/apache2/sites-available/000-default.conf

Inside that file, you will find a block starting on the first line. Inside of that block, add the following new block so your configuration file looks like the following. Make sure that all blocks are properly indented:

/etc/apache2/sites-available/000-default.conf

<VirtualHost *:80>
    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    . . .
</VirtualHost>

Save and close the file. If you used nano, do so by pressing CTRL+X, Y, then ENTER.
Note: Do the same changes for SSL sites.

Then, check your configuration:

sudo apache2ctl configtest

If there are no errors, restart Apache to put your changes into effect:

sudo systemctl restart apache2

Options

Introduction

You can configure WebMVC to use customized options such as setting the directories for storing your controllers, models, views, and template, configure security options and much more.

In this section, we will clarify these details to permit you to understand any configuration option. However, before starting, you need to know that, in most cases, you do not need to customize the execution environment of your application, the configuration we shown you on the previous page will be sufficient without the need of doing something else. In fact, all the other WebMVC configuration options can be used with their default values.

Basic organization of configurations files

// TODO

Understanding WebMVC

Introduction

This section provides you with a high-level overview of Model, View, and Controller of WebMVC and how it works to generate web pages. After reading this introduction, you should understand how the different parts of a WebMVC application are physically and logically organized and how they work together. You should also understand how the architecture of a WebMVC application differs from a standard PHP application.

Basic organization of a WebMVC application

Building a WebMVC application requires you to put your custom code into four special sub-folders of the server application root folder: models, views, templates, and controllers. As you might guess from the folder names, these folders physically organize the classes for implementing models, views, and controllers. In addition to structuring the physical organization of files, these folders also represent the logical organization of Namespaces in which each class will be encapsulated.
About the templates folder, the only thing you must know is that it will contain the HTML code that will be used by WebMVC PHP classes to generate the dynamic page of your application. The figure below shows the tree structure for folders:

Load and Dispatch by "Convention over Configuration"

When you build a traditional PHP application, there is a one-to-one correspondence between a URL and a PHP page. If you make an HTTP request like http://server/home.php from the server, then there had better be a page on disk named home.php. If the home.php file does not exist, you get an ugly 404 - Page Not Found error.

When building a WebMVC application, in contrast, there is no correspondence between the URL that you type into your browser's address bar and the files that you find in your application. In a WebMVC application, a URL corresponds to a controller action instead of a page on disk.

In a traditional PHP application, browser requests are mapped into pages. In a WebMVC application, in contrast, browser requests are mapped to controller actions. A PHP application is content-centric. A WebMVC application, in contrast, is application logic centric.

In WebMVC any browser request gets mapped to a controller action through a feature called Loading and Dispatching. This feature is built on architectural design named "Convention over Configuration" to automatically handle incoming HTTP requests and routes them to the corresponding controller actions. We will give you later more technical details about this feature in Controller Page,

Understanding Controller

A Controller is responsible for controlling the way that a user interacts with a WebMVC application. A controller contains the flow control logic for an application. A controller determines what response to send back to a user when a user makes a browser request.
Technically speaking a controller is just a concrete class of the abstract framework\Controller class. When you write a controller class it must be saved under the controllers folder of your web application to allow its instantiation.

Understanding View and Template

The View has the responsibility to organize and show data in graphical structures.
The development of a View in the web environment, unlike what happened for desktop applications, involves the use of different programming languages: some server-side, like PHP, and other client-side, like HTML, CSS, and JavaScript. In WebMVC these differences are used respectively by involving two distinct entities rather than just one as was expected by the MVC pattern, originally designed for desktop applications. These entities are the framework\View concrete class of the framework, and a common static HTML file, the Template that will contain the GUI design. Custom views and templates must respectively reside into views and templates folders. Separating HTML design contained into a template from the class View offers considerable advantages on several sides. We will discuss this in detail later.

Understanding Model

The Model handles the state of the application. The state is what your application is about. In WebMVC model is provided by the framework\Model concrete class that has the responsibility for data management of MySql. You can use an instance of this class or extend it with a custom class and save it under models folder.
The framework also provides you with a tool for automatically generating all the Model classes needed to manage all tables of a given MySQL schema.

Handling incoming HTTP requests

Now you know how WebMVC, as a result of an HTTP request, loads and runs a Controller, connected in turn with the View, Template, and Model. The following figure shows you how the framework handles incoming HTTP requests and processes data and operations.

Let's summarize the key points in the flow

  1. Incoming HTTP Request:

    • An HTTP request is delivered to the WebMVC Dispatcher.
  2. Using Dispatcher for Recognition of the Controller call:

    • The Dispatcher automatically recognizes the HTTP request as a call for Controller execution.
  3. Importing and using Controller:

    • The Dispatcher uses the Loader to use and load the appropriate Controller class.
  4. Controller Loading and Instantiation:

    • The Loader imports the Controller class and its dependencies.

    • The Dispatcher instantiates the appropriate Controller.

    • Possibility of Hierarchical MVC:

      • It's noted that the Controller might aggregate and manage the execution of one or more controllers, a feature known as "Hierarchical MVC." We will discuss it later, in this section
  5. Model Execution:

    • The Controller uses and runs the Model.
    • The Model may connect to MySQL to retrieve or store data.
  6. View Execution:

    • The Controller uses and runs the View.
    • The View reads the static design of the web page from an HTML Template.
    • The static design, along with data from the Model, is used to generate the dynamic web page.
  7. Controller Output to Dispatcher:

    • After loading and processing the Model and View, the Controller provides the output dynamically produced back to the Dispatcher.
  8. Dispatcher HTTP Response:

    • Finally, the Dispatcher sends back the output as an HTTP response.

This flow illustrates a typical request-response cycle in the WebMVC framework, where the Dispatcher plays a central role in managing the flow of control, and the Model, View, and Controller collaborate to handle different aspects of the request.

In summary, the WebMVC framework uses a Dispatcher to handle incoming HTTP requests. It recognizes the need for Controller execution, loads the appropriate Controller class, and manages the flow of control between the Model, View, and Controller to generate a response, which is then sent back to the client. This process follows the principles of the Model-View-Controller architecture, with additional support for hierarchical MVC and other features provided by the WebMVC framework.

Naming convention

WebMVC requires that you must mandatorily use PascalCase and camelCase notation for naming, respectively, classes and methods. Furthermore is strictly recommended (but optional) to use a unique name for all MVC parts. For example, if you are designing the home page of your site, you can use the name "Home" for all classes such as the Controller, View, Model, and the name 'home' for the Template, You must also use the name 'Home' for many other files, such as language translation files, which we will show you later. See the following figure:

From a conceptual point of view, this means that you can specify a unique name that identifies the MVC triad cooperation. In this example, we call it Home and it identifies a WebMVC assembly that will be generated at runtime for the aggregating of the MVC triad and the template.
Summarizing, a WebMVC assembly identifies an entity generated at runtime that will provide a primary service (typically a web page) and we can identify by a name. Then we can use this name for physically naming the MVC triad and template that cooperate together for producing the service. The name of a WebMVC assembly will match the Controller name of the triad and will be also used by the framework for providing to you an endpoint you will use for consuming the service.

Although we can use the same name for the different MVC classes triad, the unique identification of a single class name still will be possible because WebMVC uses the PHP namespaces for encapsulating each class; this convention allows avoiding naming conflicts and the proliferation of names in complex projects. We will provide more details about using namespaces when they will be used for managing subsystems for decomposing the system.

Insights WebMVC assembly

Generally, a WebMVC assembly, that inherits its name from Controller (alias root Controller), can furthermore aggregate together many more parts, rather than only its related Controller, Model View, and Template. In fact, it can assemble, into a hierarchical structure, many more parts like nested MVC triad and/or Components. All those nested elements may be also considered as child MVC assemblies. We will give you more technical details later about the hierarchy of MVC assemblies when introducing Content Decomposition and Components.

By looking at the figure above, in which we used a WebMVC Assembly name 'Home', see the positioning of the Home.php classes for the Controller, View, Model in the directory hierarchy of WebMVC. Also, see how we have been using the name 'home' for the template home.html.tpl by putting it under the 'templates' folder. By convention, we used the "_snake_case notation_" (by specifying the name using lowercase) for naming those files, like template ones, that doesn't contain any classes. Don't forget that, at the occurrence of composite words in the WebMVC assembly name, snake_case notation uses an underscore for separating words, eg. 'hello_world.html.tpl' for naming a Template related to a WebMVC assembly having the name HelloWorld. The file extension ".html.tpl" is mandatory when naming a Template.
Finally, we can use the endpoint http:/server/home for running the controller controllers\Home, or better for consuming the service provided by the Home WebMVC assembly. The name of the callable endpoint will be automatically provided to you by the framework. It will be represented in snake_notation and it will match exactly the Assembly name.

Note that the convention we discussed so far, of using a single name for all the main parts of MVC assembly, is not mandatory. If you prefer you can also decide to name classes and templates by using the traditional suffixes, eg. HomeController.php, HomeModel.php, HomeView.php and home_template.html.tpl

Summary

We exposed the basic concepts you need to understand before using WebMVC framework for developing an application. They are:

  • The roles of models, views, controllers (MVC) classes and of templates.
  • Files organization by using special folders: models, views, controllers, and templates for storing MVC classes and HTML templates.
  • PascalCase notation for naming classes
  • camelCase notation for naming their methods
  • snake_case notation for naming templates
  • snake_case notation for typing the endpoint (URL requests) and running controllers
  • At runtime MVC triad and template are managed in the form of an assembly. We can identify it with a name for using when naming files.

So you simply:

  • Code your MVC classes and the GUI HTML template.
  • Use PascalCase, camelCase notations when coding/saving classes and snake_case notation when saving the template.
  • We strongly suggest to you to identify WebMVC assembly name representing the service produced by the MVC triad and using it when naming files.
  • Store all files into their respective folders
  • Tu execute a Controller (or better to consume the service produced by the WebMVC assembly) type its name (endpoint) from the web browser by using the snake_case notation for the URL request.

What's next

After the explanation of the main architecture of WebMVC, and by understanding its basic structure we are now ready to start coding its first entity: the Controller.

Controller

Introduction

You are going to learn how fast is to start coding with the WebMVC framework.
We have already discussed the role of the Controller in the MVC design pattern here. As follow of that, for designing and implementing your first application you only need a basic knowledge of OOP programming for writing special classes: the Controllers. In fact, by writing a Controller, also equipped with some public methods, WebMVC will enable you to instantiate and execute it's logic, simply by typing special HTTP requests from your browser.

Let's start coding

Coding and running your first controller is extremely simple!
Just write and save the following file HelloWorld.php into the controllers folder:

<?php
namespace controllers;

use framework\Controller;

class HelloWorld extends Controller
{
    public function sayHello()
    {
        echo "Hello World";
    }
}

The figure below shows you the path for HelloWorld.php. Note that you must use the PascalCase notation for naming a controller.

Here's a breakdown of the code:

  • Namespace:

    • The controller is placed within the controllers namespace. Namespaces help organize classes and avoid naming conflicts.
  • Class Declaration:

    • The class is named HelloWorld and extends the Controller class provided by the WebMVC framework.
  • Method (sayHello):

    • The class contains a method named sayHello. This method will be executed when the corresponding action is triggered.
  • Action Logic:

    • The logic inside the sayHello method is simple: it echoes the string "Hello World."

To use this controller, you typically follow the URL structure that corresponds to the MVC pattern. For example, if your application is hosted at http://example.com, accessing http://example.com/HelloWorld/sayHello would trigger the sayHello method of the HelloWorld controller.

Make sure that the file is saved in the correct location, and the namespace corresponds to the folder structure. The WebMVC framework likely has conventions for organizing controllers and other components.

At this point, the only thing you have to understand is how to instantiate the HelloWorld Controller and execute its sayHello method. Just open your favorite web browser and type the following URL address:

http://localhost/hello_world/say_hello (Click to run)

You should see:

Hello World

Congratulations, you have just developed and executed your first application using the WebMVC framework!

Remember that this is a basic example, and in a real-world application, your controllers would handle more complex logic, interact with models, and generate views to render dynamic content.

Explanation: How coding and executing Controller

WebMVC requires you to code a custom Controller for implementing application logic. In general, a Controller has the responsibility to handle the logic and control flow of a software application. To do this you must create a PHP Class that extends the framework\Controller and save it under the controllers directory. That is what we did by providing HelloWorld controller. Later we ran it by requesting it from browser URL, that's mean we typed its name and its sayHellomethod by using a snake_case notation. This notation is very intuitive because it mirrors the PascalCase or camelCase notation that must be mandatory used when naming classes or methods. It simply consists of typing the HTTP request by specifying both the Controller and Method names you want to run, using lowercase and by separating names with a slash. Snake_case notation also requires an underscore for separating an eventual occurrence of composite names for the controller or for methods. In fact, we used:

hello_world -> for specifying HelloWorld Controller that must mandatory named by using the PascalCase notation

say_hello -> for specifying its sayHello method that must be mandatory named by using the camelCase notation

then we typed both controller and method names by separating them with a "/" (we assumed localhost is the application root):

http://localhost/hello_world/say_hello

That's it all.

Therefore, you don't need to configure the execution of a particular Controller, but you just use the URL notation proposed by WebMVC. This simplicity derives from the convention over configuration approach that the framework uses for object instantiation and method invocation in order to avoid tedious operations of configuration for running the controllers. The convention over configuration mechanism used by WebMVC is simple: as we just said before, you must mandatorily use the PascalCase and camelCase notation when naming, respectively, classes and methods.

Reference: Controller and object methods

As you can see in the example, for creating a Controller you must write a common PHP Class that uses and extends the abstract framework\Controller class. Then you must and save it into the directory controllers. By adding public methods to controllers\Home class, you are able to implement some application logic that can be executed by an HTTP request. The only skill you need right now is about the OOP programming. For this purpose, the next example will show you other concepts regarding the interaction between WebMVC and OOP programming. Specifically, they are about parameters and visibility of Controller methods.
Look and run the code below in which to the previous example we add:

  • a new public method that accepts one input parameter: sayHelloMessage($message)
  • a new protected method: cantSayHello():
<?php
namespace controllers;

use framework\Controller;

class HelloWorld extends Controller
{

    public function sayHello()
    {
        echo "Hello world";
    }

    public function sayHelloMessage($message)
    {
        echo "Hello $message";
    }

    protected function cantSayHello()
    {
        echo "This method cannot be called from Url";
    }
}

Now type the following address:

http://localhost/hello_world/say_hello_message/Mark (Click to run)

The output will be

Hello Mark

Also type:

http://localhost/hello_world/say_hello_message/John (Click to run)

The output now will be:

Hello John

As you can note we requested the execution of the method sayHelloMessage and for specifying a value Mark (or John) for its single parameter $message we simply typed its value into the URL. We also used a slash for separating the requested method name say_hello_message and the value for its parameter $message. This is the WebMVC convention for passing one, and also multiples, values to a method parameters through the browser URL. In other words, you must simply specify the values, corresponding to the parameters, into URL and separate them with slashes. But, take care of passing the exact numbers of values that a method requires as input parameters otherwise, an exception will be thrown. You further note that the requested method was defined as public. The reason is that only public methods can be executed.
In fact, if you try to type:

http://localhost/hello_world/say_hello_message/Mark/John
or also
http://localhost/hello_world/cant_say_hello

in both cases, you will obtain an exception. The first exception is because we passed wrong numbers of values, Mark and John, to the single parameter $message designed into the method sayHelloMessage while the second one regards the access denied that occur any time we try to call from the URL a protected method, like cantSayHello.

Summary

This page exposes to you how simple is to start coding with WebMVC in accordance with the OOP programming. Just design and write your application in terms of concrete Controller classes and public methods. Then WebMVC let you execute them as common HTTP requests. Technically speaking, it means that every valid HTTP request performed by a user will match the execution of an action managed by a controller. The figure below illustrates this behavior:

Whats next

In the next page, we expose how you can handle HTTP GET/POST requests.

Handling of HTTP Request Methods

Introduction

In the previous page, we saw how to pass values using methods and parameters of a controller. However, the Hypertext Transfer Protocol (HTTP) is designed to enable communications between clients and servers. HTTP works as a request-response protocol between a client and server. A web browser may be the client, and an application on a computer that hosts a website may be the server.

Example: A client (browser) submits an HTTP request to the server; then the server returns a response to the client. The response contains status information about the request and may also contain the requested content.

Two HTTP Request Methods: GET and POST

Two commonly used methods for a request-response between a client and server are GET and POST.

  • GET - Requests data from a specified resource
  • POST - Submits data to be processed to a specified resource

Both GET and POST methods are able to send to the server a query string containing name/value pairs that must be processed by a resource (that is a controller) residing on the server.

The GET Method

Note that the query string (name/value pairs) is sent in the URL of a GET request:

http:/localhost/hello_world.php?name=Mark&skill=Developer

Usually, HTTP GET requests are sent through the browser URL bar or by HTML links.

The POST Method

Note that the query string (name/value pairs) is sent in the HTTP message body of a POST request:

POST /hello_world.php HTTP/1.1
Host: localhost
name=Mark&skill=Developer

Usually, HTTP POST requests are submitted by using HTML forms.

Handling of GET and POST with WebMVC

PHP provides:

  • $_GET associative array to access all the sent information using the GET method.
  • $_POST associative array to access all the sent information using the POST method.

Inside a Controller, you can use both of these arrays for handling HTTP GET and/or POST requests.
In the following example, we added the method sayWhatYouGet to HelloWorld for showing you how using $_GET for handling HTTP GET requests:

<?php
namespace controllers;

use framework\Controller;

class HelloWorld extends Controller
{

    public function sayHello()
    {
        echo "Hello world";
    }

    public function sayHelloMessage($message)
    {
        echo "Hello $message";
    }

    /**
     * A simple method for showing of $_GET processing
     */
    public function sayWhatYouGet()
    {
        echo "You get the following requests:<br>";
           foreach($_GET as $get_variable => $value) {
               echo "$get_variable = $value <br>";
           }
    }

    protected function cantSayHello()
    {
        echo "This method cannot be called from Url";
    }
}

Now, to execute the sayWhatYouGetmethod in conjunction with an HTTP GET request containing some data, type:

http://localhost/hello_world/say_what_you_get?name=Mark&skill=Developer

You will get the following results:

You get the following requests:
name = Mark
skill = Developer

Handling HTTP POST by using $_POST array can be done in a similar way: you need to replace it instead of $_GET in the code above. Then, to execute the sayWhatYouGetmethod in conjunction with an HTTP POST you can use a tool like Linux CURL (for emulating a POST request of HTML form) and type:

curl --data "name=Mark&skill=Developer" -H "Content-Type: application/x-www-form-urlencoded" -X POST http://localhost/hello_world/say_what_you_get

Note: instead of using S_GET or $_POST in the code above you can also use the $_REQUEST, the PHP associative array that by default contains the contents of $_GET, $_POST (and also $_COOKIE).

Suggestions when using $_GET and $_POST

Be careful when using the values contained in $ _GET and $ _POST because they come from external users and may contain malicious data. You always must sanitize data from $_GET and $_POST before using it.
Sanitize your data depending on what it's being used for and, by using different functionalities provided by PHP:

  • If you are inserting data into the database then use mysql_real_escape_string() for quoting strings and typecasting for numbers would be the way to go (or ideally use "prepared statements"). Regarding database, WebMVC provides the autogeneration of special ORM classes equipped with sanitizing functionalities.

  • If you plan on outputting the data onto the webpage then we would recommend something like the PHP htmlspecialchars(). WebMVC provides a special function, setVar, that automatically includes this capability.

  • About sending emails or HTML forms, the following should suffice:
    filter_var($_POST['variable'], FILTER_SANITIZE_CONSTANT)
    In this case, WebMVC provides the Record Component that automatically includes this functionality.

All this does is basically "strip tags" and "encode" the special characters.
We just said that WebMVC provides some functionalities for automatically sanitize data in the proper way. We will discuss later these arguments when we will show you the View, ORM and the Record component.

Furthermore, there is no correct way to do blanket sanitation. What sanitation method you need depends on what is done to the data. Sanitize the data directly before it is used.

Summary

You learned how using HTTP GET and POST to passing values to the Controller. Depending on your needs, you are able to use both HTTP GET/POST request and HTTP request of a method/parameters of a Controller for handling some values to be processed.

Whats Next

In the next page, we expose some advantages deriving from the conjunction of OOP and WebMVC

Controller and OOP

Introduction

In OOP we build classes following the principle of the single responsibility that states that every module or class should have responsibility over a single part of the functionality provided by the software and that responsibility should be entirely encapsulated by the class.
In this section, you will learn how to use the OOP inheritance and method overriding to handle, generalize, reuse and extend a controller responsibility. Although all the following examples are focused on the Controller, all of the concepts inherent the OOP can also be applied to the development of the View and Model classes of WebMVC (which we will discuss later).

OOP inheritance and methods overriding

In the previous example, we used the OOP inheritance to take the advantages of extending the "abstract responsibilities" of framework\Controller class to "custom and specialized responsibilities" of the concrete controllers\HelloWorld class.
Now we will give you another example regarding another OOP feature: method overriding, but before you must know that:

Technically speaking, the main responsibility of a WebMVC Controller is to implement some application logic and to expose it through an interface to be called like a common HTTP request. Controller application logic has the main role to establish automatic cooperation among two external classes: View and Model we will discuss later but, right now, you must take into consideration an important aspect of WebMVC architecture: anytime you invoke from HTTP a controller execution, its default, and automatic behavior is to connect a View and a Model. Then WebMVC will show you the GUI design provided by the connected View.

To see in action this behavior, first reconsidering the previous controllers\HelloWord:

<?php
namespace controllers;

use framework\Controller;

class HelloWorld extends Controller
{
    public function sayHello()
    {
        echo "Hello World";
    }
}

But now, instead of typing http://localhost/hello_world/say_hello, like we made in the previous example, just invoking HelloWord without requesting any method execution. Type:

http://localhost/hello_world

By now we are requesting the execution of the HelloWorld standard behavior, supplied by its __construct(), and we will obtain an exception:

framework\exceptions\NotInitializedViewException

Are you surprised? That happens because, by typing http://localhost/hello_world, we invoke the controller\HelloWord standard behavior inherited from its parent: the __construct() method which is defined and built into the abstract framework\Controller class.
The parent __construct(), implements the following behavior: it uses a generic and null View when it does not have any reference to a custom instance of it. It does the same for the Model. But, when WebMVC tries to show the View it throws an exception because the design of a generic null View, by default, is not initialized. In the example, we have an uninitialized design because we are just echoing a message rather than printing an HTML GUI design.

Fortunately, by using the OOP method overriding we can build an extended version, we call it controllers\EchoController, of framework\Controller that will implement an "overridden" behavior with the purpose of avoiding the exception when we don't want use any GUI design.
Take a look at the following class:

<?php
namespace controllers;

use framework\Controller;

class EchoController extends Controller
{
    /**
     *  @override framework\Controller __construct()
     */
    public function __construct()
    {
        parent::__construct();
        $this->hookViewInitialization();
    }

    /**
     * A custom hook to initialize the design managed by the View.
     * @note: We use replaceTpl to initialize design to a space when it is Null
     */
    protected function hookViewInitialization()
    {
        $this->view->replaceTpl(" ");
    }
}

In this version, we override the standard parent behavior by building (and automatically calling) a hook to the View initialization, because we haven't the need of using a GUI design. You must don't take care, right now, of hookViewInitialization implementation. Simply you must consider that by calling it we are always able to initialize the View avoiding the exception.
So with the availability of controllers\EchoController, in witch a GUI design is always initialized, we can now refactor the previous controllers\HelloWorld in this way:

<?php
namespace controllers;

use controllers\EchoController;

class HelloWorld extends EchoController
{
    public function sayHello()
    {
        echo "Hello World";
    }
}

Now by making the request http://localhost/hello_world no exception will be thrown.
Finally, if you want to automatically show the echo message by using the previous request, override the __construct in this way:

<?php
namespace controllers;

use controllers\EchoController;

class HelloWorld extends EchoController
{
    /**
     *  @override controllers\EchoController __construct()
     */
    public function __construct()
    {
        parent::__construct();
        echo "Hello World";
    }

    /**
     * Note:
     * We don't need of sayHello() now because
     * we put an echo message in the constructor
     */
}

Summary

In this section, you learned how to apply OOP for extending and overriding a controller behavior. You also learned how you can use the __construct to automatically execute some code when instantiating a controller

What's next

In the next section, we will speak about interesting feature regarding automatic code execution of a controller: the autorun method.

Controller autorun method

Introduction

We learned how, by using the __construct method of the controller, we can implement some actions to be automatically executed. However, having in some circumstances the goal of managing complex web pages, we could involve mixing, when writing the code for the constructor, of some generics actions together with more specialized ones. The autorun method gives you the ability to separate, outside the controller constructor, the coding of those specialized actions that must be automatically executed. As result of this separation, you will obtain a smart reuse of a controller any times you will need to extend it to a more specialized behavior.

Usage example of autorun

First, consider the following and simple extension of the previous controllers\EchoController, we call it controllers\WebPageProductsList and we use it for simulating the behavior that must be executed automatically inside a web page having the responsibility of showing a list of products for a hypothetical e-commerce application.

<?php
namespace controllers;

use controllers\EchoController;

class WebPageProductsList extends EchoController
{
    /*
     * @override controllers\EchoController __construct()
     */
    public function __construct()
    {
        echo "Building page navigation bar <br>";
        echo "Building page status bar <br>";
        echo "Building page credits information <br>";
        echo "Building products list <br>";
        parent::__construct();
    }
}

We are simulating a page having a specialized responsibility to show a section containing products list. The page also shows many sections that should be shared across the different pages of the e-commerce application. These sections are the site navigation bar, the status bar, and credit information. Finally, we emulated the output of the page that shows the products list just by printing one message respectively for each section.
Now suppose we need a new web page, we call it controllers\WebPageProductDetail, that must show the same generics section regarding navigation, credits information and status bar but it must show a product detail rather than the products list.
We can code controllers\WebPageProductDetail in this way:

<?php
namespace controllers;

use controllers\EchoController;

class WebPageProductDetail extends EchoController
{
    /*
     * @override controllers\EchoController __construct()
     */
    public function __construct()
    {
        echo "Building page navigation bar <br>";
        echo "Building page status bar <br>";
        echo "Building page credits information <br>";
        echo "Building product detail<br>";
        parent::__construct();
    }
}

Now try to type the following HTTP requests to show the results:

http://localhost/web_page_products_list
and
http://localhost/web_page_product_detail

You will get the following results:

Building page navigation bar
Building page status bar
Building page credits information
Building products list

and

Building page navigation bar
Building page status bar
Building page credits information
Building product detail

Although these implementations are both correct, it is certainly not the most efficient way to write their code. In fact, all the two classes contain parts of repeated code but necessary to carry out the (shared) generic actions for computing the navigation bar, the credits information, and the status bar.
By using autorun, in conjunction with OOP extension, you can code better versions of previous controllers. See the code below:

<?php
namespace controllers;

use controllers\EchoController;

class WebPageProductsList extends EchoController
{
    /*
     * @override controllers\EchoController __construct()
     */
    public function __construct()
    {
        echo "Building page navigation bar <br>";
        echo "Building page status bar <br>";
        echo "Building page credits information <br>";
        parent::__construct();
    }

    /**
     * @override framework\Controller autorun()
     */
    protected function autorun($parameters = null)
    {
        echo "Building products list <br>";
    }
}
<?php
namespace controllers;

use controllers\WebPageProductsList;

class WebPageProductDetail extends WebPageProductsList
{
    /**
     * @override controllers\WebPageProductsList autorun()
     */
    protected function autorun($parameters = null)
    {
        echo "Building product detail <br>";
    }
}

In this version, we coded a parent plus a child controller. Then we shared the generic actions built inside the parent constructor leaving to the autorun method the responsibility for implementing, respectively, the different and specialized behavior of each controller.
So by running these two controllers, you will get the same result as above because what will happen will be:

  • by running controllers\WebPageProductsList
    • its __construct() will be executed and the three messages about navigation, status, and credit information will be shown
    • then, its autorun() method will be automatically invoked by showing you the message about products list
  • by running controllers\WebPageProductDetail
    • the parent __construct() will be executed and the three messages about navigation, status, and credit information will be shown
    • then, its autorun() method will be automatically invoked by showing you the message about product detail

Insights: using autorun

Finally, you understand the Controller and some advantages of using OOP and of the method autorun. With this premise, now we want to show you how you can build HelloWorld in a more efficient way. First of all, we customizing the generic behavior of the abstract framework\Controller by writing a reusable abstract Controller we can use any time having the need of printing out a simple text message. We call it controllers\EchoWebPage;

<?php
namespace controllers;

use framework\Controller;

abstract class EchoWebPage extends Controller
{
    /**
     * @override framework\Controller __construct().
     */
    public function __construct()
    {
        parent::__construct();
        $this->view->replaceTpl(" ");
    }
}

Now, by having an abstract Controller that implements an "echo behavior" without throwing any exceptions and, also, having a Hook (provided by autorun) to a logic that can be automatically executed, we can write the final and well engineered release of HelloWorld:

<?php
namespace controllers;

class HelloWorld extends EchoWebPage
{
    /**
     * @override  autorun()
     */
    protected function autorun($parameters = null)
    {
        echo "Hello World";
    }
}

Note that in the above controllers\HelloWorld we are extending and concretizing the abstract controllers\EchoWebPage also by omitting the initial instruction 'use controllers\EchoWebPage'. That's because both controllers\HelloWorld and contollers\EchoWebPage are encapsulated in the same controllers namespace (we will discuss its details later).

By typing

http://localhost/hello_world

You (still) will obtain

Hello World

Summary

By using the controller autorun you have the ability to automatically execute some custom code, even located outside its constructor. Then, you can potentially extend a controller just by writing or overriding the autorun code for extending a parent behavior that must be executed automatically without the need to override the constructor.

In others terms, you can think about autorun like an event that is generated after a controller object creation. So you can hook this event in any child controller, to extend the main responsibility of a parent one without the need to override the constructor.

From Wikipedia

In computer programming, the term hooking covers a range of techniques used to alter or augment the behavior of software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook.

Whats next

Until now we have focused on the WebMVC Controller. To clearly showing its basic features, we have introduced extended versions of it, called EchoController and EchoWebPage, that we built with different design principles. We designed both of them as sub-classes to modify the Controller basic behavior, that is, the interaction with the View and Model. Starting from now we will introduce the use of the View, an essential WebMVC class we need for generating web pages rather than simple text messages.

View

Introduction

We are now introducing the concrete class frameworks\View.php which is the WebMVC entity that provides you all the necessary methods to interact with the HTML pages. You will learn to use it as an object instance or, when occurring the need to produce complex and dynamic web pages, by extending it with custom classes designed for your needs.

Using the View

In MVC Design pattern the View layer has the prior responsibility to manage and show data in graphical structures like HTML page. With WebMVC, this responsibility is managed by a View that operates together with a Template file containing the HTML static design. To use a View you must:

  • Creating a custom Template file containing the static HTML design of the page you want to be shown
  • Using the concrete framework\View class provided by WebMVC to create a View object for managing the custom template
  • Creating a custom Controller to handle the View object

So, when you having the need to show an HTML page rather than a simple message (like we made with the EchoController) you must create a template file containing the HTML. Later you also need to manage the template by creating an object of the concrete class "frameworks\View.php". You also need to build a controller in a way to enabling the execution of the view object previously introduced to produce the output. This because the controller is the only MVC entity that you are able to instantiate and run (by typing an HTTP request).

Pratically:

Create the template templates\home.html.tpl containing the HTML of the web page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Site home page</title>
</head>
<body>
<p>Welcome to the site Home Page</p>
</body>
</html>

Then create the Hello controller controllers\Home.php as follow (pay attention to the comments):

namespace controllers;

use framework\Controller;
use framework\View;

class Home extends Controller
{
    /**
     * Home constructor.
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        /**
         * A reference to the file: templates/home.html.tpl
         * @note Do not to specify the file extension ".html.tpl".
         */
        $tplName = "home";


        /**
         * Set the view with a new object of type framework\View.
         * @note: We create the View object by using the template reference.
         */
        $this->view = new View($tplName);

        /**
         *  To starting up the standard WebMVC behavior of acting
         *  cooperation between Controller and the View just
         *  invoke the parent constructor by passing the current view
         */

        parent::__construct($this->view);
    }

}

The following figure shows the WebMVC directory tree with files location:

Finally, run the controller by typing the following address into your web browser:

http://localhost/home

You should see the page:

Explanation: Controller, View and Template cooperation

We create an HTML static content into a file and save it in the directory templates. The file must have a .tpl extension in order to be accepted by WebMVC. Note that the template file must be written only by using client-side technologies like HTML, JavaScript or CSS programming languages. This is a constraint specially designed in WebMVC to avoid mixing between server-side, like PHP, and client-side technologies within a single source code file.
Then, in order to manage and render out the static HTML design of templates\home.html.tpl, WebMVC let you use the concrete framework\View class by creating an object of this type. By convention, we specified the template name in lowercase (see naming convention specifications we previously discussed). Finally, as shown in the code, you can automatically use the view object inside controllers\Home.php by overriding the __construct of the abstract framework\Controller. This practice gives you the ability to automatically produce the output simply by instantiating form HTTP the controller without the need of invoking any of its methods.

Insights: Best practices of using OOP and autorun for extending the GUI design

The purpose of this section is to give you another example regarding the advantages deriving from using OOP and autorun. Supposing we want to have a "Bootstrap" mobile version of the previous page. We can do this job simply by extending the controllers\Home.php by creating controllers\HomeBootstrap.php in which we quickly hook the autorun by loading a different GUI template appositely designed for mobile devices.
See the code below:

namespace controllers;

use controllers\Home;

class HomeBootstrap extends Home
{
    /**
     * @override autorun($parameters = null)
     */
    protected function autorun($parameters = null)
    {
        /**
         * Replaces the GUI template of the parent with a Bootstrap
         * mobile template coded into templates/home.bootstrap.html.tpl
         * We used loadCustomTemplate method provided by framework\View
         */
        $this->view->loadCustomTemplate("templates/home.bootstrap");
    }
}

Now we use Bootstrap for the mobile template of GUI. Note that it is ineffective on the server-side code developed so far thanks to the features of WebMVC that isolate client-side technologies in external template files

<!DOCTYPE html>
<html>
<head>
    <title>Site Home Page</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Bootstrap core CSS -->
    <link href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css"
          rel="stylesheet" media="screen"
    >
</head>
<body>
<div class="jumbotron text-center">
    <h1>Welcome to the site Home Page</h1>
</div>
<!-- Bootstrap and jQuery core JS -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>
</body>
</html>

Now run by typing:

http://localhost/home_bootstrap

You should see the bootstrap mobile version of the homepage

Summary

This example shows you how WebMVC, by separating GUI Design, View and Controller and by managing their cooperation realizes an efficient implementation of the "Separation of Concern". We will discuss here in depth about this benefit. Right now consider only the advantage deriving from avoid to mix programming languages into a single source code when you need to show the static content of a web page rather than simple string messages.
We also focused on how OOP and even more the controller autorun behavior give you effective ways to specialize your code. The figure below shows the View usage

Whats next

In the next example, we expose how to manage dynamic content. For dynamic content, we define a content inside a web page that is evaluated and generated at runtime, rather than static content we can design at development time inside a template. For this purpose, we can adopt a better practice to using and instantiating the View.

Handling placeholders

Introduction

Consider the common situation where you have to manage and show data dynamically on the web pages. You just learned that showing output is the main role of the View. This is still valid also when data to show are dynamical. For this purpose, in this paragraph, we expose a solution for handling dynamic web page by a using static Template, View, and Controller

Handling placeholders by using Template, View, and Controller

Look again at the following templates\home.html.tpl, we shown it on the previous page. You can note we have placed the static placeholder {UserName}. Now supposing we want to assign to it a value, eg. 'Mark' but the assignment must be dynamically provided at run-time.

<!DOCTYPE html>
<html>
  <head>
    <title>Site home page</title>
  </head>
  <body>
    <p>Welcome to the site Home Page</p>
    <br />
    <p>Welcome {UserName}</p>
  </body>
</html>

To do this job we procede by creating the following View class we call views\Home.php, which extends framework\View:

<?php
namespace views;
use framework\View;

/**
 * Class views\Home
 * Handles the treatment of the placeholder designed
 * into templates\home.html.tpl and provides the dynamic page.
 */
class Home extends View
{
    /* Contructor.
     * Override framework\View __construct() to
     * use templates\home.html.tpl as design
     */
    public function __construct($tplName = null)
    {
        $tplName = "home";
        parent::__construct($tplName);
    }

    /* Custom method designed to manage the dynamic content
     * of the placeholder {UserName}.
     *
     * @param string $name The value to assign to {UserName}
     */
    public function setUserName($name)
    {
        // setVar is a method inherited from framework\View
        $this->setVar("UserName",$name);
    }

}

We are using the method setVar() provided by framework\View to replace a value to placeholder {UserName}.

Note that, unlike the instance of the abstract framework\View we used to show the static content in the previous example, we are now creating a custom class, views\Home.php, that extends framework\View. The reasons we make so are:

  • Unlike a template that cloth with a static behavior, one containing placeholders or even blocks, assumes a dynamic and specialized behavior. Consequently, it is better to manage a dynamic design by creating a custom View and by using an instance of it to provide the expected dynamic content rather than using an instance of framework\View.

Generally, a custom View should be appositely written anytime you need the treatment of the dynamism of Placeholders and Blocks coded into a Template.

  • The framework\View provides only abstract behaviors like: showing a static design, replacing a value to a placeholder, and repeating n times the content of a block. It does not know anything about a custom logic necessary for providing a dynamic web page. We need to build a custom View and one or more methods for this purpose, in which we can use all the (abstract) functionalities provided by its parent framework\View. Note that it's what we have done with setUserName($name). You can see we used setVar() that is provided by the parent framework\View.

  • By implementing a custom View, we are respecting the SOC principle. Note that the custom views\Home view we used is the only entity enabled to manage the specialized behavior we expected to obtain from the placeholders or blocks of the custom templates\home.html.tpl template. If we had chosen to use, inside the Controller, an instance of framework\View rather than wrote views\Home for handling the placeholder, probably we had coupled altogether different responsibilities and concerns inside the Controller.

Then, we modify the controller controllers\Home.php as follow to enable it to communicate with views\Home.php (pay attention to the comments):

namespace controllers;

use framework\Controller;
use views\Home as HomeView;

/**
 * Class controllers\Home
 * Handles home HTTP request.
 * Provides home web page
 */
class Home extends Controller
{
    /**
     * Home constructor.
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        /**
         * A reference to the file: templates/home.html.tpl
         * @note Do not to specify the file extension ".html.tpl".
         */
        $tplName = "home";


        /**
         * Set the view with a new object of type views\Home.
         * @note: We create the views\Home object by using the
         *        template reference.
         */
        $this->view = new HomeView($tplName);

        /**
         *  To starting up the standard WebMVC behavior of acting
         *  cooperation between Controller and the View just
         *  invoke the parent constructor by passing the current view
         */
        parent::__construct($this->view);

        /**
         * By default, the controller behavior will be
         * that to greet a Guest
         */
        $this->view->setUserName("Guest");

    }

    /**
     * Handles home/set_user_name/(value) HTTP request.
     * Provides greeting to the given value.
     *
     * @param string $user The value to be greet
     * @use views\Home->setUserName($name)
     */
    public function userToGreet($user) {
        $this->view->setUserName($user);
        $this->render();
    }

}

The following figure shows the WebMVC directory tree with files location:

Note we used 'Home' name for naming Controller, View, and 'home' for Template. We talked about this on the naming convention page page.

Finally, run the controller by typing the following address into your web browser:

http://localhost/home/user_to_greet/Mark

You should see the page:

You can also run the controller without calling its userToGreet() method. Type:

http://localhost/home/

Now, you should see:

Insight: General considerations

Although this example is very simple, there are still many feasible considerations to do:

What setVar() exactly does ?

The method setVar($placeholder, $value) provided by framework\View find a given placeholder in the active template and replace it with a given value. Bear in mind that:

  • If many occurrences of placeholders (like {UsenName}) are designed in a template, then they will be all replaced in one single setVar() call.
  • You must call a setVar() method after the view has been initialized in the controller.
  • You must not call twice the setVar() method by giving it, each time, the same placeholder.This because after the first time the placeholder will be replaced with a value, then, at the second call of setVar(), no placeholder will be found and an exception will be thrown.
  • It provides some useful actions. Follow the method reference extracted from framework\View source code:
    /**
     * Replaces all occurrences of the given placeholder with a given value.
     * If the placeholder is contained into a (previously opened) block
     * the replacement is made against the opened block.
     *
     * You can also specify if the given placeholder is enclosed in braces,
     * if you need to purify the given value against XSS and CHARSET for the given value
     *
     * @param string $placeholder  The placeholder to find
     * @param string $value        The value for replacing to founded placeholder
     * @param bool|true $useBraces If true (default) the placeholder name contain braces {}
     * @param bool $xssPurify      If true (default=false) purify the value against XSS
     * @param string $charset      Charset string for the give value. Default (UTF8) is the CHARSET
     *                             constant value defined into config\application.config.php
     *
     * @throws VariableNotFoundException.  If variable/placeholder was not found
     * @throws NotInitializedViewException If template was not loaded
     */
    public function setVar($placeholder, $value, $useBraces=true, $xssPurify=false, $charset = CHARSET)

What about the use of render()?

Although you can invoke the render() method from the Controller, in action this is a service implemented by the View for showing the GUI to the screen.
When you are writing a public method of a Controller, you need to manually invoke method render() anytime you want to output the GUI to the screen. Differently, you will not need to invoke it when you are writing a Controller constructor. The reason is that WebMVC, by default, when you are invoking a Controller without calling any of its methods, automatically executes render() for you. See the previous controller\Home source and you will find that we invoke render() inside userToGreet() but never in __construct();

Summary

In this section, we speak about the need of creating a View with the purpose of handling the placeholders and of generating dynamic content.

Whats next

In the next page, we will introduce the dynamic Blocks handling for generating dynamic pages containing repeated sections

Handling blocks

Introduction

We can extend the idea of dynamic substitution of a placeholder with some value to the case where we have to handle a block of content and we need to perform some actions on it. For this purpose, WebMVC uses the concept of Block.

Handling blocks by using Template, View, and Controller

A Block is nothing more than a piece of code (typically HTML but it can be also Javascript or CSS inside the HTML) delimited by two comments for marking its beginning and ending limit. The content inside a block is usually subject to an action in order to generate a dynamic page in the browser. The block management is a useful feature of WebMVC for computing dynamic actions like:

Repetitions and/or dynamic rendering of the content

Supposing we want to show a list of users rather than a single one like we previously done in the controllers\Home. On the section about Dynamic Content, we introduced and coded the following templates\users_manager.html.tpl, by using a block named Users suitable for this purpose.

<!DOCTYPE html>
<html>
<head>
   <title>Users list</title>
</head>
<body>
   <h1>Users list</h1>
   <table>
      <thead>
         <tr>
            <th>User</th>
            <th>Email</th>
         <tr>
      </thead>
      <tbody>
         <!-- BEGIN Users -->
         <tr>
            <td>{UserName}</td>
            <td>{UserEmail}</td>
         </tr>
        <!-- END Users -->
      </tbody>
    </table>
</body>
</html>

As you can guess from the HTML code, the expected dynamic behavior of the web page will be to show a table containing some users. For each user, it will show the username and email.

This means that the HTML contained in the Users block can be considered as a Template pattern that must be dynamical repeated N times, and valued each time with different values representing a user.

In the HTML code, two comments have been added, and they together wrap the block of code that will be dynamically replaced many times in the HTML file. These comments mark the BEGIN and the END of a block of name Users. This means that WebMVC will be able to recognize the block.

Now, we can write the code of views\UsersManager and controllers\UsersManager in order to show a table of users.

First, we code views\UsersManager and save it as views\UsersManager.php

<?php

namespace views;

use framework\View;

class UsersManager extends View
{
    /**
     * @override framework\View __construct()
     */
    public function __construct($tplName = null)
    {
        if (empty($tplName)) {
            $tplName = "/users_manager";
        }
        parent::__construct($tplName);
    }

    /**
     * Shows the given $user in the block Users
     * of tempalates\users_manager.html.tpl
     *
     * @param array $users Array of users in the format
     *                      array(array('Username'=>,'UserEmai'=>''))
     */
    public function setUsersBlock($users)
    {
        $this->openBlock("Users");
        foreach ($users as $user) {
            $this->setVar("UserName", $user["UserName"]);
            $this->setVar("UserEmail", $user["UserEmail"]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }
}

In the view we have created a method setUsersBlock that uses the block Users and processes it with all the elements contained in the given $users array; specifically, we:

  • Invoking the openBlockmethod inherited from framework\View. This method, behind the scenes, do:

    • It recognizes the block Users
    • It creates a swap, initially empty, variable to store the dynamic content to be generated. We refer it as dynamic_content
    • It generates an internal template that contains the HTML eclosed in the Users block. We refer it as template_pattern
    • It predisposes each future calling of the setVar method to be restricted to the content of template_pattern.
  • Then, we start a PHP foreach loop on $users. For each element of $users:

    • We call the setVar method by valorizing placeholders {UserName} and {UserEmail} with the values referenced by the array keys UserName and UserEmail of the current element, $user, of $users
    • We call the parseCurrentBlock() which, behind the scenes do:
      • It concatenates the content of template_pattern, which now contain data rather than placeholders, to the content of dynamic_content. Note that, on each iteration, parseCurrentBlock() will add one user to dynamic_content. On the ending of forech,dynamic_content` will contain all users.
      • It regenerates the template_pattern for using it, once again, on the next iteration.
    • Then foreach evaluates if exist the next element in $users:
      • When the element exists, foreach backs to _i_ for processing the next element. This means that placeholders contained in the (just regenerated) template_pattern are, once again, ready to be valorized, but now with the values of the next element of $users
      • Else foreach loop exit
  • Finally, by calling the setBlock method, we stop the processing on the opened block and, behind the scenes, method replaces the text originally enclosed in the block Users with the text contained into its internal variable dynamic_content

Now we code the controllers\UsersManagerand save it as controllers\UsersManager.php

<?php

namespace controllers;

use framework\Controller;
use views\UsersManager as UsersManagerView;

class UsersManager extends Controller
{

    /**
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        $this->view = new UsersManagerView();
        parent::__construct($this->view);
        $users = $this->getUsersData();
        $this->view->setUsersBlock($users);
    }

    /**
     * Provides users data
     *
     * @return array  Array of users in the
     *                format array(array('Username'=>,'UserEmai'=>''))
     */
    private function getUsersData()
    {
        $users = array(
            array('UserName' => 'Mark', 'UserEmail' => 'mark@email.com'),
            array('UserName' => 'Elen', 'UserEmail' => 'elen@email.com'),
            array('UserName' => 'John', 'UserEmail' => 'john@email.com')
        );
        return $users;
    }
}

Now you can run the controller:

http://localhost/users_manager

...and you will get the users table (see the page summary below)

Summary

In this section, we show how handling the content repetition and valorization of a block. What we have done is:

1. By assuming the need for processing a multidimensional array of users:
 $users = array(
            array('UserName'=>'Mark', 'UserEmail'=>'mark@email.com'),
            array('UserName'=>'Elen', 'UserEmail'=>'elen@email.com'),
            array('UserName'=>'John', 'UserEmail'=>'john@email.com')
          );
2. We built a static GUI design templates\users_manager.html,tpl, containing a block and two placeholders:

Users list

User Email
<!-- BEGIN Users -->
{UserName} {UserEmail}
<!-- END Users -->

3. We built the custom class views\UsersManager for handling the template
4. We build the custom class controllers\UsersManager for handling the view, users as well as the HTTP requests
5. Finally, on requesting http//localhost/users_manager, we dynamically produced out:

Users list


User Email
Mark mark@email.com
Elen elen@email.com
John john@email.com

Whats Next

In the next page, we show you how hiding and replacing the content inside a block.

Hiding and replacing content

Introduction

In the previous page, you learned how to dynamically generate content by repeating and valorizing many times a static content of a block. Another interesting feature you can do with a block is the capability to hide or to replace its content.

Hiding and replacing the content of a block

By supposing that:

  1. We want to hide the users' list of the previous templates\users_manager.html.tpl, statically defined using a block named Users.
  2. We want to replace the users' table with a message "Sorry, you are not allowed to access users' list" for simulating a protected information.

For this purpose, we add a new block called ContenUsers in the template. See the template code below:

<!DOCTYPE html>
<html>
<head>
   <title>Users list</title>
</head>
<body>
   <h1>Users list</h1>
   <!-- BEGIN ContenUsers -->
   <table>
      <thead>
         <tr>
            <th>User</th>
            <th>Email</th>
         <tr>
      </thead>
      <tbody>
         <!-- BEGIN Users -->
         <tr>
            <td>{UserName}</td>
            <td>{UserEmail}</td>
         </tr>
        <!-- END Users -->
      </tbody>
    </table>
    <!-- END ContenUsers -->
</body>
</html>

The view views\UsersManager will remain the same of the previous example

<?php

namespace views;

use framework\View;

class UsersManager extends View
{
    /**
     * @override framework\View __construct()
     */
    public function __construct($tplName = null)
    {
        if (empty($tplName)) {
            $tplName = "/users_manager";
        }
        parent::__construct($tplName);
    }

    /**
     * Shows the given $user in the block Users
     * of tempalates\users_manager.html.tpl
     *
     * @param array $users Array of users in the format
     *                     array(array('Username'=>,'UserEmai'=>''))
     */
    public function setUsersBlock($users)
    {
        $this->openBlock("Users");
        foreach ($users as $user) {
            $this->setVar("UserName", $user["UserName"]);
            $this->setVar("UserEmail", $user["UserEmail"]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }
}

Now we need to update the controllers\UsersManager by adding two methods: hideUsers and disallowUsers. See the code:

<?php

namespace controllers;

use framework\Controller;
use views\UsersManager as UsersManagerView;

class UsersManager extends Controller
{

    /**
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        $this->view = new UsersManagerView();
        parent::__construct($this->view);
        $users = $this->getUsersData();
        $this->view->setUsersBlock($users);
    }

    /**
     * Provides users data
     *
     * @return array  Array of users in the
     *                format array(array('Username'=>,'UserEmai'=>''))
     */
    private function getUsersData()
    {
        $users = array(
            array('UserName' => 'Mark', 'UserEmail' => 'mark@email.com'),
            array('UserName' => 'Elen', 'UserEmail' => 'elen@email.com'),
            array('UserName' => 'John', 'UserEmail' => 'john@email.com')
        );
        return $users;
    }

    /**
     * Hides users list
     */
    public function hideUsers()
    {
        $this->hide("ContenUsers");
        $this->render();
    }

    /**
     * Disallows users list
     */
    public function disallowUsers()
    {
        $this->view->openBlock("ContenUsers");
        $this->view->setBlock("Sorry, you are not allowed to access users' list");
        $this->render();
    }
}

Now you can run the controller in three different ways:

1) By running http://localhost/users_manager you will get the following users list:

Users list


User Email
Mark mark@email.com
Elen elen@email.com
John john@email.com
2) By running http://localhost/users_manager/hide_users you will get the following output:

Users list

3) By running http://localhost/users_manager/disallow_users you will get the following output:

Users list

Sorry, you are not allowed to access users' list

Summary

In this page, you learned how to use the hide($blockName) method for hiding the content inside a block. You also learned another capability of the setBlock() method. In fact by calling setBlock($text) and by passing it a text message you can replace the content of an opened block with the given message.

Whats Next

In the next page, we speak about nesting blocks

Nesting of blocks

Introduction

There are two general purposes of using the nesting of blocks:

  1. Showing a set of different values when each value, in turn, contains a set of shared values
  2. Showing a set of different values when each value, in turn, contains a set of custom values

Nesting of blocks for showing a subset of shared values

We start by showing you the following templates\nested_blocks.html.tpl

<!DOCTYPE html>
<html>
<head>
    <title>Users list</title>
</head>
<body>
<h1>Users list</h1>
{CurrentAction}
<table border="1">
    <thead>
    <tr>
        <th>User</th>
        <th>Email</th>
        <th>Actions</th>
    <tr>
    </thead>
    <tbody>
    <!-- BEGIN Users -->
    <tr>
        <td>{UserName}</td>
        <td>{UserEmail}</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="{GLOBAL:SITEURL}/nested_blocks/do_action/{ActionName}/{UserEmail}">{ActionCaption}</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
    </tr>
    <!-- END Users -->
    </tbody>
</table>

</body>
</html>

As you can guess from the code above the purpose of this template is to representing the static structure of a table of users and to show, for each user, a set of links for performing some actions, e.g. send email, edit or delete the user data. Note that each user will share the same names of action links. Only links parameters will be different among differnt users.

To achieve this goal, we nested the block UserActions inside the main block Users we used to show users list. Then we designed inside UserActions the static representation of an unnumbered list of actions links. Also note that, like we have done for the user data, we coded only the static representation of one action link by representing a call to doAction() method of controller\NestedBlocks controller and by passing it two parameters: the action name and the user email, their respectively designed by using {ActionName} and {UserEmail} placeholders. Pay also attention that:

  • We used the placeholder {CurrentAction} for dynamically showing an information about the link clicked by the user
  • We used the global placeholder {GLOBAL:SITEURL} for getting the root URL of links that will be generated at runtime

The following picture shows you the output of the static design in which we highlighted with green the Users block and its inner block UserActions with red:

Figure 1

For dynamically producing the output we also code the following views\NestedBlocks. Pay attention to comments for understanding the purpose of its methods:

<?php

namespace views;

use framework\View;

class NestedBlocks extends View
{
    /**
     * @override framework\View __construct()
     */
    public function __construct($tplName = null)
    {
        if (empty($tplName)) {
            $tplName = "/nested_blocks";
        }
        parent::__construct($tplName);
    }

    /**
     * Shows the given $user in the block Users
     * of tempalates\nested_blocks.html.tpl
     *
     * @param array $users Array of users in the format:
     *                      array( array('Username'=>'','UserEmail'=>'') )
     */
    public function setUsersBlock($users)
    {
        $this->openBlock("Users");
        foreach ($users as $user) {
            $this->setVar("UserName", $user["UserName"]);
            $this->setVar("UserEmail", $user["UserEmail"]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }

    /**
     * Shows the given $actions in the block UserActions
     * of tempalates\nested_blocks.html.tpl
     *
     * @param array $actions Array of actions in the format:
     *                       array( array('ActionName'=>'','ActionCaption'=>'') )
     */
    public function setUserActionsBlock($actions)
    {
        $this->openBlock("UserActions");
        foreach ($actions as $action) {
            $this->setVar("ActionName", $action["ActionName"]);
            $this->setVar("ActionCaption", $action["ActionCaption"]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }
}

Finally, we code the controllers\NestedBlocks for providing the users' data, handling links actions, and for interacting with the previous view views\NestedBlocks:

<?php

namespace controllers;

use framework\Controller;
use views\NestedBlocks as NestedBlockView;

class NestedBlocks extends Controller
{
    /**
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        $this->view = new NestedBlockView();
        parent::__construct($this->view);

        $actions = $this->getUserActions();
        $this->view->setUserActionsBlock($actions);

        $users = $this->getUsersData();
        $this->view->setUsersBlock($users);
    }

    /**
     * Set the default behaviour when no actions is performed
     */
    protected function autorun($parameters = null)
    {
        $this->view->setVar("CurrentAction","Please, perform an action on user");
    }

    /**
     * Provides users data.
     *
     * @return array Array of users in the
     *               format array( array('Username'=>'','UserEmai'=>'') )
     */
    private function getUsersData()
    {
        $users = array(
            array('UserName' => 'Mark', 'UserEmail' => 'mark@email.com'),
            array('UserName' => 'Elen', 'UserEmail' => 'elen@email.com'),
            array('UserName' => 'John', 'UserEmail' => 'john@email.com')
        );
        return $users;
    }

    /**
     * Provides users actions.
     *
     * @return array Array of actions in the format:
     *               array( array('ActionName'=>'','ActionCaption'=>'') )
     */
    private function getUserActions()
    {
        $userActions = array(
            array("ActionName" => "email" ,"ActionCaption" => "Send email"),
            array("ActionName" => "edit"  ,"ActionCaption" => "Edit information"),
            array("ActionName" => "erase","ActionCaption" => "Delete user")
        );
        return $userActions;
    }

    /**
     * Performs the given action on a given user.
     *
     * @param string $actionName The action to performs
     * @param string $userEmail The user email on which to perform the action
     */
    public function doAction($actionName,$userEmail)
    {
        $currentAction = "Current action: $actionName on user $userEmail";
        $this->view->setVar("CurrentAction",$currentAction);
        $this->render();
    }
}

By running http://localost/nested_blocks you will obtain:

Figure 2

The following picture shows the result after clicking email link of the user Elen:

Figure 3

The following code shows you the HTML dynamically generated after running http://localost/nested_blocks of Figure 2. You can notice how the two blocks, Usersand UsersActions, were dynamically valued with the data provided by the two arrays $userActions, and $user of the controller controllers\NestedBlocks.

<!DOCTYPE html>
<html>
<head>
    <title>Users list</title>
</head>
<body>
<h1>Users list</h1>
<h3>Please, perform an action on user</h3>
<table border="1">
    <thead>
    <tr>
        <th>User</th>
        <th>Email</th>
        <th>Actions</th>
    <tr>
    </thead>
    <tbody>
    <!-- BEGIN Users -->
    <tr>
        <td>Mark</td>
        <td>mark@email.com</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/email/mark@email.com">Send email</a> </li>

                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/edit/mark@email.com">Edit information</a> </li>

                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/erase/mark@email.com">Delete user</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>

    </tr>

    <tr>
        <td>Elen</td>
        <td>elen@email.com</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/email/elen@email.com">Send email</a> </li>

                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/edit/elen@email.com">Edit information</a> </li>

                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/erase/elen@email.com">Delete user</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
    </tr>

    <tr>
        <td>John</td>
        <td>john@email.com</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/email/john@email.com">Send email</a> </li>

                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/edit/john@email.com">Edit information</a> </li>

                <li><a href="http:/localhost/mvccourse/nested_blocks/do_action/erase/john@email.com">Delete user</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
    </tr>
    <!-- END Users -->
    </tbody>
</table>

</body>
</html>

Summary

In this page, we showed how to code the nesting of blocks for producing a set of multiple values relating to parent data. The examples we showed assumed to share the same set of values but with different parents.

What's next

In the next page, we will show you a similar example but with the need of producing a set of custom values related to each parent data.

Insight on nesting of blocks

Introduction

In this page, we are going to expose a little variation of the previous example. We now consider the need of showing a set of custom values that must be assigned to each user we previously have shown.

Nesting of blocks for showing a subset of custom values

Now supposing the need of adding a new column to the previous table containing distinct roles for each user. So we need to rearrange the template templates\nested_blocks.htpl.tpl in the following way:

<!DOCTYPE html>
<html>
<head>
    <title>Users list</title>
</head>
<body>
<h1>Users list</h1>
<h3>{CurrentAction}</h3>
<table border="1">
    <thead>
    <tr>
        <th>User</th>
        <th>Email</th>
        <th>Actions</th>
        <th>Roles</th>
    <tr>
    </thead>
    <tbody>
    <!-- BEGIN Users -->
    <tr>
        <td>{UserName}</td>
        <td>{UserEmail}</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="{GLOBAL:SITEURL}/nested_blocks/do_action/{ActionName}/{UserEmail}">{ActionCaption}</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
        <td>
            <ul>
                <!-- BEGIN {UserEmail}Roles -->
                <li>{UserRoleName}</li>
                <!-- END {UserEmail}Roles -->
            </ul>
        </td>
    </tr>
    <!-- END Users -->
    </tbody>
</table>
</body>
</html>

In the table header, we simply added a new column containing the caption Role. In the body, we also added a new column but containing a new block. We assigned the name {UserEmail}Roles to this block with the purpose of dynamically generate many distinct block name, in the format 'UserEmailRoles', for how many users will be shown in the table.

You can better understand this result by running http://localhost/nested_blocks. You will obtain:

Now, if you will give a look inside the source code below, regarding the HTML that was dynamically generated, you will find these blocks named `mark@email.comRoles,elen@email.comRoles, andjohn@email.comRoles`:

<!DOCTYPE html>
<html>
<head>
    <title>Users list</title>
</head>
<body>
<h1>Users list</h1>
<h3>Please, perform an action on user</h3>
<table border="1">
    <thead>
    <tr>
        <th>User</th>
        <th>Email</th>
        <th>Actions</th>
        <th>Roles</th>
    <tr>
    </thead>
    <tbody>
    <!-- BEGIN Users -->
    <tr>
        <td>Mark</td>
        <td>mark@email.com</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/email/mark@email.com">Send email</a> </li>

                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/edit/mark@email.com">Edit information</a> </li>

                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/erase/mark@email.com">Delete user</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
        <td>
            <ul>
                <!-- BEGIN mark@email.comRoles -->
                <li>{UserRoleName}</li>
                <!-- END mark@email.comRoles -->
            </ul>
        </td>
    </tr>

    <tr>
        <td>Elen</td>
        <td>elen@email.com</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/email/elen@email.com">Send email</a> </li>

                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/edit/elen@email.com">Edit information</a> </li>

                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/erase/elen@email.com">Delete user</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
        <td>
            <ul>
                <!-- BEGIN elen@email.comRoles -->
                <li>{UserRoleName}</li>
                <!-- END elen@email.comRoles -->
            </ul>
        </td>
    </tr>

    <tr>
        <td>John</td>
        <td>john@email.com</td>
        <td>
            <ul>
                <!-- BEGIN UserActions -->
                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/email/john@email.com">Send email</a> </li>

                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/edit/john@email.com">Edit information</a> </li>

                <li><a href="https://server.local/mvccourse/nested_blocks/do_action/erase/john@email.com">Delete user</a> </li>
                <!-- END UserActions -->
            </ul>
        </td>
        <td>
            <ul>
                <!-- BEGIN john@email.comRoles -->
                <li>{UserRoleName}</li>
                <!-- END john@email.comRoles -->
            </ul>
        </td>
    </tr>
    <!-- END Users -->
    </tbody>
</table>
</body>
</html>

Now, by having distinct blocks regarding each user's roles, for dynamically assigning him the corresponding set of values, we need to rearrange the views\NetsedBlocks by adding the method setUsersRoles($usersRoles). See the code below and also pay attention to its comments:

<?php

namespace views;

use framework\View;

class NestedBlocks extends View
{
    /**
     * @override framework\View __construct()
     */
    public function __construct($tplName = null)
    {
        if (empty($tplName)) {
            $tplName = "/nested_blocks";
        }
        parent::__construct($tplName);
    }

    /**
     * Shows the given $user in the block Users
     * of tempalates\nested_blocks.html.tpl
     *
     * @param array $users Array of users in the format:
     *                      array( array('Username'=>'','UserEmail'=>'') )
     */
    public function setUsersBlock($users)
    {
        $this->openBlock("Users");
        foreach ($users as $user) {
            $this->setVar("UserName", $user["UserName"]);
            $this->setVar("UserEmail", $user["UserEmail"]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }

    /**
     * Shows the given $actions in the block UserActions
     * of tempalates\nested_blocks.html.tpl
     *
     * @param array $actions Array of actions in the format:
     *                       array( array('ActionName'=>'','ActionCaption'=>'') )
     */
    public function setUserActionsBlock($actions)
    {
        $this->openBlock("UserActions");
        foreach ($actions as $action) {
            $this->setVar("ActionName", $action["ActionName"]);
            $this->setVar("ActionCaption", $action["ActionCaption"]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }

    /**
     * Shows the given $usersRoles in the block {UserEmail}Roles
     * of tempalates\nested_blocks.html.tpl
     *
     * @param array $usersRoles Array of users roles  in the format:
     *                         array ( array("UserEmail"=>'',"UserRoles"=> array(r1,r2...,rn)))
     */
    public function setUsersRoles($usersRoles)
    {
        foreach ($usersRoles as $userRoles ) {
            $email = $userRoles["UserEmail"];
            $roles = $userRoles["UserRoles"];
            $this->openBlock("$email"."Roles");
            foreach ($roles as $roleName) {
                $this->setVar("UserRoleName", $roleName);
                $this->parseCurrentBlock();
            }
            $this->setBlock();
        }
    }
}

Finally we udate the controllers\NestedBlocks for providing users' roles data and for handling the View

<?php

namespace controllers;

use framework\Controller;
use views\NestedBlocks as NestedBlockView;

class NestedBlocks extends Controller
{
    /**
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        $this->view = new NestedBlockView();
        parent::__construct($this->view);

        $actions = $this->getUserActions();
        $this->view->setUserActionsBlock($actions);

        $users = $this->getUsersData();
        $this->view->setUsersBlock($users);

        $usersRoles = $this->getUsersRoles();
        $this->view->setUsersRoles($usersRoles);
    }

    /**
     * Set the default behaviour when no actions is performed
     */
    protected function autorun($parameters = null)
    {
        $this->view->setVar("CurrentAction","Please, perform an action on user");
    }

    /**
     * Provides users data.
     *
     * @return array  Array of users in the
     *                format array( array('Username'=>'','UserEmai'=>'') )
     */
    private function getUsersData()
    {
        $users = array(
            array('UserName' => 'Mark', 'UserEmail' => 'mark@email.com'),
            array('UserName' => 'Elen', 'UserEmail' => 'elen@email.com'),
            array('UserName' => 'John', 'UserEmail' => 'john@email.com')
        );
        return $users;
    }


    /**
     * Provides users actions.
     *
     * @return array Array of actions in the format:
     *                       array( array('ActionName'=>'','ActionCaption'=>'') )
     */
    private function getUserActions()
    {
        $userActions = array(
            array("ActionName" => "email" ,"ActionCaption" => "Send email"),
            array("ActionName" => "edit"  ,"ActionCaption" => "Edit information"),
            array("ActionName" => "erase","ActionCaption" => "Delete user")
        );
        return $userActions;
    }

    /**
     * Provides users roles.
     *
     * @return array Array of users roles in the format:
     *                       array ( array("UserEmail"=>'',"UserRoles"=>array(r1,r2...,rn)))
     */
    private function getUsersRoles()
    {
        $usersRoles = array(
                        array(  "UserEmail" => "mark@email.com",
                                "UserRoles" => array("admin","webmaster","moderator")
                        ),
                        array(  "UserEmail" => "elen@email.com",
                                "UserRoles" => array("editor","webmaster","user")
                        ),
                        array(  "UserEmail" => "john@email.com",
                                "UserRoles" => array("user","editor")
                        )
                    );
        return $usersRoles;
    }


    /**
     * Performs the given action on a given user.
     *
     * @param string $actionName The action to performs
     * @param string $userEmail The user email on which to perform the action
     */
    public function doAction($actionName,$userEmail)
    {
        $currentAction = "Current action: $actionName on user $userEmail";
        $this->view->setVar("CurrentAction",$currentAction);
        $this->render();
    }
}

Now by running http://localhost/nested_blocks you will see the following result:

Insight the example

Keep in mind that when you nesting of blocks you have to manage the correct sequence on how to produce the repetitions of the data in order to obtain the desired output. In this example, in fact, when coding the controller __construct, we first generated the data relating to the actions common to each user. Then we produced the list of users, also producing distinct blocks for each user's specific roles. Finally, we have enhanced the roles for each distinct block in the format UserEmailRole. If you try to change this sequence, the result could not be correct.

Summary

In this page, we discussed another dynamic behavior you can obtain when using the nesting of blocks. Although we used the nesting of blocks, or even blocks in general, to produce simple HTML list, you must take in consideration that you can apply them on different GUI components like, for example, options' list, radio buttons, check boxes, divs and so on.

Whats next

In the next page, we starting to introduce another important entity of MVC design pattern: the Model

Model

Introduction

In an MVC software architecture, a Model is a component that has the responsibility for data management. In other words, the model maintains a repository of data and provides the methods for data recording and retrieval. It is worthwhile to observe that the decomposition into the three components of an MVC architecture reflects the approach of divide et impera in which the controller assumes the role of coordinator that assigns the tasks of data management and data presentation to the model and view components respectively.

WebMVC Model

In WebMVC, the instantiation of a model is similar to that of a controller or a view; in fact, it is sufficient to extend the framework\Model class. As an example, we can further discuss the problem of showing a list of people in a browser. In the previous page, the list of users, actions, and roles were taken from the controller; while this could be convenient when the problem to solve is of small dimension (we could do without the model), it is more frequent the case where the data are managed by the model. So we can build a news class, models\NestedBlocks, in this way:

namespace models;

use framework\Model;

class NestedBlocks extends Model
{

    /**
     * Provides users data.
     *
     * @return array  Array of users in the format:
     *                array( array('Username'=>'','UserEmai'=>''))
     */
    public function getUsersData()
    {

        $users = array(
            array('UserName' => 'Mark', 'UserEmail' => 'mark@email.com'),
            array('UserName' => 'Elen', 'UserEmail' => 'elen@email.com'),
            array('UserName' => 'John', 'UserEmail' => 'john@email.com')
        );
        return $users;
    }

    /**
     * Provides users actions.
     *
     * @return array Array of actions in the format:
     *               array( array('ActionName'=>'','ActionCaption'=>'') )
     */
    public function getUserActions()
    {
        $userActions = array(
            array("ActionName" => "email" ,"ActionCaption" => "Send email"),
            array("ActionName" => "edit"  ,"ActionCaption" => "Edit information"),
            array("ActionName" => "erase","ActionCaption" => "Delete user")
        );

        return $userActions;
    }

    /**
     * Provides users' roles.
     *
     * @return array Array of users roles in the format:
     *               array ( array("UserEmail"=>'',"UserRoles"=>array(r1,r2...,rn)))
     */
    public function getUsersRoles()
    {
        $usersRoles = array(
            array("UserEmail" => "mark@email.com",
                  "UserRoles" => array("admin","webmaster","moderator")
            ),
            array("UserEmail" => "elen@email.com",
                  "UserRoles" => array("moderator","editor","user")
            ),
            array("UserEmail" => "john@email.com",
                  "UserRoles" => array("user","editor")
            )
        );
        return $usersRoles;
    }

}

By using the new class models\NestedBlocks, in which we coded methods for providing all the necessary data, we can now refactor the controllers\NestedBlocks by:

  • Adding a reference to models\NestedBlocks
  • Eliminating its protected methods getUsersData(), getUserActions(), and getUsersRoles() because they are now provided by models\NestedBlocks as public services.

See the code below for the new version of controllers\NestedBlocks :

<?php

namespace controllers;

use framework\Controller;
use views\NestedBlocks as NestedBlockView;
use models\NestedBlocks as NestedBlockModel;

class NestedBlocks extends Controller
{
    /**
     * @override framework\Controller __construct()
     */
    public function __construct()
    {
        $this->view = new NestedBlockView();
        $this->model = new NestedBlockModel();
        parent::__construct($this->view,$this->model);

        $actions = $this->model->getUserActions();
        $this->view->setUserActionsBlock($actions);

        $users = $this->model->getUsersData();
        $this->view->setUsersBlock($users);

        $usersRoles = $this->model->getUsersRoles();
        $this->view->setUsersRoles($usersRoles);
    }

    /**
     * Set the default behaviour when no actions is performed
     */
    protected function autorun($parameters = null)
    {
        $this->view->setVar("CurrentAction","Please, perform an action on user");
    }

    /**
     * Performs the given action on a given user.
     *
     * @param string $actionName The action to performs
     * @param string $userEmail The user email on which to perform the action
     */
    public function doAction($actionName,$userEmail)
    {
        $currentAction = "Current action: $actionName on user $userEmail";
        $this->view->setVar("CurrentAction",$currentAction);
        $this->render();
    }
}

We don't need to update the previuos views\NestedBlokcs and templates\nested_blocks.html.tpl because they are already decoupled from control and data logic.
The figure below shows the files structure of model, view, template, and controller:

So we can run again controllers\NestedBlocks by typing http://localhost/nested_blocks to obtain the same result of the previous page:

Summary

By using Model, the Controller must now take into account the coordination of View and Model following these steps:

  • Link the variable $this->view and $this->model to the corresponding class instances passing them to the constructor of the framework\Controller class
  • Use the instantiated model and view to retrieve and visualize the array of data provided by the Model

Summary the controller, retrieve the data from the model; then, calling the view, it arranges the presentation. Note that the code of view and the corresponding template are unchanged.

By introducing the Model you can now better understand the WebMVC architecture that separates an application into four layers: Model, View, Template, and Controller.

  • Model: Model represents the shape of the data and business logic. It maintains the data of the application. Model objects retrieve and store model state in some data structures like a database, array and so on.
    Model is a data and business logic.

  • View: View handles the user interface. View display data using Model and Template to the user and also enables them to submit an update on data. These requests will be later interceped and managed by the controller.
    View is a User Interface logic to provide data dynamically.

  • Template: Templates provides the static GUI design. A template is used by the View to generate dynamic web pages also by consuming data provided by the Model.
    Template is the User Interface design.

  • Controller: Controller handles the user request. Typically, a user interacts with View, which in-tern raises appropriate URL request, this request will be handled by a controller. The controller renders the appropriate view with the model data as a response.
    Controller is a request handler and a coordinator for Model and View.

The following figure summarizes the interaction between Model, View, Template, and Controller.

Whats next

Having in mind the role of a Model and how to use it in the MVC architecture, we can now introduce the data structures most used by WebMVC Model: MySQL database

Interacting with MySQL

Introduction

WebMVC framework\Model extends PHP \mysqli class for a native interaction with MySQL. In this page, we will show you the basic steps for creating and connecting to a MySQL database as well as for the data retrieving.

WebMVC and MySQL interaction

We can modify the previous example and retrieve the array of users and roles from a database. Before we do so, it must be taken into account that:

  • WebMVC framework\Model extends the PHP \mysqli class to interact with MySQL database
  • framework\Model also provides you with a custom $sql attribute, the methods updateResultSet(), and getResultSet() for:

    • Model->$sql is a class attribute designed to store any valid SQL statement, e.g a SELECT query, INSERT, UPDATE, DELETE, and more.
    • With Model->updateResultSet() you can execute the SQL statement you previously stored in the attribute Model->$sql
    • With Model->getResultSet() you are able to access the result of the executed SQL statement by means of Model->updateResultSet(). The result can be one of the following:

      • FALSE on failure regarding the execution of the SQL statement
      • for successful SELECT, SHOW, DESCRIBE or EXPLAIN statements (which returning data rows) it returns a PHP \mysqli_result object. The object will contain some data rows you can fetch by using some specialized methods it provides, or better it inherits from parent mysqli class. These methods are (see the \mysqli_result full documentation here):

            mixed fetch_all()
            mixed fetch_array()
            array fetch_assoc()
            object fetch_field()
            array fetch_fields()
            object fetch_object()
            mixed fetch_row()
      • for other successful SQL statements (which do not return any data rows) it returns TRUE.

  • You can also consume all public methods and attributes provided by the parent PHP \mysqli class, eg. mysqli:query(), mysqli:real_escape_string, etc. (see the mysqli full documentation here).
  • You must configure the file config\application.config.php. Specifically, you must modify the constants DBHOST, DBUSER, DBPASSWORD, DBNAME, and DBPORT according to your MySQL setting.
    The code belowe shows the section in config\application.config.php regarding MySQL configuration:
/**
 * Defines the constants for MySQL database connection parameters.
 */
/**
 *  MySQL Host
 */
define("DBHOST","TOUR_DB_HOST");
/**
 *  MySQL User
 */
define("DBUSER","YOUR_USER");
/**
 * MySQL Password
 */
define("DBPASSWORD","YOUR_PASSWORD");
/**
 *  MySQL Database
 */
define("DBNAME","YOUR_DB_NAME");
/**
 *  MySQL Port
 */
define('DBPORT', '3306');

In this example, we modify the models\NestedBlocks for enable it to retrieve data from some database tables. We assume:

  • the availability of a relational database schema, named _mvc_wiki_, containing tables and data for managing users and roles.
  • the availability of a table called user containing the same data of the array $users shown in the previous example
  • the availability of a table called _application_role_ containing all available application roles that can be assigned to a user
  • the availability of a table called _users_roles_ containing roles of each user and also reflecting the same roles assignment we did in the array $usersRoles shown in the example before

The following figure shows the database schema and tables' relationships:

Note that, when we design the database, we use the snake_case notation (lowercase and _under_scores_), rather the camelCase or PascalCase, for naming convention of tables and fields. This convention is widely adopted for MySQL design. Furthermore, by adopting this convention, WebMVC will provide you with an ORM engine to handle database tables with some specialized classes. We will discuss it later in this page.

To build the required DB schema and data you can use the following mvc_wiki_db.sql MySQL script:

-- MySQL Script for mvc_wiki database

-- Temporary disabling DB constraints checking
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';


-- -----------------------------------------------------
-- Create the schema for a simple database named mvc_wiki
-- -----------------------------------------------------
CREATE DATABASE IF NOT EXISTS `mvc_wiki` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `mvc_wiki`;

-- -----------------------------------------------------
-- Create table `user`
-- Stores users information
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `user` (
  `user_id` INT NOT NULL AUTO_INCREMENT,
  `user_name` VARCHAR(45) NULL,
  `user_email` VARCHAR(100) NULL,
  PRIMARY KEY (`user_id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Create table `application_role`
-- Stores all available application roles 
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `application_role` (
  `role_id` INT NOT NULL AUTO_INCREMENT,
  `role_name` VARCHAR(45) NULL,
  PRIMARY KEY (`role_id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Create table `users_roles`
-- Stores users roles 
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `users_roles` (
  `user_id` INT NOT NULL,
  `role_id` INT NOT NULL,
  PRIMARY KEY (`user_id`, `role_id`),
  INDEX `fk_user_has_application_role_application_role1_idx` (`role_id` ASC),
  INDEX `fk_user_has_application_role_user_idx` (`user_id` ASC),
  CONSTRAINT `fk_user_has_application_role`
    FOREIGN KEY (`user_id`)
    REFERENCES `user` (`user_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_application_role_assigned_to_user`
    FOREIGN KEY (`role_id`)
    REFERENCES `application_role` (`role_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- Restoring DB constraints checking
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

-- -----------------------------------------------------
-- Sample data for table `user`
-- -----------------------------------------------------
START TRANSACTION;
INSERT INTO `user` (`user_id`, `user_name`, `user_email`) VALUES (1, 'Mark', 'mark@email.com');
INSERT INTO `user` (`user_id`, `user_name`, `user_email`) VALUES (2, 'Elen', 'elen@email.com');
INSERT INTO `user` (`user_id`, `user_name`, `user_email`) VALUES (3, 'John', 'john@email.com');
COMMIT;


-- -----------------------------------------------------
-- Sample data data for table `application_role`
-- -----------------------------------------------------
START TRANSACTION;
INSERT INTO `application_role` (`role_id`, `role_name`) VALUES (5, 'admin');
INSERT INTO `application_role` (`role_id`, `role_name`) VALUES (4, 'webmaster');
INSERT INTO `application_role` (`role_id`, `role_name`) VALUES (3, 'moderator');
INSERT INTO `application_role` (`role_id`, `role_name`) VALUES (2, 'editor');
INSERT INTO `application_role` (`role_id`, `role_name`) VALUES (1, 'user');
COMMIT;


-- -----------------------------------------------------
-- Sample data for table `users_roles`
-- -----------------------------------------------------
START TRANSACTION;
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (1, 5);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (1, 4);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (1, 3);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (2, 3);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (2, 2);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (2, 1);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (3, 2);
INSERT INTO `users_roles` (`user_id`, `role_id`) VALUES (3, 1);
COMMIT;

To create the schema on your MySQL database open a command line prompt and type (be sure the mysql excutable file is in your PATH):

mysql -uYOUR_USER -pYOUR_PASSWORD < mvc_wiki_db.sql

Finally, we can modify the models\NestedBlocks by updating getUsersData() and getUsersRoles() with database access PHP instructions for retrieving information. Pay attention that we need to returns data retrieved from the DB by using arrays also having the same formats required by the views\NestedBlocks. For example, the format of $usersRoles array must be the following:

See the code below for the new version models\NestedBlocks by paying attention to comments:

<?php
namespace models;

use framework\Model;

class NestedBlocks extends Model
{

    /**
     * Retrieves users from db
     *
     * @return array|false Array of users in the format:
     *                     array( array('Username'=>'','UserEmai'=>'') )
     *                     Returns false when no data found
     */
    public function getUsersData()
    {
        // Notice: 
        // - We use PHP HereDoc to specify the SQL string
        $this->sql = <<<SQL
        SELECT
            user_name as UserName,
            user_email as UserEmail
        FROM
            user;
SQL;
        // Run the SQL statement and store the result
        $this->updateResultSet();

        // getResultSet() returns a mysqli_result already with the format
        // array( array('Username'=>'','UserEmai'=>'') )
        return $this->getResultSet();
    }

    /**
     * Provides users actions.
     *
     * @return array Array of actions in the format:
     *               array( array('ActionName'=>'','ActionCaption'=>'') )
     */
    public function getUserActions()
    {
        $userActions = array(
            array("ActionName" => "email" ,"ActionCaption" => "Send email"),
            array("ActionName" => "edit"  ,"ActionCaption" => "Edit information"),
            array("ActionName" => "erase","ActionCaption" => "Delete user")
        );

        return $userActions;
    }

    /**
     * Retrieves users' roles from db.
     *
     * @return array|false Array of users roles in the format:
     *                     array(array("UserEmail"=>'',"UserRoles"=>array(r1,r2...,rn)))
     *                     Returns false when no data found
     */
    public function getUsersRoles()
    {

        // Notice: 
        // - We use PHP HereDoc to specify the SQL string
        // 
        // - The SQL below joins tables user and application_role for retrieving
        //   the values for email and role name of each id stored into the
        //   users_roles table.
        $this->sql = <<<SQL
        SELECT
            user.user_id,
            user.user_email,
            application_role.role_name
        FROM
            user
              JOIN users_roles ON ((user.user_id = users_roles.user_id))
                JOIN application_role ON ((users_roles.role_id = application_role.role_id))
        ORDER BY
            application_role.role_id DESC
SQL;
        // Run the SQL statement and store the result
        $this->updateResultSet();

        // Gets the results into a variable for processing
        $mySqlResults = $this->getResultSet();

        // Initializing an empty array for storing users roles
        $usersRoles= array();

        if ($mySqlResults) {
            while ($row = $mySqlResults->fetch_array()) {
                $userId=$row["user_id"];
                $userEmail=$row["user_email"];
                $userRoleName=$row["role_name"];

                // Adds email to $usersRoles array
                $usersRoles[$userId]["UserEmail"]=$userEmail;

                // Adds role to $usersRoles array
                $usersRoles[$userId]["UserRoles"][] = $userRoleName;
            }
        }
        return !empty($usersRoles)? $usersRoles : false;
    }

}

So we can run once again controllers\Nested Blocks by typing http://localhost/nested_blocks to obtain the same result we shown on the previous page, but now by having data provided by MySQL:

Note that we don't need any updates on the Controller, View, and Template. This is the main purpose of MVC design, that is to separate the computational tasks in different layers also avoiding that, making a change on a layer, involves the need to update the others.

Summary

By understanding how to interacting with MySQL we are now potentially ready to build a web application. We finally, described all the steps needed to design and run a MVC instance which provide a dynamic web page. They are:

  • create a Model class interacting with the database and fetching data, to be used within the View
  • create a View class linking it to a Template file which provides the static design of a web page.
  • replace a value, provided by the Model, to a Placeholder within the Template
  • define, within the Template, a Block of static content, that must be dynamically replaced, for example with a list of values provided by the Model
  • create a Controller for managing and linking Model and View
  • run a Controller and/or its public Methods calling them from the URL

Pay also attention to the flow of operations when you run an MVC Instance with WebMVC:

  1. The URL calls a Controller or one of its Method (either with or without parameters)
  2. The Controller links and retrieves data from the Model
  3. The Controller links the View
  4. The View loads the static design from the Template
  5. The data retrieved from the Model are sent to the View by the Controller
  6. The View organizes the data for the presentation and generates the output.
  7. Finally, the Controller fetches the output from the View and sends back it to the user.

Note that, in the flow above, we intentionally omitted to describe Loader and Dispatcher actions because they are automatically executed by WebMVC

The following figure gives a graphical representation of these points:

Whats next

In the next page, we show you how to INSERT, UPDATE, and DELETE data from a database by using the WEB MVC ORM Engine.

MySQL ORM

Introduction

Object-relational mapping (ORM) is a mechanism that makes it possible to address, access and manipulate objects without having to consider how those objects relate to their database tables.
WebMVC provides you with a useful tool for the Object Relation Mapping of MySQL. This tool generates automatically Model classes for any tables of a given database schema.

WebMVC Model and ORM - Object Relational Mapping

To generate Model classes to map database tables you need to:

1) Use lowercase with the underscore, which is the widely used MySQL naming notation (formerly named as snake case notation), on your database tables and field names.

2) Configure your database schema by modifying util\mysqlreflection\mysqlreflection.config.php file and, assigning appropriate values to DBHOST, DBNAME, DBUSER, DBPASSWORD, and DBPORT PHP constants, according to your MySQL settings.

3) Then, launch the tool by typing:
http/localhost/util/app_create_beans.php Note that, the GUI of the utility uses Bootstrap and jQuery from CDN. So you also need an internet connection alive before running it.

4) Once the utility is started, click the "Generate classes" button.

The following figure shows you the startup screen of the utility:

Utility GUI

After running the generation of classes you can close the utility. You will find all the generated classes under models\beans folder.

Notice that:

  • You can find a class for each table of your MySQL schema.

  • Each auto-generated class name is prefixed with "Bean" followed by the table name in a PascalCase format. E.g, for the table name users_roles you will find a class named BeanUsersRoles.

  • Each auto-generated class extends framework\Model.php, so you can relate it to a Controller

Each auto-generated Model, widely known as a Database Bean, provides you with the following:

  • A constructor for managing a fetched data row from a table or for adding a new one on it

  • Management for both single or composite Primary Keys

  • Automatic mapping of the different date formats which may occur between the web application and database

  • It defines a set of attributes corresponding to the table fields

  • Setter and Getter methods for each attribute

  • Setter methods automatically sanitize data against SQL injection

  • OOP methods for simplifying the DML operations of SELECT, INSERT, UPDATE, and DELETE.

  • A facility for quickly updating a previously fetched row

  • Useful methods to obtain table DDL and the last executed SQL statement

  • Error handling of SQL statements

  • It uses camelCase and PascalCase naming convention on class, attributes, and methods that were generated to map table and fields

  • PHPDOC on table, fields, class, attributes, and usage of methods

Notice that ORM doesn't relieve you from a good DB design. This means you must design a good relational database schema, before using the ORM engine provided by WebMVC.

What's next

You can use autogenerated database bean classes as a Model or in conjunction with it when coding your MVC applications. On the next page, we explain a a fully functioning DB application and consuming, Model, View ad an autogenerate database bean class.

Example of a DB Application

Introduction

The purpose of this example is to provide a simple but fully functioning customer management application, and also for exposing all basic concepts of WebMC Framework we discussed right now:

  • Controller
  • Model
  • View
  • Template
  • Placeholders and Blocks
  • OOP
  • MVC Assembly
  • Database and ORM using autogenerated Database Bean

System Requirements

First of all, we need to define the system requirements, they are the following:
CRM User must be able:

  • To store, manage, and retrieve information about customers. The information must be about the name, email, nationality (Italian or guest), and assurance level (low, middle, or high) of any given customer;
  • To browse customers list
  • To select a customer from the list for showing, editing its data or, eventually, delete its record from the DB
  • To select a customer from the list for rapidly sending a mail
  • To add a new customer record to the DB

System Design

We organized the System Design under the following sections:

Database Design

First of all, we need to define the Database Design to store and manage customers. In our system it is very simple, it consists of the single table customer coded below:

--
-- Table `customer`
--
CREATE TABLE `customer` (
  `customer_id` int(11) NOT NULL,
  `name` varchar(45) NOT NULL,
  `email` varchar(100) NOT NULL,
  `nationality` varchar(4) DEFAULT NULL,
  `assurance` int(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- PK for table `customer`
--
ALTER TABLE `customer`
  ADD PRIMARY KEY (`customer_id`);

--
-- Unique index for email field
-- 
ALTER TABLE `customer` ADD UNIQUE(`email`);

--
-- AUTO_INCREMENT for table `customer`
--
ALTER TABLE `customer`
  MODIFY `customer_id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;

As you can see in the SQL code above, the customer table stores the name, email, nationality, and assurance level (listed in system requirements). It also defines an incremental primary key, required by DB normalization regarding no duplicate tuples, and a Unique index for the email field (a Non-Functional Requirement). Note that, when building a database, you should always be compliant with DB design principles.

GUI Design

Due to the simplicity of the system, we made a rapid "System Analysis" for establishing its GUI Design capable of covering all previous system requirements. So, we decided to provide two user scenarios, or better two web pages, for the purpose.

  • One for providing the customer's list, from which the user should be able to select a customer for editing it's data or, also, adding a new one.
  • The second for providing the showing or editing of a customer record previously selected, as well as to delete it, or for insert data regarding a new one.
    The following figures show you the GUI for these pages:

The left side showing the GUI for browsing customers with the ability to select a customer record for editing, or sending a mail easily. There is also a function to add a new record. The right side showing the GUI for a customer record. The same GUI can be used for implementing two different operative mode can be done on a customer record. The first, in the upper left, is regarding the editing mode where the user can show and update data, or delete a customer record previously selected. The second, in the bottom right, is regarding the inserting mode when the user needs to add a new customer to DB.

Under a UML point of view, the right side may be considered as a single Use Case, the customer record, that can be extended with two different features: one regarding inserting, other for editing data.

In conclusion, the GUI Design may be defined with the following two GUI templates:

The first template, on the left, is used for implementing the browsing, the second, on the right, for the customer's record. In the last one, both the insert and editing functions are implemented as extensions on a common section showing form fields. Furthermore, all templates are built with a responsive design (look, for example, at the navigation bar) provided by Bootstrap, a wonderful HTML/CSS/JS web framework for designing amazing Web GUI. You may also note like some UI elements, in both templates, are designed to be shown when some circumstances occurred. For example, the red message "no customer" will be shown only when no customers are stored in the DB. Furthermore, the red section "Errors" or buttons regarding form submission will be respectively processed depending, on the occurrence of a DB error or by analyzing if the record is in inserting or editing mode.

Application Design

Finally, we provide the Application Design for organizing system development. We define the following two Web MVC Assemblies for implementing the application :

  • CustomersManager for browsing customers
  • CustomerRecord for customer data manipulation.
    Because the system is very simple we do not have the need for any Subsystems Design

System Implementation

The implementation details of both assemblies will be exposed in the section below

CustomersManager

The following sections contain the template and MVC source code of the CustomersManager assembly.

file templates\customers_manager.html.tpl

<!DOCTYPE html>
<html>
<head>
    <title>Customers Manager</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap core CSS -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"
          media="screen">

    <!-- Fonts Awesome -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>
    <![endif]-->
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
            data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">CRM Simple</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="#">Home</a></li>
                <li class="active"><a href="{GLOBAL:SITEURL}/customers_manager">Customers</a></li>
            </ul>
        </div>
    </div>
</nav>

<div class="container">
    <div class="row">
        <div class="col col-12">
            <h1>Customers Manager</h1>
            <hr>
            <div class="table-responsive-lg">
                <table class="table table-hover">
                    <thead>
                    <tr>
                        <th scope="col">Actions</th>
                        <th scope="col">Customer name</th>
                        <th scope="col">Email</th>
                    </tr>
                    </thead>
                    <tbody>
                    <!-- BEGIN CustomersList -->
                    <tr>

                        <th scope="row">
                            <a class="btn btn-info" href="customer_record/open/{CustomerID}">
                            <i class="fa fa-edit"></i>&nbsp;Edit</a>
                            <a class="btn btn-warning" href="mailto:{CustomerEmail}">
                            <i class="fa fa-address-card-o"></i>&nbsp;Email</a>
                        </th>
                        <td>{CustomerName}</td>
                        <td>{CustomerEmail}</td>
                    </tr>
                    <!-- END CustomersList -->
                    </tbody>
                    <tfoot>
                        <!-- BEGIN NoCustomers -->
                        <tr>
                            <td colspan="3" class="text-danger text-center">
                                No customer
                            </td>
                        </tr>
                        <!-- END NoCustomers -->
                        <tr>
                            <td colspan="3">
                                <a class="btn btn-primary" href="customer_record"><i class="fa fa-plus"></i>&nbsp;Add a new customer</a>
                            </td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        </div>
    </div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>
</body>
</html>

file controllers\CustomersManager.php

namespace controllers;

use framework\Controller;
use framework\Model;
use framework\View;
use models\CustomersManager as CustomersManagerModel;
use views\CustomersManager as CustomersManagerView;

class CustomersManager extends Controller
{
    protected $view;
    protected $model;

    /**
    * Object constructor.
    *
    * @param View $view
    * @param Model $mode
    */
    public function __construct(View $view=null, Model $model=null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

    /**
    * Autorun method. Put your code here for running it after object creation.
    * @param mixed|null $parameters Parameters to manage
    *
    */
    protected function autorun($parameters = null)
    {
       $customers = $this->model->getCustomers();
       $this->view->setCustomersBlock($customers);
    }

    /**
    * Inizialize the View by loading static design of /customers_manager.html.tpl
    * managed by views\CustomersManager class
    *
    */
    public function getView()
    {
        $view = new CustomersManagerView("/customers_manager");
        return $view;
    }

    /**
    * Inizialize the Model by loading models\CustomersManager class
    *
    */
    public function getModel()
    {
        $model = new CustomersManagerModel();
        return $model;
    }
}

The controller just initializes View and Model. Then retrieve customers' data (from Model) to be processed by the View.

file models\CustomersManager.php

namespace models;

use framework\Model;

class CustomersManager extends Model
{
    /**
    * Object constructor.
    *
    */
    public function __construct()
    {
        parent::__construct();
    }

    /**
    * Autorun method. Put your code here for running it after object creation.
    * @param mixed|array|null $parameters Additional parameters to manage
    *
    */
    protected function autorun($parameters = null)
    {

    }


    public function getCustomers()
    {
        // Notice: we use PHP HereDoc to specify SQL string
        $this->sql = <<<SQL
        SELECT
            customer_id as CustomerID,
            name as CustomerName,
            email as CustomerEmail
        FROM
            customer
        ORDER BY
            name;
SQL;
        $this->updateResultSet();
        // The mysqli result set already has the format:
        // array( array('CustomerID'=>'','CustomerName'=>'','CustomerEmail'=>''),)
        return $this->getResultSet();
    }
}

The Model just implements the necessary SQL statement for retrieving customers from MySQL database.

file views\CustomersManager.php

namespace views;

use framework\View;

class CustomersManager extends View
{

    /**
     * Object constructor.
     *
     * @param string|null $tplName The html template containing the static design.
     */
    public function __construct($tplName = null)
    {
        if (empty($tplName))
            $tplName = "/customers_manager";
        parent::__construct($tplName);
    }

    /**
     * Render CustomersList Block
     *
     * @param \mysqli_result $customers
     * @throws \framework\exceptions\BlockNotFoundException
     * @throws \framework\exceptions\NotInitializedViewException
     * @throws \framework\exceptions\VariableNotFoundException
     */
    public function setCustomersBlock(\mysqli_result $customers)
    {
        if ($customers->num_rows > 0) {
            $this->hide("NoCustomers");
            $this->openBlock("CustomersList");
            while ($customer = $customers->fetch_object()) {
                $this->setVar("CustomerID", $customer->CustomerID);
                $this->setVar("CustomerName", $customer->CustomerName);
                $this->setVar("CustomerEmail", $customer->CustomerEmail);
                $this->parseCurrentBlock();
            }
            $this->setBlock();
        } else {
            $this->hide("CustomersList");
        }
    }
}

The View renders the customer list by processing the template Block CustomersList. If no data are retrieved from Model it shows an information text. To perform this action it uses the block hiding feature

CustomerRecord

In the next section, we show you the codes about record management for editing or inserting customers. The main news is regarding the Model and the auto-generated DataBase Bean class, from the MySQL customer table, we obtained through the ORM engine we previously discussed
The database Bean code is shown below

file models\beans\BeanCustomer.php

/**
 * Class BeanCustomer
 * Bean class for object oriented management of the MySQL table customer
 *
 * Comment of the managed table customer: Not specified.
 *
 * Responsibility:
 *
 *  - provides instance constructor for both managing of a fetched table or for a new row
 *  - provides destructor to automatically close database connection
 *  - defines a set of attributes corresponding to the table fields
 *  - provides setter and getter methods for each attribute
 *  - provides OO methods for simplify DML select, insert, update and delete operations.
 *  - provides a facility for quickly updating a previously fetched row
 *  - provides useful methods to obtain table DDL and the last executed SQL statement
 *  - provides error handling of SQL statement
 *  - uses Camel/Pascal case naming convention for Attributes/Class used for mapping of Fields/Table
 *  - provides useful PHPDOC information about the table, fields, class, attributes and methods.
 *
 * @extends MySqlRecord
 * @implements Bean
 * @filesource BeanCustomer.php
 * @category MySql Database Bean Class
 * @package models/bean
 * @author Rosario Carvello <rosario.carvello@gmail.com>
 * @version GIT:v1.0.0
 * @note  This is an auto generated PHP class builded with MVCMySqlReflection, a small code generation engine extracted from the author's personal MVC Framework.
 * @copyright (c) 2016 Rosario Carvello <rosario.carvello@gmail.com> - All rights reserved. See License.txt file
 * @license BSD
 * @license https://opensource.org/licenses/BSD-3-Clause This software is distributed under BSD Public License.
*/
namespace models\beans;
use framework\MySqlRecord;
use framework\Bean;

class BeanCustomer extends MySqlRecord implements Bean
{
    /**
     * A control attribute for the update operation.
     * @note An instance fetched from db is allowed to run the update operation.
     *       A new instance (not fetched from db) is allowed only to run the insert operation but,
     *       after running insertion, the instance is automatically allowed to run update operation.
     * @var bool
     */
    private $allowUpdate = false;

    /**
     * Class attribute for mapping the primary key customer_id of table customer
     *
     * Comment for field customer_id: Not specified<br>
     * @var int $customerId
     */
    private $customerId;

    /**
     * A class attribute for evaluating if the table has an autoincrement primary key
     * @var bool $isPkAutoIncrement
     */
    private $isPkAutoIncrement = true;

    /**
     * Class attribute for mapping table field name
     *
     * Comment for field name: Not specified.<br>
     * Field information:
     *  - Data type: varchar(45)
     *  - Null : NO
     *  - DB Index:
     *  - Default:
     *  - Extra:
     * @var string $name
     */
    private $name;

    /**
     * Class attribute for mapping table field email
     *
     * Comment for field email: Not specified.<br>
     * Field information:
     *  - Data type: varchar(100)
     *  - Null : NO
     *  - DB Index:
     *  - Default:
     *  - Extra:
     * @var string $email
     */
    private $email;

    /**
     * Class attribute for mapping table field nationality
     *
     * Comment for field nationality: Not specified.<br>
     * Field information:
     *  - Data type: varchar(4)
     *  - Null : YES
     *  - DB Index:
     *  - Default:
     *  - Extra:
     * @var string $nationality
     */
    private $nationality;

    /**
     * Class attribute for mapping table field assurance
     *
     * Comment for field assurance: Not specified.<br>
     * Field information:
     *  - Data type: int(1)
     *  - Null : YES
     *  - DB Index:
     *  - Default:
     *  - Extra:
     * @var int $assurance
     */
    private $assurance;

    /**
     * Class attribute for storing the SQL DDL of table customer
     * @var string base64 encoded $ddl
     */
    private $ddl = "Q1JFQVRFIFRBQkxFIGBjdXN0b21lcmAgKAogIGBjdXN0b21lcl9pZGAgaW50KDExKSBOT1QgTlVMTCBBVVRPX0lOQ1JFTUVOVCwKICBgbmFtZWAgdmFyY2hhcig0NSkgTk9UIE5VTEwsCiAgYGVtYWlsYCB2YXJjaGFyKDEwMCkgTk9UIE5VTEwsCiAgYG5hdGlvbmFsaXR5YCB2YXJjaGFyKDQpIERFRkFVTFQgTlVMTCwKICBgYXNzdXJhbmNlYCBpbnQoMSkgREVGQVVMVCBOVUxMLAogIFBSSU1BUlkgS0VZIChgY3VzdG9tZXJfaWRgKQopIEVOR0lORT1Jbm5vREIgREVGQVVMVCBDSEFSU0VUPXV0Zjg=";

    /**
     * setCustomerId Sets the class attribute customerId with a given value
     *
     * The attribute customerId maps the field customer_id defined as int(11).<br>
     * Comment for field customer_id: Not specified.<br>
     * @param int $customerId
     * @category Modifier
     */
    public function setCustomerId($customerId)
    {
        // $this->customerId = (int)$customerId;
        $this->customerId = (int) $this->real_escape_string($customerId);
    }

    /**
     * setName Sets the class attribute name with a given value
     *
     * The attribute name maps the field name defined as varchar(45).<br>
     * Comment for field name: Not specified.<br>
     * @param string $name
     * @category Modifier
     */
    public function setName($name)
    {
        // $this->name = (string)$name;
        $this->name = (string) $this->real_escape_string($name);
    }

    /**
     * setEmail Sets the class attribute email with a given value
     *
     * The attribute email maps the field email defined as varchar(100).<br>
     * Comment for field email: Not specified.<br>
     * @param string $email
     * @category Modifier
     */
    public function setEmail($email)
    {
        // $this->email = (string)$email;
        $this->email = (string) $this->real_escape_string($email);
    }

    /**
     * setNationality Sets the class attribute nationality with a given value
     *
     * The attribute nationality maps the field nationality defined as varchar(4).<br>
     * Comment for field nationality: Not specified.<br>
     * @param string $nationality
     * @category Modifier
     */
    public function setNationality($nationality)
    {
        // $this->nationality = (string)$nationality;
        $this->nationality = (string) $this->real_escape_string($nationality);
    }

    /**
     * setAssurance Sets the class attribute assurance with a given value
     *
     * The attribute assurance maps the field assurance defined as int(1).<br>
     * Comment for field assurance: Not specified.<br>
     * @param int $assurance
     * @category Modifier
     */
    public function setAssurance($assurance)
    {
        // $this->assurance = (int)$assurance;
        $this->assurance = (int) $this->real_escape_string($assurance);
    }

    /**
     * getCustomerId gets the class attribute customerId value
     *
     * The attribute customerId maps the field customer_id defined as int(11).<br>
     * Comment for field customer_id: Not specified.
     * @return int $customerId
     * @category Accessor of $customerId
     */
    public function getCustomerId()
    {
        return $this->customerId;
    }

    /**
     * getName gets the class attribute name value
     *
     * The attribute name maps the field name defined as varchar(45).<br>
     * Comment for field name: Not specified.
     * @return string $name
     * @category Accessor of $name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * getEmail gets the class attribute email value
     *
     * The attribute email maps the field email defined as varchar(100).<br>
     * Comment for field email: Not specified.
     * @return string $email
     * @category Accessor of $email
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * getNationality gets the class attribute nationality value
     *
     * The attribute nationality maps the field nationality defined as varchar(4).<br>
     * Comment for field nationality: Not specified.
     * @return string $nationality
     * @category Accessor of $nationality
     */
    public function getNationality()
    {
        return $this->nationality;
    }

    /**
     * getAssurance gets the class attribute assurance value
     *
     * The attribute assurance maps the field assurance defined as int(1).<br>
     * Comment for field assurance: Not specified.
     * @return int $assurance
     * @category Accessor of $assurance
     */
    public function getAssurance()
    {
        return $this->assurance;
    }

    /**
     * Gets DDL SQL code of the table customer
     * @return string
     * @category Accessor
     */
    public function getDdl()
    {
        return base64_decode($this->ddl);
    }

    /**
    * Gets the name of the managed table
    * @return string
    * @category Accessor
    */
    public function getTableName()
    {
        return "customer";
    }

    /**
     * The BeanCustomer constructor
     *
     * It creates and initializes an object in two way:
     *  - with null (not fetched) data if none $customerId is given.
     *  - with a fetched data row from the table customer having customer_id=$customerId
     * @param int $customerId. If omitted an empty (not fetched) instance is created.
     * @return BeanCustomer Object
     */
    public function __construct($customerId = null)
    {
        // $this->connect(DBHOST,DBUSER,DBPASSWORD,DBNAME,DBPORT);
        parent::__construct();
        if (!empty($customerId)) {
            $this->select($customerId);
        }
    }

    /**
     * The implicit destructor
     */
    public function __destruct()
    {
        $this->close();
    }

    /**
     * Explicit destructor. It calls the implicit destructor automatically.
     */
    public function close()
    {
        // unset($this);
    }

    /**
     * Fetchs a table row of customer into the object.
     *
     * Fetched table fields values are assigned to class attributes and they can be managed by using
     * the accessors/modifiers methods of the class.
     * @param int $customerId the primary key customer_id value of table customer which identifies the row to select.
     * @return int affected selected row
     * @category DML
     */
    public function select($customerId)
    {
        $sql =  "SELECT * FROM customer WHERE customer_id={$this->parseValue($customerId,'int')}";
        $this->resetLastSqlError();
        $result =  $this->query($sql);
        $this->resultSet=$result;
        $this->lastSql = $sql;
        if ($result){
            $rowObject = $result->fetch_object();
            @$this->customerId = (integer)$rowObject->customer_id;
            @$this->name = $this->replaceAposBackSlash($rowObject->name);
            @$this->email = $this->replaceAposBackSlash($rowObject->email);
            @$this->nationality = $this->replaceAposBackSlash($rowObject->nationality);
            @$this->assurance = (integer)$rowObject->assurance;
            $this->allowUpdate = true;
        } else {
            $this->lastSqlError = $this->sqlstate . " - ". $this->error;
        }
        return $this->affected_rows;
    }

    /**
     * Deletes a specific row from the table customer
     * @param int $customerId the primary key customer_id value of table customer which identifies the row to delete.
     * @return int affected deleted row
     * @category DML
     */
    public function delete($customerId)
    {
        $sql = "DELETE FROM customer WHERE customer_id={$this->parseValue($customerId,'int')}";
        $this->resetLastSqlError();
        $result = $this->query($sql);
        $this->lastSql = $sql;
        if (!$result) {
            $this->lastSqlError = $this->sqlstate . " - ". $this->error;
        }
        return $this->affected_rows;
    }

    /**
     * Insert the current object into a new table row of customer
     *
     * All class attributes values defined for mapping all table fields are automatically used during inserting
     * @return mixed MySQL insert result
     * @category DML
     */
    public function insert()
    {
        if ($this->isPkAutoIncrement) {
            $this->customerId = "";
        }
        // $constants = get_defined_constants();
        $sql = <<< SQL
            INSERT INTO customer
            (name,email,nationality,assurance)
            VALUES(
            {$this->parseValue($this->name,'notNumber')},
            {$this->parseValue($this->email,'notNumber')},
            {$this->parseValue($this->nationality,'notNumber')},
            {$this->parseValue($this->assurance)})
SQL;
        $this->resetLastSqlError();
        $result = $this->query($sql);
        $this->lastSql = $sql;
        if (!$result) {
            $this->lastSqlError = $this->sqlstate . " - ". $this->error;
        } else {
            $this->allowUpdate = true;
            if ($this->isPkAutoIncrement) {
                $this->customerId = $this->insert_id;
            }
        }
        return $result;
    }

    /**
     * Updates a specific row from the table customer with the values of the current object.
     *
     * All class attribute values defined for mapping all table fields are automatically used during updating of selected row.<br>
     * Null values are used for all attributes not previously setted.
     * @param int $customerId the primary key customer_id value of table customer which identifies the row to update.
     * @return mixed MySQL update result
     * @category DML
     */
    public function update($customerId)
    {
        // $constants = get_defined_constants();
        if ($this->allowUpdate) {
            $sql = <<< SQL
            UPDATE
                customer
            SET
                name={$this->parseValue($this->name,'notNumber')},
                email={$this->parseValue($this->email,'notNumber')},
                nationality={$this->parseValue($this->nationality,'notNumber')},
                assurance={$this->parseValue($this->assurance)}
            WHERE
                customer_id={$this->parseValue($customerId,'int')}
SQL;
            $this->resetLastSqlError();
            $result = $this->query($sql);
            if (!$result) {
                $this->lastSqlError = $this->sqlstate . " - ". $this->error;
            } else {
                $this->select($customerId);
                $this->lastSql = $sql;
                return $result;
            }
        } else {
            return false;
        }
    }

    /**
     * Facility for updating a row of customer previously loaded.
     *
     * All class attribute values defined for mapping all table fields are automatically used during updating.
     * @category DML Helper
     * @return mixed MySQLi update result
     */
    public function updateCurrent()
    {
        if ($this->customerId != "") {
            return $this->update($this->customerId);
        } else {
            return false;
        }
    }

}
?>

As you can note, the class provides you with all the necessary methods for customer table management. It also extends the framework\MySqlRecord,and implements the framework\Bean interface, which are both frameworks facilities for interacting with MySQL, as well as, for classifying a Database Bean as a Model type could be consumed by an MVC Assembly.
For these reasons, as you can easily note in the following code regarding the models\CostomerRecord.php, we designed it, simply by extending the previously showed database Bean, and so we are able to interact with the table customer (managed by its parent Database Bean)

file models\CustomerRecord.php

namespace models;

use models\beans\BeanCustomer;

class CustomerRecord extends BeanCustomer
{
    /**
     * Get the list of allowed nationalities for a customer.
     *
     * @return array Array of nationalities
     */
    public function getCustomerNationalitiesList(){
        return  array (
            array ("it","Italian"),
            array( "out","Guest")
        );
    }

    /**
     * Get the list of allowed assurances level for a customer.
     *
     * @return array Array of assurances level
     */
    public function getCustomerAssurancesList(){
        return  array (
            array ("1","Low"),
            array( "2","Middle"),
            array( "3","High"),
        );
    }
}

As you can note, in the code above, we don't have any methods regarding data management like, for example, inserting or updating records. We just defined two methods providing customer nationalities and assurances levels we need when classifying a customer.

The template code is shown below file templates\customer_record.html.tpl

<!DOCTYPE html>
<html>
<head>
    <title>Customer Record</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap core CSS -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"
          media="screen">
    <!-- Fonts Awesome -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>
    <![endif]-->
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                    aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">CRM Simple</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="#">Home</a></li>
                <li class="active"><a href="{GLOBAL:SITEURL}/customers_manager">Customers</a></li>
            </ul>
        </div>
    </div>
</nav>

<div class="container">

    <h1>Customer Record</h1>
    {Operation}
    <hr>
    <form class="form-horizontal" method="post">
        <!-- BEGIN DBError -->
        <div class="alert alert-danger" role="alert">
            {Errors}
        </div>
        <!-- END DBError -->
        <div class="form-group">
            <label for="name" class="control-label col-xs-4">Name</label>
            <div class="col-xs-8">
                <div class="input-group">
                    <div class="input-group-addon">
                        <i class="fa fa-user-md"></i>
                    </div>
                    <input id="name" name="name" placeholder="Customer name" type="text" required="required"
                           class="form-control" value="{name}">
                </div>
            </div>
        </div>
        <div class="form-group">
            <label for="email" class="control-label col-xs-4">Email</label>
            <div class="col-xs-8">
                <div class="input-group">
                    <div class="input-group-addon">
                        <i class="fa fa-address-card-o"></i>
                    </div>
                    <input id="email" name="email" placeholder="Customer email" type="email" required="required"
                           class="form-control" value="{email}">
                </div>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-xs-4">Nationality</label>
            <div class="col-xs-8">
                <!-- BEGIN NationalitiesCheckboxes -->
                <label class="checkbox-inline">
                    <input type="radio" name="nationality" value="{nationality}" {is_checked}>
                    {nationality_text}
                </label>
                <!-- END NationalitiesCheckboxes -->
            </div>
        </div>
        <div class="form-group">
            <label for="assurance" class="control-label col-xs-4">Customer assurance</label>
            <div class="col-xs-8">
                <select id="assurance" name="assurance" required="required" class="select form-control">
                    <!-- BEGIN AssuranceOptions -->
                    <option value="{assurance}" {is_selected}>{assurance_text}</option>
                    <!-- END AssuranceOptions -->
                </select>
            </div>
        </div>
        <div class="form-group row">
            <div class="col-xs-offset-4 col-xs-8">
                <input type="hidden" name="customer_id" value="{customer_id}">
                <!-- BEGIN AddMode -->
                <input name="operation_insert" type="submit" class="btn btn-primary" value="Add customer">
                <!-- END AddMode -->
                <!-- BEGIN EditMode -->
                <input name="operation_update" type="submit" class="btn btn-primary" value="Update">
                <input name="operation_delete" id="delete" type="submit" class="btn btn-danger" value="Delete customer">
                <!-- END EditMode -->
                <a href="{GLOBAL:SITEURL}/customers_manager" class="btn btn-info">Close or Cancel</a>
            </div>

        </div>
    </form>

</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>

<script>
    function confirmDelete(){
        return confirm("Delete current customer ?");
    }
    $(document).ready(function() {
        $("#delete").click(function () {
            return confirmDelete();
        });
    });
</script>
</body>
</html>

The template designs different blocks we need for managing DB error, nationalities list and of assurances levels, as well as, the buttons manging inserting or editing actions will be submitted by the HTML form. Finally, it also contains the necessary Javascript and Jquery code for handling confirmation message when deleting a record.

Finally, we show the code for controllers\CustomerRecord.php

namespace controllers;

use framework\Controller;
use framework\Model;
use framework\View;

use models\CustomerRecord as CustomerRecordModel;
use views\CustomerRecord as CustomerRecordView;

class CustomerRecord extends Controller
{
    protected $view;
    protected $model;

    /**
     * CustomerRecord constructor.
     *
     * @param View|null $view
     * @param Model|null $model
     * @throws \framework\exceptions\TemplateNotFoundException
     */
    public function __construct(View $view = null, Model $model = null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view, $this->model);
    }

    /**
     * Autorun method. Put your code here for running it after
     * object creation.
     *
     * @param mixed|null $parameters Parameters to manage
     */
    protected function autorun($parameters = null)
    {
        $this->handleFormActionsSubmission();
        $this->view->initFormFields($this->model);
    }

    /**
     * Manage open action of a customer record
     *
     * @param int $customerID The customer id you need to select
     * @throws \framework\exceptions\NotInitializedViewException
     */
    public function open($customerID)
    {

        $currentRecord = $this->model->select($customerID);
        if (!$currentRecord)
            $this->closeAndRedirect();
        $this->autorun();
        $this->render();
    }

    /**
     * Manage form actions submission.
     *
     */
    private function handleFormActionsSubmission()
    {

        if (isset($_POST["operation_update"])) {
            $this->handlePostFields();
            $this->model->updateCurrent();
        }
        if (isset($_POST["operation_delete"])) {
            $this->model->delete($_POST["customer_id"]);
            $this->closeAndRedirect();
        }
        if (isset($_POST["operation_insert"])) {
            $this->handlePostFields();
            $this->model->insert();
            $this->closeAndRedirect();
        }
    }

    /**
     * Handle post fields and setting the corresponding
     * model values.
     *
     */
    private function handlePostFields()
    {
        $this->model->setName(@$_POST["name"]);
        $this->model->setEmail(@$_POST["email"]);
        $this->model->setNationality(@$_POST["nationality"]);
        $this->model->setAssurance(@$_POST["assurance"]);
    }

    /**
     * Closing and redirecting if no SQL error occurred
     *
     */
    private function closeAndRedirect()
    {
        if (!$this->model->isSqlError()) {
            header("location:" . SITEURL . "/customers_manager");
        }
    }


    /**
     * Init View by loading static design of /customer_record.html.tpl
     * managed by views\CustomerRecord class
     *
     */
    public function getView()
    {
        $view = new CustomerRecordView("/customer_record");
        return $view;
    }

    /**
     * Init Model by loading models\CustomerRecord class
     *
     */
    public function getModel()
    {
        $model = new CustomerRecordModel();
        return $model;
    }
}

The controller, depending on form submission, is able to perform the right action on the database by using the Model, which is the customer database bean. It interacts with the View below by passing it the model (look at autorun method). Also, note the default behavior for the controller is to show the record in Inserting Mode. For handling record update it provides the open method. Thi method just selects the given customer record and performs a redirection if the customer is not found in the table, then it executes the autorun method.

file 'views\CustomerRecord.php'

namespace views;

use framework\View;

class CustomerRecord extends View
{

    /**
    * Object constructor.
    *
    * @param string|null $tplName The html template containing the static design.
    */
    public function __construct($tplName = null)
    {
        if (empty($tplName))
            $tplName = "/customer_record";
        parent::__construct($tplName);
    }

    public function initFormFields(\models\CustomerRecord $model){
        $name = isset($_POST["name"]) ? $_POST["name"] : $model->getName();
        $email = isset($_POST["email"]) ? $_POST["email"] : $model->getEmail();
        $nationality = isset($_POST["nationality"]) ? $_POST["nationality"] : $model->getNationality();
        $assurance = isset($_POST["assurance"]) ? $_POST["assurance"] : $model->getAssurance();
        $customer_id = isset($_POST["customer_id"]) ? $_POST["customer_id"] : $model->getCustomerId();

        $this->setVar("name", $name);
        $this->setVar("email", $email);
        $this->initCustomerNationalities($model->getCustomerNationalitiesList(),$nationality);
        $this->initCustomerAssurances($model->getCustomerAssurancesList(),$assurance);
        $this->setVar("customer_id", $customer_id);

        if (empty($model->getCustomerId())) {
            $this->setVar("Operation", "New Customer");
            $this->hide("EditMode");
        } else {
            $this->setVar("Operation", "Edit Customer");
            $this->hide("AddMode");

        }

        if (!$model->isSqlError()){
            $this->hide("DBError");
        } else {
            $this->setVar("Errors", $model->lastSqlError());

        }
    }

    private function initCustomerNationalities($nationalities,$checkedItem = ""){
        $this->openBlock("NationalitiesCheckboxes");
        foreach ($nationalities  as $nationality) {
            $this->setVar("nationality", $nationality[0]);
            if ($checkedItem == $nationality[0]) {
                $this->setVar("is_checked", "checked");
            } else {
                $this->setVar("is_checked", "");
            }
            $this->setVar("nationality_text", $nationality[1]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }

    private function initCustomerAssurances($assurances, $selectedIdem = ""){
        $this->openBlock("AssuranceOptions");
        foreach ($assurances  as $assurance) {
            $this->setVar("assurance", $assurance[0]);
            if ($selectedIdem == $assurance[0]) {
                $this->setVar("is_selected", "selected");
            } else {
                $this->setVar("is_selected", "");
            }
            $this->setVar("assurance_text", $assurance[1]);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }
}

The View initializes the GUI elements depending on the Model (and by its status) it receives. Look at the initFormFields method. So behaviors of all GUI elements are depending on inserting or updating mode, which is strictly related to the database bean status. The status can be expressed with the following roles:

  • inserting mode = when no record is selected by the received bean
  • updating mode = an existing record is selected by the received bean

Conclusion

The example we just discussed is a simple demonstration for the basic features of the Web MVC framework and its ORM engine for easily building of database applications. In the next pages of the current wiki, we expose you how to improve this basic example with advanced functionalities like:

  • Internationalization
  • Hierarchical MVC (HMVC) for content decompositions ad reuse
  • Authentication and role-based access management
  • Component-Based development

System decomposition

Introduction

In this section, you will learn about the key design principles and guidelines for software architecture.
Many of these principles have addressed the development of the framework determining those characteristics of decomposition that will be illustrated in the following pages. Moreover, the topics covered are useful to have a general understanding of the architecture that software application should have.
If you are already familiar with this concepts you can skip reading of this page.

Key Principles of Software Architecture

In this section, you will learn about the key design principles and guidelines for software architecture. Software architecture is often described as the organization or structure of a system, where the system represents a collection of components that accomplish a specific function or set of functions. In other words, architecture is focused on organizing components to support specific functionality. This organization of functionality is often referred to as grouping components into “areas of concern.” Th figure below illustrates common application architecture with components grouped by different areas of concern.

In addition to the grouping of components, other areas of concern focus on the interaction between the components and how different components work together. The guidelines in this page examine different areas of concern that you should consider when designing the architecture of your application.

Key Design Principles

When getting started with your design, keep in mind the key principles that will help you to create an architecture that adheres to proven principles, minimizes costs and maintenance requirements, and promotes usability and extendibility. The key principles are:

Separation of concerns. Divide your application into distinct features with as little overlap in functionality as possible. The important factor is minimization of interaction points to achieve high cohesion and low coupling. However, separating functionality at the wrong boundaries can result in high coupling and complexity between features even though the contained functionality within a feature does not significantly overlap.

Single Responsibility principle. Each component or module should be responsible for only a specific feature or functionality, or aggregation of cohesive functionality.

Principle of Least Knowledge (also known as the Law of Demeter or LoD). A component or object should not know about internal details of other components or objects.

Don’t repeat yourself (DRY). You should only need to specify intent in one place. For example, in terms of application design, specific functionality should be implemented in only one component; the functionality should not be duplicated in any other component.

Minimize upfront design. Only design what is necessary. In some cases, you may require upfront comprehensive design and testing if the cost of development or a failure in the design is very high. In other cases, especially for agile development, you can avoid big design upfront (BDUF). If your application requirements are unclear, or if there is a possibility of the design evolving over time, avoid making a large design effort prematurely. This principle is sometimes known as YAGNI ("You ain’t gonna need it").

When designing an application or system, the goal of a software architect is to minimize the complexity by separating the design into different areas of concern. For example, the user interface (UI), business processing, and data access all represent different areas of concern. Within each area, the components you design should focus on that specific area and should not mix code from other areas of concern. For example, UI processing components should not include code that directly accesses a data source, but instead should use either business components or data access components to retrieve data.

However, you must also make a cost/value determination on the investment you make for an application. In some cases, you may need to simplify the structure to allow, for example, UI data binding to a result set. In general, try to consider the functional boundaries from a business viewpoint as well. The following high level guidelines will help you to consider the wide range of factors that can affect the ease of designing, implementing, deploying, testing, and maintaining your application.

Design Practices

Keep design patterns consistent within each layer. Within a logical layer, where possible, the design of components should be consistent for a particular operation. For example, if you choose to use the Table Data Gateway pattern to create an object that acts as a gateway to tables or views in a database, you should not include another pattern such as Repository, which uses a different paradigm for accessing data and initializing business entities. However, you may need to use different patterns for tasks in a layer that have a large variation in requirements, such as an application that contains business transaction and reporting functionality.

Do not duplicate functionality within an application. There should be only one component providing a specific functionality—this functionality should not be duplicated in any other component. This makes your components cohesive and makes it easier to optimize the components if a specific feature or functionality changes. Duplication of functionality within an application can make it difficult to implement changes, decrease clarity, and introduce potential inconsistencies.

Prefer composition to inheritance. Wherever possible, use composition over inheritance when reusing functionality because inheritance increases the dependency between parent and child classes, thereby limiting the reuse of child classes. This also reduces the inheritance hierarchies, which can become very difficult to deal with.

Establish a coding style and naming convention for development. Check to see if the organization has established a coding style and naming standards. If not, you should establish common standards. This provides a consistent model that makes it easier for team members to review code they did not write, which leads to better maintainability.

Maintain system quality using automated QA techniques during development. Use unit testing and other automated Quality Analysis techniques, such as dependency analysis and static code analysis, during development. Define clear behavioral and performance metrics for components and sub-systems, and use automated QA tools during the build process to ensure that local design or implementation decisions do not adversely affect the overall system quality.

Consider the operation of your application. Determine what metrics and operational data are required by the IT infrastructure to ensure the efficient deployment and operation of your application. Designing your application’s components and sub-systems with a clear understanding of their individual operational requirements will significantly ease overall deployment and operation. Use automated QA tools during development to ensure that the correct operational data is provided by your application’s components and sub-systems.

Application Layers

Separate the areas of concern. Break your application into distinct features that overlap in functionality as little as possible. The main benefit of this approach is that a feature or functionality can be optimized independently of other features or functionality. In addition, if one feature fails, it will not cause other features to fail as well, and they can run independently of one another. This approach also helps to make the application easier to understand and design, and facilitates management of complex interdependent systems.

Be explicit about how layers communicate with each other. Allowing every layer in an application to communicate with or have dependencies upon all of the other layers will result in a solution that is more challenging to understand and manage. Make explicit decisions about the dependencies between layers and the data flow between them.

Use abstraction to implement loose coupling between layers. This can be accomplished by defining interface components such as a façade with well-known inputs and outputs that translate requests into a format understood by components within the layer. In addition, you can also use Interface types or abstract base classes to define a common interface or shared abstraction (dependency inversion) that must be implemented by interface components.

Do not mix different types of components in the same logical layer. Start by identifying different areas of concern, and then group components associated with each area of concern into logical layers. For example, the UI layer should not contain business processing components, but instead should contain components used to handle user input and process user requests.

Keep the data format consistent within a layer or component. Mixing data formats will make the application more difficult to implement, extend, and maintain. Every time you need to convert data from one format to another, you are required to implement translation code to perform the operation and incur a processing overhead.

Components, Modules, and Functions

A component or an object should not rely on internal details of other components or objects. Each component or object should call a method of another object or component, and that method should have information about how to process the request and, if appropriate, how to route it to appropriate subcomponents or other components. This helps to create an application that is more maintainable and adaptable.

Do not overload the functionality of a component. For example, a UI processing component should not contain data access code or attempt to provide additional functionality. Overloaded components often have many functions and properties providing business functionality mixed with crosscutting functionality such as logging and exception handling. The result is a design that is very error prone and difficult to maintain. Applying the single responsibility and separation of concerns principles will help you to avoid this.

Understand how components will communicate with each other. This requires an understanding of the deployment scenarios your application must support. You must determine if all components will run within the same process, or if communication across physical or process boundaries must be supported—perhaps by implementing message-based interfaces.

Keep crosscutting code abstracted from the application business logic as far as possible. Crosscutting code refers to code related to security, communications, or operational management such as logging and instrumentation. Mixing the code that implements these functions with the business logic can lead to a design that is difficult to extend and maintain. Changes to the crosscutting code require touching all of the business logic code that is mixed with the crosscutting code. Consider using frameworks and techniques (such as aspect oriented programming) that can help to manage crosscutting concerns.

Define a clear contract for components. Components, modules, and functions should define a contract or interface specification that describes their usage and behavior clearly. The contract should describe how other components can access the internal functionality of the component, module, or function; and the behavior of that functionality in terms of pre-conditions, post-conditions, side effects, exceptions, performance characteristics, and other factors.

Key Design Considerations

This guide describes the major decisions that you must make, and which help to ensure that you consider all of the important factors as you begin and then iteratively develop your architecture design. The major decisions, briefly described in the following sections, are:

  • Determine the Application Type
  • Determine the Deployment Strategy
  • Determine the Appropriate Technologies
  • Determine the Quality Attributes
  • Determine the Crosscutting Concerns

Determine the Application Type

Choosing the appropriate application type is the key part of the process of designing an application. Your choice is governed by your specific requirements and infrastructure limitations. Many applications must support multiple types of client, and may make use of more than one of the basic archetypes. This guide covers the following basic application types:

  • Applications designed for mobile devices.
  • Rich client applications designed to run primarily on a client PC.
  • Rich Internet applications designed to be deployed from the Internet, which support rich UI and media scenarios.
  • Service applications designed to support communication between loosely coupled components.
  • Web applications designed to run primarily on the server in fully connected scenarios.

In addition, it provides information and guidelines for some more specialist application types. These include the following:

  • Hosted and cloud-based applications and services.
  • Office Business Applications that integrate desktop or client/server technologies.

Determine the Deployment Strategy

Your application may be deployed in a variety of environments, each with its own specific set of constraints such as physical separation of components across different servers, a limitation on networking protocols, firewall and router configurations, and more. Several common deployment patterns exist, which describe the benefits and considerations for a range of distributed and non-distributed scenarios. You must balance the requirements of the application with the appropriate patterns that the hardware can support, and the constraints that the environment exerts on your deployment options. These factors will influence your architecture design.

Determine the Appropriate Technologies

When choosing technologies for your application, the key factors to consider are the type of application you are developing and your preferred options for application deployment topology and architectural styles. Your choice of technologies will also be governed by organization policies, infrastructure limitations, resource skills, and so on. You must compare the capabilities of the technologies you choose against your application requirements, taking into account all of these factors before making decisions.

Determine the Quality Attributes

Quality attributes, such as security, performance, and usability, can be used to focus your thinking on the critical problems that your design should solve. Depending on your requirements, you might need to consider every quality attribute covered in this section, or you might only need to consider a subset. For example, every application design must consider security and performance, but not every design needs to consider interoperability or scalability. Understand your requirements and deployment scenarios first so that you know which quality attributes are important for your design. Keep in mind that quality attributes may conflict; for example, security often requires a tradeoff against performance or usability.

When designing to accommodate quality attributes, consider the following guidelines:

  • Quality attributes are system properties that are separate from the functionality of the system.
  • From a technical perspective, implementing quality attributes can differentiate a good system from a bad one.
  • There are two types of quality attributes: those that are measured at runtime, and those that can only be estimated through inspection.
  • Analyze the tradeoffs between quality attributes.

Questions you should ask when considering quality attributes include:

  • What are the key quality attributes required for your application? Identify them as part of the design process.
  • What are the key requirements for addressing these attributes? Are they actually quantifiable?
  • What are the acceptance criteria that will indicate that you have met the requirements?

Determine the Crosscutting Concerns

Crosscutting concerns represent key areas of your design that are not related to a specific layer in your application. For example, you should consider implementing centralized or common solutions for the following:

  • A logging mechanism that allows each layer to log to a common store, or log to separate stores in such a way that the results can be correlated afterwards.
  • A mechanism for authentication and authorization that passes identities across multiple layers to permit granting access to resources.
  • An exception management framework that will work within each layer, and across the layers as exceptions are propagated to the system boundaries.
  • A communication approach that you can use to communicate between the layers.
  • A common caching infrastructure that allows you to cache data in the presentation layer, the business layer, and the data access layer.

The following list describes some of the key crosscutting concerns that you must consider when architecting your applications:

  • Instrumentation and logging. Instrument all of the business-critical and system-critical events, and log sufficient details to recreate events in your system without including sensitive information.
  • Authentication. Determine how to authenticate your users and pass authenticated identities across the layers.
  • Authorization. Ensure proper authorization with appropriate granularity within each layer, and across trust boundaries.
  • Exception management. Catch exceptions at functional, logical, and physical boundaries; and avoid revealing sensitive information to end users.
  • Communication. Choose appropriate protocols, minimize calls across the network, and protect sensitive data passing over the network.
  • Caching. Identify what should be cached, and where to cache, to improve your application’s performance and responsiveness. Ensure that you consider Web farm and application farm issues when designing caching.

What's Next

After introducing the basic principles of the Systems' Architectures, we can now describe the key features of WebMVC framework that allow you to apply different levels of decomposition to a software application. These are:

The following table reassumes these concepts and gives you a smart report for consulting the features provided by the framework and also show you their applicability scope. We will discuss them in dept in the next pages:

System decomposition levels provided by WebMVC Framework with their applicability scope
level scope
OOP Logic, computational ad reusable parts that cooperates for producing a goal. Typically are basic entities of a system designed with SOLID principles:
  • Single responsibility principle: a class should have only a single responsibility (i.e. changes to only one part of the software's specification should be able to affect the specification of the class)
  • Open/closed principle: software entities … should be open for extension, but closed for modification.
  • Liskov substitution principle: objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program. Also, known as Design by Contract.
  • Interface segregation principle: "many client-specific interfaces are better than one general-purpose interface."
  • Dependency inversion principle: one should "depend upon abstractions, [not] concretions.
SUBSYSTEMS Composing a system looking at its business roles. Typically, subsystems are physical directories collecting classes. Subsystems are mapped into logical namespaces/packages
BUSINESS OBJECTS Whereas a program may implement classes, which typically end in objects managing or executing behaviors, a business object usually does nothing itself but holds a set of instance variables or properties, also known as attributes, and associations with other business objects, weaving a map of objects representing the business relationships. For example, a "Manager" would be a business object where its attributes can be "Name", "Second name", "Age", "Area", "Country" and it could hold an 1-n association with its employees (a collection of Employee instances). WebMVC let you to automatically generate Business ObjectS from MySQL tables .
MVC Decomposing system scenarios (typically web pages) looking at computer concerns. For example data functions, graphic presentation, flow control and so on. Futhermore with WebMVC, the usage of different types of programming languages is broken down amongst the Model, View, Template, and Controller in order to avoid mixing them within a single code file.
HMVC Decomposing system scenarios looking at the widgetization and reuse of their content
LOCALIZATION Decomposing system scenarios looking at their different language translations
CBD Building system recurring aspects by assembling reusable components
ACL and SECURITY Decomposing system scenarios looking at their different access levels and security

Subsystems

Introduction

One of the purposes of WebMVC is to provide tools that allow software developers to take advantage of sound principles when they design and implement complex web applications. An important principle of software engineering is system decomposition that can be used to split out a software system into smaller interacting parts, called subsystems, in order to dominate the system complexity.

Subsystems and namespaces

In WebMVC, the decomposition of a software can be pursued considering several perspectives. We have already seen how the splitting into model, view, and controller can be done. MVC is a canonical architectural pattern that can be applied in a great variety of software applications, and in a certain sense, we can say that the MVC decomposition pattern can be used for many application domains regardless of their structure.
Another decomposition perspective concerns how to split a software with respect to an application domain. Consider, for example, a software system that has the purpose to manage some fundamental functions of an enterprise such as manufacturingand crm(customers relationship management); we call it minierp. After the system design phase made by the software engineer, the subsystems structure can be represented in WebMVC by means of two fundamental concepts strictly related to each other. They are:

  • a hierarchy of directories, by which, we can organize the different source code files used to compose the software application

  • namespaces, groups of related software entities, e.g. classes or interfaces, that each has a unique name or identifier and also a scope in which it was defined

In the broadest definition namespaces are a way of encapsulating items. This can be seen as an abstract concept in many places. For example, in any operating system directories serve to group related files, and act as a namespace for the files within them. As a concrete example, the file foo.txt can exist in both directory /home/greg and in /home/other, but two copies of foo.txt cannot co-exist in the same directory. In addition, to access the foo.txt file outside of the /home/greg directory, we must prepend the directory name to the file name using the directory separator to get /home/greg/foo.txt. This same principle extends to namespaces in the programming world

For each subsystem, WebMVC uses both directories and namespaces:

  • It uses a directory to physically store all its classes, interfaces, functions, and constants.
  • Then it uses a namespace, labeled with the same directory name and path, to load, refer and use each class (or interface, function, etc.) needs to be instantiated and executed;

Naming convention for subsystems and namespaces

WebMVC directories and namespaces must mandatory have

  • identical names
  • identical path
  • they must be conventionally written in lowercase
  • it is also preferable to use shorts and semantically descriptive names.

A practical example of subsystems

For example, if we decide to implement the subsystem manufacturing for the minierp application we previously introduced, we must define:

  1. controllers\manufacturing - the directory path where to store classes for the manufacturing subsystem;
  2. controllers\manufacuring - the namespace that we must use when coding each class of the manufacturing subsystem.

An excerpt of directory structure for the minierp web application is shown in figure 6.1.
We must mandatory use two decomposition levels for defining the directories structure:

  1. the first is the MVC decomposition, the directories controllers, models, views and templates
  2. the second is provided by the directories structure we have for representing the application domain. Important! we have a replica of the application domain directories structure within the models, views, controllers, and templates directory.

Figure 6.1. An excerpt of the static system decomposition of minierp.

In the minierp software, the initial application domain decomposition is made by the subsystems crm, and manufacturing; the latter comprises the class Inventory. Note that the directory controllersis the root directory from which all application controllers for the defined subsystems can be invoked. This is because in WebMVC the directory controllers is the entry point to access application software functionalities. You can also note the replica of application domain directories structure (we did at controllers folder level) within models directory, views, and templates.

How to invoke a subsystem controller class

In the Controller page, we have learned how to invoke a controller from the URL according to the format:

http://site/controller

http://site/controller/method/param1/param2/.../paramn

where the automatic conversion from the URL to the class name works, for example, as follows:

http://localhost/hello_world/say_hello_message/Mark => controllers\UserManager->sayHelloMessage('Mark')

We extend this convention in order to call a controller class located within a subsystem using formats like:

http://site/subsystem/controller/method/param1/param2/.../paramn

http://site/subsystem/.../subsystem/controller/method/param1/param2/.../paramn.

Managing the inventory record

An example taken from the minierp web application concerns the presentation of the inventory records list whose code is located within the subsystem manufacturing/Inventory. It is a simplified version of a software application that aims at the inventory management of a manufacturing industry. The inventory table is taken from the database named minierp and is made of the following attributes:

  • code -> the record key
  • description -> the description of the good maintained in the inventory
  • stock -> quantity in stock

The task to retrieve the inventory records is in charge of the Inventory model:

namespace models\manufacturing;

use framework\Model;

class Inventory extends Model
{
    public function getInventory()
    {
        $this->sql = "SELECT * FROM inventory";
        $this->updateResultSet();
    return $this->getResultSet();
    }
}

Next, the Inventory view class receives the records retrieved by the model and proceeds with the substitution of the template placeholders contained in /manufacturing/inventory with the values taken from the records. The Inventory view takes advantage of the concept of block.

namespace views\manufacturing;

use framework\View;

class Inventory extends View
{
    public function __construct($tplName = null)
    {
        if (empty($tplName))
            $tplName = "/manufacturing/inventory";
        parent::__construct($tplName);
    }

    public function setInventoryBlock(\mysqli_result $resultset){
        $this->openBlock("Parts");
        while ($part = $resultset->fetch_object()) {
            $this->setVar("code",$part->code);
            $this->setVar("description",$part->description);
            $this->setVar("stock",$part->stock);
            $this->parseCurrentBlock();
        }
        $this->setBlock();
    }
}        

The file inventory.html.tpl simply arranges the output in the form of a table. The block named Parts states how the record retrieved by the inventory table will be rendered in output one row at a time by the Inventory view.

`html template <!DOCTYPE html>

Inventory

Inventory

code description stock
{code} {description} {stock}


Finally, the code of the `Inventory controller` coordinates, as usual, the work made by the model and the view. We remark that the function `showInventory()` has to be public; it first invokes the method `getInventory()` from the model, then it passes the result set to the method `setInventoryBlock()` of the view that arranges for the placeholder substitutions with the values contained in the retrieved records.

```php
namespace controllers\manufacturing;

use framework\Controller;
use framework\Model;
use framework\View;

use models\manufacturing\Inventory as InventoryModel;
use views\manufacturing\Inventory as InventoryView;


class Inventory extends Controller
{
    public function __construct(View $view=null, Model $model=null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

   public function showInventory() {
        $inventoryResultSet = $this->model->getInventory();
        $this->view->setInventoryBlock($inventoryResultSet);
        $this->render();
   }

    public function getView()
    {
        $view = new InventoryView("/manufacturing/inventory");
        return $view;
    }

    public function getModel()
    {
        $model = new InventoryModel();
        return $model;
    }
}

Assuming that in the table inventory there are data of components necessary to build a digital mouse, we can get the output typing:

http://localhost/minierp/manufacturing/inventory/show_inventory

Summary

Subsystems allow splitting software with respect to an application domain. After the system design phase made by the software engineer, the subsystems structure can be represented in WebMVC by means of two fundamental concepts strictly related to each other. They are:

  • a hierarchy of directories
  • the namespace
    For each subsystem, WebMVC uses a directory to physically store all its classes and uses a namespace to refer each class when it needs to be instantiated and executed.
    Into WebMVC package you will find different examples of subsystems here

What's Next

In the next section, you will learn how WebMVC let you apply another pattern for decomposing the content of an application

Examples

Subsystem examples

Into WebMVC package you will find different examples of source code located under the examples subfolder. They give you a demonstration of the main WebMVC functionalities, as well as, how to organize software into subsystems. Subsystems of the provided examples are organized as follow:

  • examples - which is the main subsystem
    • about - it contains a simple demonstration of coding a source code information helper
    • cms - this subsystem contains general examples about how showing contents by using WebMVC
    • db - this subsystem contains some DB related examples

Below is the directory/files structure:

.
Rember that, due to the MVC design pattern, you will find a replica of examples folders/files structure also under models, views and, templates folders.

You can run a demo from here

Conclusion

As we just described in the previous page a subsystem, in WebMVC, is identified at two levels:

  1. a physical level, by defining a path in the file system. For example, the pathname examples/cms identify the subsystem cms which is contained into the main subsystem examples;
  2. a logical level, by using namespace in source code and its name must be equal to filesystem pathname. In this case, it is the name examples/cms and must be equal to its pathname. Namespaces are used in source code for structuring and organizing naming conflicts.

So, for each subsystem, WebMVC uses a directory for physically storing of all its classes and uses a namespace to refer each class when it needs to be instantiated and executed; directories and namespaces must have identical names that are conventionally written in lowercase.

In conclusion, you need to take in mind that there are two decomposition levels when writing code with WebMVC:

  • First is the MVC decomposition (directories: controllers, models, views and templates)
  • Second is provided by the application subsystems, that is a folders/files structure you need to replicate within the controllers, models, views, as well as, for the templates directory.
    Also, remember that the directory controllers are the root directory from which all application controllers for the defined subsystems can be invoked. This is because in WebMVC the directory controllers are the entry point to access application software functionalities.

Inside all levels live WebMVC assemblies. An assembly is represented by a logical name you need to choose to identify the aggregation of the classes for Model, View, and, Controller and also for the HTML. Then when you coding classes for Model, View, and, Controller and also for the HTML you can use the same logical name for storing these files.

Content Based decomposition and HMVC

Introduction

You just learned how WebMVC let you decompose an application using subsystems. However, this is not the unique way for organizing software design in an efficient way.
For introducing a new approach for decomposing a system, we consider the previous example, when we showed you the web page for browsing products of an e-commerce application. We discovered how it can dispose of different sections of content (such as navigation bar, status bar, and credits information) could be shared with other pages, such as product detail page, cart and so on.
So, in addition of applying a decomposition by subsystems, we use for logically organize our e-commerce application (we can design subsystems such as the store, orders, users and so on), we can also design the content of a web page by composing it using different and small sections. Also, we could reuse each of these sections when they are shared across different pages.

Designing content decomposition

While subsystems provide an efficient way to apply separation of logical application roles, we can use a feature of WebMVC, named HMVC (Hierarchical MVC), to decompose the application from the point of view of its "Content".
By assuming that the content of a web page, in WebMVC, is obtained as a result of the execution of Controller, we can state:

A content of a generic web page that can be designed by using nested and smaller sections of content can be generated by running nested controllers where each controller is responsible to produce one section of the page content.

HMCV is a built-in feature of WebMVC framework that allows you for nesting Controllers in a very simple way for producing nested contents inside a web page. The standard assembly of Model, View (with Template), and Controller will become layered into a "hierarchy of parent-child MVC layers". The image below illustrates how this works:

Each assembly functions independently from one another. An assembly can request access to another WebMVC assembly via their controllers. Both of these points allow the application to be distributed over multiple locations if needed. In addition, the layering of WebMVC assembly allows for a more in-depth and robust application development. This leads to several advantages:

  • Modularization: Reduction of dependencies between the disparate parts of the application.
  • Organization: Having a composition mechanism of WebMVC assemblies lighter workloads.
  • Reusability: By nature of the design it is easy to reuse nearly every piece of code.
  • Extendibility: Makes the application more extensible without sacrificing ease of maintenance.

These advantages will allow you to get M.O.R.E out of your application with less headaches.

Let's start the e-commerce application design now!
First of all, we simplify it by assuming we need to develop just the products list, the product detail page and by sharing among them a simple navigation bar.
Now, take a look at the following UML diagram we made for representing controller classes relationships:

Note that, just for simplifying, we are only considering controller classes. Models and Views will have a similar design.
The diagram defines:

  • Two primary subsystems illustrated using two boundary rectangles: framework (obtained from WebMVC) and e-commerce (we will use it for encapsulating all e-commerce classes). Also, you must remember that subsystems must be logically encapsulated by using namespaces (in the figure the names preceding colon)
  • Inside the e-commerce subsystem, we design two child subsystems: store and common respectively used for encapsulating the store classes and the common classes we need to reuse across the e-commerce application.
  • Inside store namespace we put ProductsListand ProductDetail controllers for managing their respective pages
  • Inside commonnamespace we put NavigationBarcontroller for managing the content needed for painting a navigation bar
  • Finally, the diagram shows you the following relationships representing pages composition:
    • ProductsList contains a NavigationBar. Look at the composition relationship into the UML diagram.
    • ProductDetails also contains the previous NavigationBar
    • ProductsList, ProductDetail and NavigationBar are controllers extending the base class Controller provided by WebMC

The file system structure for representing subsystems and classes for controllers is shown in the following figure:

Note that you can imagine having moreover subsystems: users, salesand so on.
As shown in the figure below, Models, Views, and Templates will have a mirror structure:

You already knew that the Templates folder will contain the HTML files for GUI.

Coding content decomposition

In the following section, we code controllers, and templates by omitting views and models because they are not relevant for this purpose. Let's start by coding first the ProductsList and NavigationBar writing the following files

  • Controllers:
    • controllers\ecommerce\store\ProductsList.php
    • controllers\ecommerce\common\NavigationBar.php
  • Templates:
    • templates\ecommerce\store\products_list.html.tpl
    • templates\ecommerce\common\navigation_bar.html.tpl

The ProductsList controller:

/**
 * Class ProductsList
 *
 */
namespace controllers\ecommerce\store;

use framework\Controller;
use framework\Model;
use framework\View;
use controllers\ecommerce\common\NavigationBar as NavigationBar;


class ProductsList extends Controller
{
    protected $view;
    protected $model;

    /**
    * Object constructor.
    *
    * @param View $view
    * @param Model $mode
    */
    public function __construct(View $view=null, Model $model=null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

    /**
    * Autorun method is automatically executed after object creation.
    * We do the nesting of the navigation bar.
    *
    * @param mixed|null $parameters Parameters to manage
    *
    */
    protected function autorun($parameters = null)
    {

        $navigation = new NavigationBar();
        $this->bindController($navigation);
    }

    /**
    * Get the View by using an instance of concrete framework\View class.
    * The instance will use the template containing the GUI design stored into
    * ecommerce/store/products_list.html.tpl
    */
    public function getView()
    {
        $view = new View("/ecommerce/store/products_list");
        return $view;
    }

    /**
    * Get the Model by using an instance of concrete framework\Model class.
    */
    public function getModel()
    {
        $model = new Model();
        return $model;
    }
}

Pay attention to the folowing lines inside the `autorun() method:

       $navigation = new NavigationBar();
       $this->bindController($navigation); 

With this two lines of code, we first create an instance of NavigationBar (remember we designed the NavigationBar as a child of the root controller ProducstList). Secondly, we put the instance inside the ProductsList controller simply by binding it into the GUI of the caller controller (ProductsList). The bind method also requires a special placeholder that must be present into the GUI template of ProducstList and the placeholder must be also named equal to the child controller. Look at the following protucts_list.html.tpl template we designed for ProductsList. You will find the placeholder we just described named {Controller:ecommerce\common\NavigationBar}:

<!DOCTYPE html>
<html>
<head>
    <title>eCommerce Demo - Products list</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap core CSS -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen">

</head>
<body>

<div class="container">

    {Controller:ecommerce\common\NavigationBar}

    <h1>This is the products list demo page</h1>
    <hr>

    <table id="" class="table table-hover">
        <thead>
            <tr>
                <th>Product name</th>
                <th>Description</th>
                <th class="text-right">Price</th>
                <th> </th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Mouse</td>
                <td>High quality mouse</td>
                <td class="text-right">30,45</td>
                <td><a href="#">Show detail</a></td>
            </tr>
            <tr>
                <td>Printer</td>
                <td>Laserjet 20 pages/mm</td>
                <td class="text-right">90,00</td>
                <td><a href="#">Show detail</a></td>
            </tr>
            <tr>
                <td>Monitor</t></td>
                <td>LCD 23''</td>
                <td class="text-right">150,00</td>
                <td><a href="#">Show detail</a></td>
            </tr>
        </tbody>
    </table>

</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>

</body>
</html>

The following figure is the static output produced by the GUI template in which you will see the placeholder we designed for nesting the NavigationBar into ProductsList

So to make the example up and running we need also to code the NavigationBar (child) controller:

/**
 * Class NavigationBar
 *
 */
namespace controllers\ecommerce\common;

use framework\Controller;
use framework\Model;
use framework\View;

class NavigationBar extends Controller
{
    protected $view;
    protected $model;

    /**
    * Object constructor.
    *
    * @param View $view
    * @param Model $mode
    */
    public function __construct(View $view=null, Model $model=null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

    /**
    * Autorun method. Put your code here for running it after object creation.
    * @param mixed|null $parameters Parameters to manage
    *
    */
    protected function autorun($parameters = null)
    {

    }

    /**
    * Get the View by using an instance of concrete framework\View class.
    * The instance will use the template containing the GUI design stored into
    * ecommerce/common/navigation_bar.html.tpl
    */
    public function getView()
    {
        $view = new View("/ecommerce/common/navigation_bar");
        return $view;
    }

    /**
    * Get the Model by using an instance of concrete framework\Model class.
    */
    public function getModel()
    {
        $model = new Model();
        return $model;
    }
}

and its GUI template navigation_bar.html.tpl

 <nav class="navbar navbar-default">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">eCommerce Demo</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="">Catalog</a></li>
                    <li><a href="#">Contact us</a></li>
                    <li><a href="#">Cart</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">User Profile <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li class="dropdown-header">Language settings</li>
                            <li><a href="?locale=en">English</a></li>
                            <li><a href="?locale=it-it">Italian</a></li>
                            <li role="separator" class="divider"></li>
                            <li class="dropdown-header">User settings</li>
                            <li><a href="#">Payments</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

The GUI template simply draws:

Finally the following figure shows you the result running https://server/ecommerce/store/products_list and producing HMVC:

Now, just for concluding the e-commerce example we designed in the previous UML diagram, we show you the necessary code for implementing the product detail page. Like the previous ProductsList, ProductDetail will use HMVC for nesting of (the previous and shared) NavigationBar.

The code for ProductDetail controller:

/**
 * Class ProductDetail
 *
 */
namespace controllers\ecommerce\store;

use framework\Controller;
use framework\Model;
use framework\View;
use controllers\ecommerce\common\NavigationBar as NavigationBar;


class ProductDetail extends Controller
{
    protected $view;
    protected $model;

    /**
    * Object constructor.
    *
    * @param View $view
    * @param Model $mode
    */
    public function __construct(View $view=null, Model $model=null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

    /**
    * Autorun method is automatically executed after object creation.
    * We do the nesting of the navigation bar.
    *
    * @param mixed|null $parameters Parameters to manage
    *
    */
    protected function autorun($parameters = null)
    {

        $navigation = new NavigationBar();
        $this->bindController($navigation);
    }

    /**
    * Get the View by using an instance of concrete framework\View class.
    * The instance will use the template containing the GUI design stored into
    * ecommerce/store/products_list.html.tpl
    */
    public function getView()
    {
        $view = new View("/ecommerce/store/product_detail");
        return $view;
    }

    /**
    * Get the Model by using an instance of concrete framework\Model class.
    */
    public function getModel()
    {
        $model = new Model();
        return $model;
    }
}

A very simple GUI template (product_detail.html.tpl) for the product detail ...

<!DOCTYPE html>
<html>
<head>
    <title>eCommerce Demo - Product detail</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap core CSS -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen">

</head>
<body>

<div class="container">

    {Controller:ecommerce\common\NavigationBar}

    <h1>This is the product detail demo page</h1>
    <hr>

</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>
</body>
</html>

Running https://server/ecommerce/store/product_detail you will produce:

Summary

The largest practical benefit of using an HMVC architecture is the widgetization of content structures. An example might be comments, ratings, Twitter or blog RSS feed displays, or the display of shopping cart contents across pages of an e-commerce website. It is essentially a piece of content that needs to be displayed across multiple pages, and possibly even in different places, depending on the context of the main HTTP request.

Traditional MVC frameworks generally don't provide a direct answer for these types of content structures, so people generally end up duplicating and switching layouts, using custom helpers, creating their own widget structures or library files, or pulling in unrelated data from the main requested Controller to push through to the View and render in a partial. None of these are particularly good options, because the responsibility of rendering a particular piece of content or loading required data ends up leaking into multiple areas and getting duplicated in the places it is used.

The HMVC feature of WebMVC has the ability to dispatch sub-requests to a Controller to handle these kinds of responsibilities. In this regard, HMVC is really just a natural byproduct of striving for increased code modularity, re-usability, and maintaining a better separation of concerns. This is the selling point of HMVC provided by the framework.

What's next

In the next section, you will learn how to create a multi-language application.

Decomposition by localization

Introduction

Definitions about internationalization and localization are:

Internationalization

1) Commerce: The growing tendency of corporations to operate across national boundaries. 2) Marketing and Computing: An approach to designing products and services that are easily adaptable to different cultures and languages.

Localization

The practice of adjusting a product's functional properties and characteristics to accommodate the language, cultural, political, and legal differences of a foreign market or country.

Like other frameworks, WebMVC provides support to write software applications aiming at reaching a larger audience by means of internationalization and localization. Definition 2) of internationalization is interpreted in WebMVC how the capability to build, with little effort, the GUI of software in different natural languages. This capability allows the presentation of your application to people of different nations or with specific visualization requirements.

The term localization is the counterpart of internationalization. Having a product or service that is ready for the international market, with the term localization we refer to the process that adapts a product or service to meet the needs of a language, culture, or desired population’s “look-and-feel”. From one side, we can say that the internationalization focuses on the structure of your application because it builds several static contents ready to serve people of different nations or cultures. On the other side, the localization process places a user within a context that is near, familiar, and easy to use. It can be regarded as a dynamic aspect that restricts the wide context of a multilinguistic application to the smaller context suitable for the user. It is trivially to observe that the most important part of a localization process is the translation of a word or a text from one language to another, but localization is a bit more. For example, a message could be written in a completely different manner when we write it for a nation rather than another one, even if the message conveys the same semantics.

Internationalization and Localization How To

These aspects are considered by WebMVC by means of a standard way to build multilanguage applications.

In the following figure, we show an example of WebMVC Assembly, named Localization, that manages a GUI capable to shift the language from a page written in English to a page written in Italian. The showed example is taken from examples we provide with the framework and it is located under examples/cms/localization path of controllers, models, views, and templates directories.

So, by assuming your server is localhost and your project root is webmvcframwork, by running https://localhost/webmvcframework/examples/cms/localization?locale=en the output will be showned in English:

By running https://localhost/webmvcframework/examples/cms/localization?locale=it-t the output will be showned in Italian:

Note we used a URL parameter locale with a given value en or it-it to apply respectively English or Italian translation of the content.

By running https://localhost/webmvcframework/examples/cms/localization, without locale parameter, the output will be showned in the default browser language:

WebMVC manages the technicalities of internationalization/localization providing the folder locales where resource files containing the presentation content can be placed in different subfolders, one for each natural language. Again, the folder locales have to reflect the structure of system decomposition that we made for our project examples/cms/controllers.
The following image shows the files structure for controllers, models, views, templates, and locales

As you can see, insides the folders locales (the blue box) you need to reflect the same system decomposition assigned to the controllers folder (the red box, as well as models, views, and templates).
In the blu box, the directories en and it-it contain the resource files for the translation of the content shown in GUI. In particular, for the welcome page managed by the controller https://localhost/webmvcframework/examples/cms/localization, the following files:

locales/en/application.txt and locales/it-it/application.txt

contain a list of the resource identifiers that will be used to respectively translate, in English or Italian, and that could also be shared among all controllers of your application

whereas the following files:

locales/en/controllers/Localization.txt and locales/it-it/controllers/Localization.txt

contain a list of the resource identifiers that will be used to respectively translate, in English or Italian, the page content managed by the controller controllers/examples/cms/Localization.php.

By now, to show you as the resource identifiers are organized and managed by WebMVC for applying translations, in the following sections, we illustrate the content of all .txt files and of the template file.
Note: we just consider the resource files regarding English because for Italian, as well as for more other languages you need, logic, structure, and principles are the same.

The code for HTM template file: template/examples/cms/localization.html.tpl is:

<!DOCTYPE html>
<html>
<head>
    <title>Localization example</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap core CSS -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen">

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>
    <![endif]-->
</head>
<body>

<nav class="navbar navbar-default">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">{RES:ProjectName}</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li class="active"><a href="">Home</a></li>
                <li><a href="#about">{RES:Contacts}</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{RES:Setting} <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li class="dropdown-header">{RES:LanguageSettings}</li>
                        <li><a href="?locale=en">{RES:English}</a></li>
                        <li><a href="?locale=it-it">{RES:Italian}</a></li>
                        <li role="separator" class="divider"></li>
                        <li class="dropdown-header">{RES:GuiSettings}</li>
                        <li><a href="">{RES:LookAndFeel}</a></li>
                    </ul>
                </li>
                <li><a href="..">{RES:Exit}</a></li>
            </ul>
        </div>
    </div>
</nav>

<div class="container">
    <h1>{RES:Welcome}</h1>
    <p>{BodyMessage}</p>
    <p>{RES:InfoMessage}</p>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>
</body>
</html>

In the above template we used a new type of placeholders defined by the format {RES:PlaceHolderName}.
In the above HTML code they are:

{RES:ProjectName}
{RES:Contacts}
{RES:Setting}
{RES:LanguageSettings}
{RES:English}
{RES:Italian}
{RES:GuiSettings}
{RES:LookAndFeel}
{RES:Exit}
{BodyMessage}
{RES:InfoMessage}
and
{RES:Welcome}

Values for this type of placeholders is automatically computed by the framework by using the following criteria:

  • Determining the current locale language defined by the given URL parameter locale or by the browser default language
    *Appling, to each placeholder, the corresponding value. Any values are phisically stored in an appropriate resource file, which is automatically selected by the framework depending on selected language and by the current running controller.

So, when we running https://localhost/webmvcframework/examples/cms/localization?locale=en WebMVC framework automatically selects the resource file:

locales/en/controllers/examples/cms/Localization.txt, which contains:

#Comment:Translations for controller Localization
ProjectName=MRP Management System
Contacts=Contact us
Setting=Settings
LanguageSettings=Language settings
English=English
Italian=Italian
GuiSettings=GUI settings
LookAndFeel=Look and Feel
Exit=Exit (TOC)
InfoMessage=Click Settings->Language settings to change language. Traslations are placed into "locales" folder.

as well as the file locales/en/applicaton.txt, which contains:

Welcome=Welcome

As you can note, these resource files selected contain the values for RES type placeholders of the template.

For example, the resource identifier InfoMessage is linked to the value ”Message from the localization file: Click Settings->Language settings to change language” that will replace the placeholder {RES:InfoMessage} appearing in the template

Just a bit more of information about the resource file: you can store in the file application.txt differents values that you can be shared among many different controllers or use a specific resource file for a given controller like we made when using Localization.txt for the controller controllers\example\cms\Localizatio.php.

Finally, we show you View and Model source code for Localization example

file models\examples\cms\Localization.php

<?php
namespace models\examples\cms;

use framework\Model;

class Localization extends Model
{

    private $pageBodies;

    public function __construct()
    {
        // Simulate a multi language database
        $bodiesDb = array(
            "it-it"     =>  "Questo è il contenuto della pagina per la lingua italiana.",
            "en"        =>  "This is the page objectContent for english language",
        );

        $this->pageBodies = $bodiesDb;
    }

    /**
     *
     * Gets page body
     * @param string $locale string identifying LCID
     * @return string
     */
    public function getBody($locale)
    {

        if (isset($_REQUEST[LOCALE_REQUEST_PARAMETER])) {
            $locale = $_REQUEST[LOCALE_REQUEST_PARAMETER];
        }
        return $this->pageBodies[$locale];
    }
}

file views\examples\cms\Localization.php

<?php

namespace views\examples\cms;

use framework\View;

class Localization extends View
{

    /**
    * Object constructor.
    *
    * @param string|null $tplName The html template containing the static design.
    */
    public function __construct($tplName = null)
    {
        if (empty($tplName))
            $tplName = "/examples/cms/localization";
        parent::__construct($tplName);
    }

    /**
    * Sets value for BodyMessage placeholder
    *
    * @param mixed $value
    */
    public function setVarBodyMessage($value)
    {
        $this->setVar("BodyMessage",$value);
    }

}

Skills and technologies decomposition

Introduction

The architecture of WebMVC is designed by following the Separation of Concerns (SOC) design principle. In the next section, we discuss the advantages deriving from SOC as well as by focusing on all possible organizational benefits due to the technologies separation provided by the framework.

SOC advantages

In general, SOC is a design principle for separating a computer program into distinct sections, such that each section addresses a separate concern. A concern is a set of information that affects the code of a computer program. A concern can be as general as the details of the hardware the code is being optimized for or as specific as the name of a class to instantiate. MVC, which is a SOC implementation, is a software architectural pattern for user interfaces that divides an application into three interconnected parts. This is done to separate internal representations of information (Model) from the ways information is managed (Controller) and then presented (View) to, and accepted from, the user.

The value of separation of concerns is simplifying development and maintenance of computer programs. When concerns are well-separated, individual sections can be reused, as well as developed and updated independently. Of special value is the ability to later improve or modify one section of code without having to know the details of other sections, and without having to make corresponding changes to those sections.

Organizational benefits

In general, organizational benefits usually derive from a better organization of work through specialization and coordination. Specialization concerns the division of a work into smaller parts and their assignment to specialized workers. This is a standard practice in modern software development methodologies where the work can proceed in parallel in relatively little increments assigned to software developers and testing activities can be pursued as soon as possible. However, the specialization that comes from the division of work also requires coordination because what has been broken by specialization (subsystem decomposition) has to be reconducted to unity by coordination (system integration).

Effective advantages with WebMVC

The advantages deriving from the division of work are possible when we decide to use the MVC architectural pattern provided by WebMVC. Furthermore, the technologies used in each MVC layer are homogeneous (e.g. HTML and Javascript for the Template, PHP for the View and Controller, and PHP with SQL for the Model part). The code reflects the separation of the professional skills necessary to deal with the various aspects of development as shown in the following table. The code is easier to design, implement, verify and maintain, and you can use pre-existing code where appropriate (e.g. by using a pre-built website template). The same information can be presented in several ways (e.g. textual/graphics) and on different devices. Note that the Controller assumes the role of coordinator of View and Model and this easy the system integration activity necessary to build a software system perceived as a unity.

Table Separation of skills and technologies for the development of Web MVC applications.

With WebMVC, the usage of different types of programming languages is broken down amongst the Model, View, Template, and Controller in order to avoid mixing them within a single code file.

Whats next

In the next sections we speak about an important feature of WebMVC that emphasizes the separation of concerns: Component Based Development

Component Based Development

Introduction

Components-based development (CBD), is a branch of software engineering that emphasizes the separation of concerns with respect to the wide-ranging functionality available throughout a given software system. It is a reuse-based approach to defining, implementing and composing loosely coupled independent components into systems. This practice aims to bring about an equally wide-ranging degree of benefits in both the short-term and the long-term for the software itself and for organizations that sponsor such software. (from wikipedia)

In Sommerville), you can find a discussion about the benefits of component-based software engineering. The following definition of software component is due to Szyperski:

A software component is a Unit of composition with contractually-specified interfaces and explicit context dependencies only. A software component can be deployed independently and is subject to composition by third parties.

A widely used definition is due to UML:

“A modular part of a system, that encapsulates its content and whose manifestation is replaceable within its environment. A component defines its behavior in terms of provided and required interfaces".

A component may be replaced by another if and only if their provided and required interfaces are identical. This idea is the underpinning for the plug-and-play capability of component-based systems and promotes software reuse.

WebMVC Components: Components designed for the web

For recurring problems occurring during the implementation of data-intensive applications, WebMVC provides software components that can be reused to easy software development. Framework’s components, in fact, realize common Aspects that can occur, in a similar way, into different web applications. Many of these aspects are regarding the database, for example, data listing, data listing with sorting, data listing with filtering, data listing with pagination, record management with common table’s operations regarding select, insert, delete and update operations. The framework offers a set of pre-built components for implementing the necessary server logic for these common database management aspects.

You must keep in mind that, unlike a canonical component that implements a reusable behavior, a component designed for the web must possibly also consider the graphic layout that it will have to expose on the GUI. For this reason, WebMCV components are equipped with specific functions to manage this need, and there are two major advantages for using them:

  1. The first is because each WebMVC component is designed like an MVC Assembly. In fact, it assembles a Controller, Model, View, and a Template. So a developer can easily customize its GUI just by updating its Template according to the needs of the application. The component internal logic will remain fully reusable without the need for any source code modifications.

  2. Because components are designed like MVC assemblies, developers can also use and aggregate many of them into one parent controller for the purpose of building a complex application page regarding database management. In the provided examples you will find a typical DB application implemented here (controllers\examples\db\PartListManager) that uses different components assembled into one root Controller.

Conclusion

Now you learned about the roles and goals of the components, in the this section you will find a detailed description for all WebMVC components

Role-based decomposition

Overview

Role-based decomposition is a model for designing security and concurrency in object-oriented software development environments. In the software development process, the target software system consists of a large number of components closely related with one another. This results in potential operation conflicts in security, as well as, cooperative works among the project members. This problem can be much reduced by decomposing the target software system into the relatively independent assemblies where each one incorporates a role-based decomposition model to separate operation conflicts frequently occurring during collaboration. So, a basic WebMVC assembly is also designed for managing role-based access control.
We will' discuss the implementations for Security and Role-based in the next sections.

Security

Introduction

WebMVC provides different levels of application security.
Click the following links for details of each one:

  1. RBAC
  2. Authentication
  3. ACL
  4. Escaping and Validation
  5. CSRF and XSS
  6. Encryption
  7. Securing your application files

RBAC

WebMVC uses a security model known as RBAC, "Role Based Access Control".
RBAC establishes that:

a) Every user must be authenticated, identified, and assigned to an application role (i.e. admin, user, power user, and so on).
b) Afterward, a user, after logging in, can access only web pages that were designed to have restricted access only to their role.
c) Roles can (or not) restrict user access to web pages. They can also restrict database record operations, like INSERT, UPDATE, or DELETE.

WebMVC, lets you implement a),b) and, c) by providing you services for:

1) Defining the database tables in which to store: users, credentials, roles (access_level), and assignment of a role to users 2) Implementing a login mechanism for authenticating and identifying users. 3) Implementing a mechanism to establish that the access for execution of given MVC assemblies is allowed only to an authenticated user and/or who has the appropriate role. 4) Limiting database record operations depending on user role

Database tables for storing users, credentials, roles, and assignment of a role to users.

You can use the sql\rbac.sql script to automatically create these tables. The generated tables will also contain sample data for users and roles. In the example, data access_level = 100 is for Administrators while 60 is for Managers. User credentials will be given by its email and password and password will be optionally stored by using md5 or password_hash one-way encryption algorithm. In sample data, all md5 passwords are equal to "password".
The figure below shows you the tables diagram:

While tables are used to store the RBAC information regarding users and roles, the framework\User class provides you with all methods and helpers for handling user status every time you need and from everywhere (inside of a controller, model, or view). Specifically, the framework/User.php class will use tables and data for implementing the following methods that you can/must use during the development of your MVC classes for managing user authentication, login, logout, and so on (read comments):

First of all "getter" methods:

<?php

    /**
     * Gets user ID. The id is the primary key
     *
     * @return int
     */
    public function getId()

    /**
     * Gets user email. Used for credential
     *
     * @return string
     */
    public function getEmail()

    /**
     * Gets user password. Used for credential
     *
     * @return string (Optionally encrypted by md5 algo)
     */
    public function getPassword()

    /**
     * Gets user role
     *
     * @return int The number identifying the user role
     */
    public function getRole()

Here are some useful methods to manage user login and logout:

<?php

    /**
     * Login user
     *
     * @param string $mail User email
     * @param string $password User password
     *
     * @return bool True if login ok, else false
     */
    public function login($email, $password)

    /**
     * Logout user
     *
     * @return bool
     */
    public function logout()

    /**
     * Checks if a user is successfully logged in
     *
     * @return bool True if logged in, else false
     */
    public function isLogged()

    /**
     * Checks if a user is logged in. If false it redirects to a custom link used for showing
     * the login form and requiring authentication. If true it redirects to a custom link.
     *
     * @param null|string $redirect
     *                The Controller URL for redirecting when the user is not logged in.
     *                If null it automatically redirects to the default login page.
     * @param null|string $returnLink
     *                The return link is to be used for redirecting if the user is successfully logged in.
     *                If null it (still) will be the default login page
     * @param null|string $LoginWarningMessage
     *                A custom warning message to show in the login form after
     *                unsuccessful login
     *                If null it will be the default message
     *
     */
    public function checkForLogin($redirect=null, $returnLink=null, $LoginWarningMessage=null)

    /**
     * Auto-login by using Cookies
     *
     * @uses ChiperService
     * It uses the ChiperService class to decrypt Cookie
     */
    public function autoLoginFromCookies()

If you prefer to define and use your custom tables for storing and managing users and roles you must instruct WebMVC how to find tables for getting information. In this case, it will be necessary to modify the file config/security.config.php (please read comments)

<?php

/**
 * security.config.php
 *
 * Main application security configuration parameters.
 * You can change those values according to your security
 * MySQL environment or Chiper preferences
 */

/**
 * Defines the constants for MySQL database User table.
 * Class User uses this information.
 */

/**
 *  Constant for the User MySQL Table
 */
define("USER_TABLE", "user");

/**
 *  Defines a constant for INT Primary Key field of the User MySQL Table
 */
define("USER_ID","id_user");

/**
 *  Defines a constant for the UNIQUE email field of the User MySQL Table
 *  Email is used as the credential
 */
define("USER_EMAIL","email");

/**
 *  Defines a constant for the password field of the User MySQL Table
 *  Password is used as the credential
 */
define("USER_PASSWORD"," password");

/**
 *  Defines a constant for the role field of the User MySQL Table
 *  User role defines access levels criteria managed by RBAC Engine
 */
define('USER_ROLE', 'id_access_level');

/**
 *  Defines a constant for Administrator role id
 *
 */
define('ADMIN_ROLE_ID', 100);

/**
 *  Defines a constant for the enable field of the User MySQL Table
 *  User enable field can temporarily disable users.
 *  Leave blank the value for USER_ENABLED if you don't want to manage the enabling/disabling of users.
 *  Note: User enable database field value must be 1 or -1
 */
define('USER_ENABLED', 'enabled');

Using RBAC

You can protect access and execution of a Controller by using RBAC features. To do this the abstract Controller class of WebMVC provides you with the following methods:

  /**
     * Restricts on RBAC. User role must have a role contained into RBACL.
     *
     * @param string $redirect The Controller URL path to redirecting when access is denied.
     *                         If null it redirects to the default login page.
     * @param null|string $returnLink The return link after logging in with the default login page
     * @param null|string $LoginWarningMessage A custom warning message to show
     * @return User
     */
    protected function restrictToRBAC($redirect = null, $returnLink = null, $LoginWarningMessage = null)


    /**
     * Restricts access only to authenticated users
     *
     * @param string $redirect The Controller URL path to redirecting when the user is not logged in.
     *                         If null it redirects to the default login page.
     * @param null|string $returnLink The return link after logging in with the default login page
     * @param null|string $LoginWarningMessage A custom warning message to show
     * @return User
     */
    protected function restrictToAuthentication($redirect = null, $returnLink = null, $LoginWarningMessage = null)


   /**
     * Grants a user role for access
     *
     * @param int $role number
     */
    protected function grantRole($role)
    {
        $role = (int)$role;
        $this->roleBasedACL[] = $role;
    }

So you can restrict access and execution of a Controller only to authenticated users or to users having a specific role.

In controller\examples\cms\InnerBlocks you can find an example of how to implement RBAC. The following code section illustrates how the access restriction can be implemented into the __construct class constructor.

    /**
    * Object constructor.
    *
    * @param View $view
    * @param Model $mode
    */
    public function __construct(View $view=null, Model $model=null)
    {
        $this->grantRole(100);  // Administrator
        $this->grantRole(60);   // Manager (see access_level table)
        $this->restrictToRBAC(null,"examples/cms/inner_blocks");
        // Alternatively, you can limit access to only the authenticated user, regardless of role
        // $this->restrictToAuthentication(null,"examples/cms/inner_blocks");

        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

It means that just by writing the first three lines of code you restricted the execution only to Administrator (role 100) and Manager (60). So if you try to access to: http://localhost/examples/cms/inner_blocks the response will be:

  • If you never logged in before, then you will be redirected to the login Controller/Page to specify your credentials
  • After authentication (or if you are previously authenticated) framework checks your credentials and, then if you have an appropriate role granted into controller\examples\cms\InnerBlocks (defined with the first two lines of code into the constructor)
  • If you successfully match the role then the framework lets you access and execute controller\examples\cms\InnerBlocks, otherwise you will be redirected to the login page and a login warning message will be shown (the message text is an optional parameter of restrictToRBAC and restrictToAuthentication)

Finally, If you just want to restrict access to authenticated users, regardless of role/access_level assigned to each user, you can use restrictToAuthentication method in a similar way described before.

Limiting database record operations depending on user role

To evaluate the user role you can simply use the "getRole" method of "framework\User" and thus determine the correct database operation that you need to implement and that is required by your application. Below is an example of code that you can, arbitrarily, put into a Model (as well as Controller or View) to illustrate how to implement a DB operation depending on the role of the current user logged in

// ...some code before
$user = new framework\User();
$userRole = $user->getRole();
if ($userRole == 100) {
 // custom code to allow add or update a record
}
// custom code to read a record

Using components

The components provided by the framework are instances of the abstract class framework\components\Component, in turn, an instance of the abstract class framework\Controller; therefore, a component is a full-fledged controller.
In the next page, we start by showing the first and simple webMVC Component: the DataRepeater

DataRepeater

The simpler component made available by WebMVC is framework\components\DataRepeater to easy the displaying of data coming from a given source. Two possible scenarios where the DataRepeater can be conveniently used are when:

  • a list of records from a database
  • or data stored in an array

must be provided in the output according to a given visualization structure.

In the example above example, implemented by coding the SimpleDataRepeater assembly, we show how an instance of framework\components\DataRepeater will provide data repetition from different data sources, array and DB, and how you can use it.

First of all the code for templates\simple_data_repeater.html.tpl where a Block Parts is designed to renders data.

<!DOCTYPE html>
<html>
<head>
    <title>DataRepeater component</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap core CSS -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>

</head>
<body>

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>
</body>
<div class="container-fluid">
    <h2>Part list example using the DataRepeater component</h2>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th>Part code</th>
                        <th>Description</th>
                    </tr>
                </thead>
                <tbody>
                     <!-- BEGIN Parts -->
                     <tr>
                        <td>{part_code}</td>
                        <td>{description}</td>
                    </tr>
                    <!-- END Parts -->
                </tbody>
            </table>
        </div>
    </div>
</div>
</html>

Here is the simple views\SimpleDataRepetaer.php

<?php
namespace views;

use framework\View;

class SimpleDataRepeater extends View
{

    /**
    * Object constructor.
    *
    * @param string|null $tplName The html template containing the static design.
    */
    public function __construct($tplName = null)
    {
        if (empty($tplName))
            $tplName = "/simple_data_repeater";
        parent::__construct($tplName);
    }

}

Below we show the models\SimpleDataRepetaer.php in which we coded two methods getPartsFromArray() and getPartsFromDB() (read code comments) providing respectively data from array and DB. You will find the DDL of the table part here (lines from 171 to 198)

<?php

namespace models;

use framework\Model;

class SimpleDataRepeater extends Model
{

    public function getPartsFromArray()
    {

        $users = array(
            array('part_code' => '1', 'description' => 'description 1'),
            array('part_code' => '2', 'description' => 'description 2'),
            array('part_code' => '3', 'description' => 'description 3'),

        );
        return $users;
    }

    /**
     * Provides parts from table part
     *
     * @return \mysqli_result
     * @note  resultset must be in a proper keys and number of columns
     * required by the Block which is used by the Datarepeater
     * component.
     */
    public function getPartsFromDB()
    {
       $this->sql = "SELECT part_code,description FROM part";
       $this->updateResultSet();
    }

}

Finally the code for controllers\SimpleDataRepeater

<?php

namespace controllers;

use framework\components\DataRepeater;
use framework\Controller;
use framework\Model;
use framework\View;
use models\SimpleDataRepeater as SimpleDataRepeaterModel;
use views\SimpleDataRepeater as SimpleDataRepeaterView;

class SimpleDataRepeater extends Controller
{
    protected $view;
    protected $model;

    /**
     * SimpleDataRepeater constructor.
     *
     * @param View|null $view
     * @param Model|null $model
     * @throws \framework\exceptions\TemplateNotFoundException
     */
    public function __construct(View $view=null, Model $model=null)
    {
        $this->view = empty($view) ? $this->getView() : $view;
        $this->model = empty($model) ? $this->getModel() : $model;
        parent::__construct($this->view,$this->model);
    }

    /**
    * Autorun method. Put your code here for running it after object creation.
    * @param mixed|null $parameters Parameters to manage
    *
    */
    protected function autorun($parameters = null)
    {
        $this->useArray();
        // $this->useDB();
        // $this->manualSetupArray();
        // $this->manualSetupDB();
    }

    /**
     * DataReapeter smart initialization and usage with array values
     *
     * @throws \ReflectionException
     * @throws \framework\exceptions\NotInitializedViewException
     * @throws \framework\exceptions\TemplateNotFoundException
     * @throws \framework\exceptions\VariableNotFoundException
     */
    public function useArray(){
        $parts = $this->model->getPartsFromArray();
        $repeater = new DataRepeater($this->view,null,"Parts",$parts);
        $this->bindComponent($repeater);
    }


    /**
     * DataReapeter smart initialization and usage with DB values
     *
     * @throws \ReflectionException
     * @throws \framework\exceptions\NotInitializedViewException
     * @throws \framework\exceptions\TemplateNotFoundException
     * @throws \framework\exceptions\VariableNotFoundException
     */
    public function useDB() {
        $this->model->getPartsFromDB();
        $repeater = new DataRepeater($this->view,$this->model,"Parts",null);
        $this->bindComponent($repeater);
    }


    /**
     *  DataReapeter manual initialization and usage with array values
     */
    public function manualSetupArray(){
        $parts = $this->model->getPartsFromArray();
        $repeater = new DataRepeater();
        $repeater->setView($this->view);
        $repeater->setContentToBlock("Parts");

        $repeater->setValuesFromArray($parts);
        $repeater->render();
    }

    /**
     *  DataReapeter manual initialization and usage with DB values
     */
    public function manualSetupDB(){
        $this->model->getPartsFromDB();
        $repeater = new DataRepeater();
        $repeater->setView($this->view);
        $repeater->setModel($this->model);
        $repeater->setContentToBlock("Parts");

        $repeater->setValuesFromModel();
        $repeater->render();
    }



    /**
    * Initialize the View by loading the static design of /simple_data_repeater.html.tpl
    * managed by views\SimpleDataRepeater class
    *
    */
    public function getView()
    {
        $view = new SimpleDataRepeaterView("/simple_data_repeater");
        return $view;
    }

    /**
    * Initialize the Model by loading models\SimpleDataRepeater class
    *
    */
    public function getModel()
    {
        $model = new SimpleDataRepeaterModel();
        return $model;
    }
}

In the above code, you will find the following methods. Each one uses different (optionally) parameters to instantiate the DataRepeater component:

                           new DataRepeater( [View] , [Model] , [Template Block], [Array] )
  • useArray(): It automatically renders array data into the Block labeled Parts.

    new DataRepeater($this->view,null,"Parts",$parts)

  • useDB(): It automatically renders model data into the Block labeled Parts.

    new DataRepeater($this->view,$this->model,"Parts",null)

  • manualSetupArray(): It manually renders array data into the Block labeled Parts by manually setting the DataRepeater Instance.

  • manualSetupDB(): It manually renders model data into the Block labeled Parts by manually setting the DataRepeater Instance.

Please read the code comments and implementations for technical details regarding the methods above. Let only one of them be uncommented inside the autorun method for showing its output when running SimpleDataRepeater.