-
Notifications
You must be signed in to change notification settings - Fork 16
A fully functioning DB application
The purpose of this example is to provide a simple but fully functioning customer management application, and also to exposing all basic concepts of WebMC Framework we discussed until now:
- Controller
- Model
- View
- Templates
- Placeholders and Blocks
- OOP
- MVC Assembly
- Database and ORM using autogenerated Database Bean
The Systems Requirements of our system are the following:
The user of the CRM application 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
We organized the System Design under the following sections:
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 normal forms](https://en.wikipedia.org/wiki/Database_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.
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 only to be only shown in particular circumstances. 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 mode or editing.
Finally, we provide the Application Design for organizing system development. We define the following two Web MVC Assemblies for implementing the application :
- `` for browsing customers
-
CustomerRecord
for customer data manipulation.
Because the system is very simple we do not have the need for any Subsystems Design
The implementation details of both assemblies will be exposed in the section below
The following sections contain the template and MVC source code of 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> Edit</a>
<a class="btn btn-warning" href="mailto:{CustomerEmail}"> <i class="fa fa-address-card-o"></i> 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> 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 features