Author: Cyril Ogana
Viewers: 3,597
Last month viewers: 49
Categories: PHP Tutorials, Sponsored
Read this tutorial article to learn how to quickly build framework based PHP applications with Codelobster IDE.
Introduction
Web Frameworks and Content Management Systems allow developers to heavily leverage on tried and tested modules and design patterns allowing the developer to concentrate on the application logic.
However, despite PSR (PHP Standards Recommendations), each language framework has some tacit standards and design methods which are unique. When developing on such platforms, an IDE that supports your frameworks logic and flow will heavily gear your efficiency for the better.
IDEs are a very focal part of software development. Fully fledged platforms of their own offering useful features that heavily influence developer productivity and code quality as well. Today I am using a portable PHP IDE named Codelobster to achieve good productivity when developing applications with well known frameworks.
Download Codelobster
Codelobster is a portable integrated development environment (IDE) primarily for PHP, which also supports HTML, CSS, and JavaScript development. Plug-ins are available for Drupal, WordPress, Smarty, Joomla, JQuery, Facebook, Codeigniter, Yii, and CakePHP.
It is available for free trial. I have just downloaded version 5.8 from the CodeLobster web site. There is also a Pro version available with a complete set of plugins.
Creating a Symfony Project
For this tutorial we shall be developing on Codelobster a simple personal budgeting application based on Symfony 2 framework.
When you are using the Pro version of CodeLobster, all Plugins are available to you. When using the Lite or Free version, you can opt to purchase the specific plugin for your project.
On the CodeLobster Plugin menu, select Symfony >> Create Project.
The wizard screen will then take you through a series of forms to gather more information about your Symfony project.
1) Enter the Project name, project location and base URL of your project. You have the option to toggle between creating a new folder as well.
2) Choose the Symfony version you are using. We shall use version 2.8 for this project.
3) Choose the ORM you are using. We shall use Doctrine for this project. You can also toggle between having the application generate front end and/or back end code for you. We choose to opt out and handle our programming ourselves.
4) Select a database and enter the connection parameters both for DB administration and for the application. We are using MySQL / MariaDB / Percona for this example. Create a database called budget, whose assets we shall create later in this article.
5) Finalize the process by leaving the default path as localhost (ammend as per your implementation) as well as the default database port for MySQL blank (unless you are using a different port from 3306, no need to update it).
CodeLobster will take a while to download the Symfony framework and run the installer using your provided settings. Once it has completed running the install process, we can begin our Symfony development.
You may want to configure the path to the Symfony console via Tools >> Preferences >> Symfony >> Settings. The path to Symfony console is in the app directory of your project.
On your command line navigate to your project root and start the In-Built Symfony console which runs PHP's internal Web Server for use in development - php app/console server:run - Your development web app should now be accessible on browser via http://127.0.0.1:8000 where you can preview your app while you build it.
Our Application Requirements
Our personal budgeting application shall be very simple. See the requirements below:
1) The user will enter their desired monthly budget based on common personal expenditure categorizations. For simplicity, the model will not factor in rolling up and down of expenditure categories (e.g. rent, groceries rolling up to household expenditure). We will treat each expense category individually.
2) Ideally, the personal budget may be updated monthly after review.
3) The budget report shall be available on demand to be run on the system.
4) An amber alert shall be generated by the system when the monthly expenditure is within a margin of 15% to breach for a particular expenditure category.
5) A red alert shall be created for when the system has actually reached the category limit
These are simple requirements for the sake of the tutorial. In reality, we would add some more functionality such as the aforementioned rolling up of expenditure categories as well as multi-currency support.
We will also not add other features that can make the app usable in real world, such as choosing date ranges for the expenditure vs budget when running the report. A simple Process diagram is shown below:
Creating The Database Assets
Our database shall consist of two tables: one for budget input, one for expenditure input; as well as one view, for our report generator. On CodeLobster, click on Tools >> MySQL >> SQL Manager.
1) Enter your database host and authentication parameters. The database we are connecting to is named budget.
2) Your connection should appear in the SQL window. Right Click on it and Click Connect.
3) Right click the connection and click "Create Table"
The assets we require are defined in SQL as below. If you choose to type SQL as is, you can do it via CodeLobsters SQL Editor. Right click your connection and click "New SQL File". You will be aided by the SQL Keyword and Asset hints in this the SQL editor.
Note that we actually have 3 views but we need one in our logic layer. The other two are necessary because MySQL does not support SELECT statements in the FROM clause of a view, thus we create two views as feeders to the one view that we want for our report; which will intersect data from the budget and expenditure views.
We shall name the main view v_budget_vs_expenditure. I shall not go into detail of explaining the View SQL though the concept is that for budget table, each expenditure category e.g. Entertainment must pick the latest entry added i.e. pick it as an addendum to previous entries, while the expenditure view must sum expenditures under a category to get period expenditure for the category.
The two data-sets thus contain one occurrence per category, but for different reasons. The report view thus simulates an "OUTER JOIN" to create the final report data.
CREATE TABLE `budget_register` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`description` varchar(90) NOT NULL DEFAULT '',
`date_from` DATETIME NOT NULL,
`value` decimal(16,4) unsigned NOT NULL DEFAULT '0.0000',
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `expenditure_register` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`description` varchar(90) NOT NULL DEFAULT '',
`date_from` DATETIME NOT NULL,
`value` decimal(16,4) unsigned NOT NULL DEFAULT '0.0000',
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Summarize the Budget*/ CREATE VIEW `budget_view` AS
SELECT `t0`.`description` AS `description`, `t0`.`date_from` AS `date_from`, `t0`.`value` AS `budget_value` FROM ( `budget`.`budget_register` `t0` LEFT JOIN `budget`.`budget_register` `t1` ON(((`t0`.`description` = `t1`.`description`) AND (`t0`.`id` < `t1`.`id`))))
WHERE isnull(`t1`.`id`)
/*Summarize the Expenditure*/ CREATE VIEW `expenditure_view` AS
SELECT `exp_reg`.`description` AS `exp_description`, `exp_reg`.`date_from` AS `exp_datefrom`, sum(`exp_reg`.`value`) AS `expenditure_value` FROM `budget`.`expenditure_register` `exp_reg` GROUP BY `exp_reg`.`description`
/*Our Report View (merge data simulate OUTER JOIN)*/
(SELECT `t0`.`date_from` AS `date_from`, `t0`.`description` AS `description`, `t0`.`budget_value` AS `budget_value`, `t1`.`expenditure_value` AS `expenditure_value` FROM (`budget`.`budget_view` `t0` LEFT JOIN `budget`.`expenditure_view` `t1` ON((`t0`.`description` = `t1`.`exp_description`)))) UNION (SELECT `t0`.`date_from` AS `date_from`, `t0`.`description` AS `description`, `t0`.`budget_value` AS `budget_value`, `t1`.`expenditure_value` AS `expenditure_value` FROM (`budget`.`expenditure_view` `t1` RIGHT JOIN `budget`.`budget_view` `t0` ON((`t0`.`description` = `t1`.`exp_description`))))
Creating The Application Controllers
Symfony allows us to leverage on the MVC design pattern, enabling to implement our application cleanly. We require the following controllers for our application.
1) Index (homepage)
2) Budget controllers for Create, Read, Update, Delete
3) Expenditure controllers for Create, Read, Update, Delete
4) Report generator controller
Index Controller
We would like the landing page to be a dashboard type page containing links to the other functions provided by the application.
We shall be using Twig, Symfony's templating system, which if we could describe in two words would be simple and powerful.
You would need to see Symfony documentation to learn more about Twig. We may have also used PHP templates for this.
Open for editing the DefaultController of your application via the CodeLobster Project Window; Src >> AppBundle >> Controller >> DefaultController.php
Our indexAction controller should be edited as below:
/**
* @Route("/", name="homepage")
*/
public function indexAction( Request $request )
{
// replace this example code with whatever you need
return $this->render( 'default/index.html.twig', array(
'base_dir' => realpath( $this->container->getParameter( 'kernel.root_dir' ).'/..'),
));
}
In the PHPDoc comments section, we have added a route to this controller using the annotation @Route. We are routing to our index (/), and naming the annotation homepage for reference in other parts of the application.
We may also have added routing configuration using YAML or XML configurations. See Symfony documentation for more. The controller returns the call $this->render(...) which itself returns a Response object that gets parsed to HTML. The Symfony Response object encapsulates actual HTTP Response in an OOP implementation.
Note that we are rendering our template named index.html.twig, which by default we shall locate in directory app/Resources/views/default
The Twig template for our index is presented below.Twig has a large syntax. Our template contains native HTML as well in most parts. CodeLobster gives HTML hints for elements as well as attribute names.
CSS, JavaScript and Angular hinting is also available out of the box. You may also choose to create your HTML elements with the HTML toolbar, which I find useful when I am entering HTML special characters. You can enable the HTML toolbar (or any other toolbar) via View >> Windows menu link on CodeLobster.
Our Index Twig Template:
{% extends 'base.html.twig' %}
{% block body %}
<div id="header" style="width:100%; text-align: center"><p style="font-family: verdana,tahoma; font-size:35px; font-style: bold">BUDGET 101 MINUS 1</p><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">"Budgeting is an important component of financial success. It's not difficult to implement, and it's not just for people with limited funds. Budgeting makes it easier for people with incomes and expenses of all sizes to make conscious decisions about how they'd prefer to allocate their money", Investopedia</p></div>
<div id="container" style="width:100%;" >
<div id="budget_icon" style="float:left; text-align: center; width:30%">
<img src="{{ asset( 'img/icons/budget_icon.png' ) }}" style="width: 180px; height: 180px"/><br /><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">Budget Register</p>
</div>
<div id="expenditure_icon" style="float:left; text-align: center; width:30%">
<img src="{{ asset( 'img/icons/expenditure_icon.png' ) }}" style="width: 180px; height: 180px"/><br /><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">Expenditure Register</p>
</div>
<div id="report_icon" style="float:left; text-align: center; width:30%">
<img src="{{ asset( 'img/icons/report_icon.png' ) }}" style="width: 180px; height: 180px"/><br /><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">Report Generator</p>
</div>
</div>
<div id="footer" style="width:100%; text-align: center; clear:left;"><br /><br /><hr /><p style="font-family: verdana, tahoma; font-size: 12px; font-style: italic;">©Inspector Budget Inc 2015</p></div>
{% endblock %}
{% block stylesheets %}
{% endblock %}
We have made a some calls to the template function {{ asset... }} which refers to world-readable content saved in your web directory of the project.
You should save your icons in the web directory as per the template. Navigating to this directory is possible via CodeLobster. Right click the particular project directory and click Windows File Explorer to explore the directory and transfer appropriate content to it. You may view our simple index page on the attached images in this article.
Refactoring The Template Design
Code reuse is an important principle of software development. Our budget application will require that we have the header and footer section in each view, and that the links to the various views be included in the header section.
Thus we create a header and footer template in the app/Resources/views/ directory which we shall then embed to the base.html.twig template that will allow us to propagate the header and footer to each view that extends the base via {% extends 'base.html.twig' %} block.
Because a template that extends another cannot implement html tags, we shall use the {%i include %} block when we face this scenario, such as in the form views.
We add the below templates for the header and footer blocks, aptly named header.html.twig and footer.html.twig.
header.html.twig (note addition of intended routes on the menu item links)
<div id = "header" style = "width: 100%; text-align: center">
<p style = "font-family: verdana, tahoma; font-size: 35px; font-style: bold" >BUDGET 101 MINUS 1</p>
<p style="font-family: verdana, tahoma; font-size: 18px; font-style: italic;" >"Budgeting is an important component of financial success. It's not difficult to implement, and it's not just for people with limited funds. Budgeting makes it easier for people with incomes and expenses of all sizes to make conscious decisions about how they'd prefer to allocate their money", Investopedia</p>
<div style = "text-align: center; width: 100%; position: relative">
<div style="width: 150px; position: absolute; left: 0px; background-color: #28acd6; color: #ffffff; padding: 5px; margin: 3px" ><a href="/" style="text-decoration: none" ><font color="#ffffff" >Home</font></a</div>
<div style="width: 150px; position: absolute; left: 166px; background-color: #28acd6; color: #ffffff; padding: 5px; margin: 3px" ><a href="/budget" style="text-decoration: none" ><font color="#ffffff" >Budget</font></a></div>
<div style="width: 150px; position: absolute; left: 332px; background-color: #28acd6; color: #ffffff; padding: 5px; margin: 3px"><a href="/expenditure" style="text-decoration: none" ><font color="#ffffff" >Expenditure</font></a></div>
<div style="width: 150px; position: absolute; left: 498px; background-color: #28acd6; color: #ffffff; padding:5px; margin: 3px"><a href="/budgetreport" style="text-decoration: none" ><font color="#ffffff" >Report</font></a></div>
<div style="width: 150px; position: absolute; left: 664px; background-color: #28acd6; color: #ffffff; padding: 5px; margin: 3px"><a href="/logout" style="text-decoration: none" ><font color="#ffffff" >Logout</font></a></div>
</div>
</div><br/><br/><br/>
footer.html.twig
<div id="footer" style="width:100%; text-align: center;clear:left;"><br /><br /><hr />Our index page is now trimmed down a bit as we do not have to boilerplate header and footer html in it:
<p style="font-family: verdana,tahoma; font-size:12px; font-style: italic;">©Inspector Budget Inc 2015</p>
</div>
{% extends 'base.html.twig' %}
{% block body %}
<div id="container" style="width:100%;" >
<div id="budget_icon" style="float:left; text-align: center; width:30%">
<img src="{{ asset( 'img/icons/budget_icon.png' ) }}" style="width: 180px; height: 180px"/><br /><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">Budget Register</p>
</div>
<div id="expenditure_icon" style="float:left; text-align: center; width:30%">
<img src="{{ asset( 'img/icons/expenditure_icon.png' ) }}" style="width: 180px; height: 180px"/><br /><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">Expenditure Register</p>
</div>
<div id="report_icon" style="float:left; text-align: center; width:30%">
<img src="{{ asset( 'img/icons/report_icon.png' ) }}" style="width: 180px; height: 180px"/><br /><p style="font-family: verdana,tahoma; font-size:18px; font-style: italic;">Report Generator</p>
</div>
</div>
{% endblock %}
{% block stylesheets %}
{% endblock %}
Budget and Expenditure Controllers
Creating data driven applications often requires one or all of the CRUD operations. Symfony comes bundled with Doctrine and has the data driven architecture in mind. We need to generate the CRUD forms for our tables.
We can do this manually too, but we will choose to take advantage of one of the excellent SensioGeneratorBundle features, generating the controller and forms.
Before we go further, keep in mind for each of these data driven functionalities the logic is:
>>> Database Asset is Mapped in PHP Object by
>>> an Entity (a Doctrine entity) whose properties are mapped to Form objects by
>>> a Form Type object which is manipulated by a
>>> Controller method which is accessed from client side via a
>>> Route
The SensioGeneratorBundle shall generate our Form, Controller, handle routing as well as create front end views.
Before we generate the CRUD forms, as we have seen, we need to create our entities first. A Symfony form maps entity data to a persisted object. Thus, we need doctrine entities. These shall be stored in the Entity directory of your bundle.
We can use the Doctrine console to generate these entities, or manually generate them. Like Symfony, you have the choice of Annotation, Yaml or XML mapping.
We will generate three Entities, one for our budget_register table, one for the expenditure_register table and one for the view named v_budget_vs_expenditure. These are vanilla Doctrine entities and are saved under src/AppBundleEntity directory.
<?phpNote, I have not illustrated ExpenditureRegister. Because the budget and expenditure tables are similar, you can copy BudgetRegister entity and create an ExpenditureRegister entity mapped to the expenditure_registry table with the same properties and methods; Below is the BudgetReport entity which shall provide data to our report view.
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* AppBundle\Entity\BudgetRegister
* @ORM\Entity
* @ORM\Table( name = "budget_register")
*/
class BudgetRegister
{
/**
* @ORM\Column( type = "integer")
* @ORM\Id
* @ORM\GeneratedValue( strategy = "AUTO")
*/
protected $id;
/**
* @ORM\Column(type = "datetime")
*/
protected $dateFrom;
/**
* @ORM\Column(type = "string", length = 90)
*/
protected $description;
/**
* @ORM\Column(type = "decimal", scale = 4)
*/
protected $value;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set date from
*
* @param \DateTime $dateFrom
*
* @return BudgetRegister
*/
public function setDateFrom( $dateFrom )
{
$this->dateFrom = $dateFrom;
return $this;
}
/**
* Get date from
*
* @return \DateTime
*/
public function getDateFrom()
{
return $this->dateFrom;
}
/**
* Set description
*
* @param string $description
*
* @return Product
*/
public function setDescription( $description )
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set value
*
* @param double $value
*
* @return BudgetRegister
*/
public function setValue( $value )
{
$this->value = $value;
return $this;
}
/**
* Get value
*
* @return double
*/
public function getValue()
{
return $this->value;
}
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* AppBundle\Entity\BudgetReport
* @ORM\Entity
* @ORM\Table( name = "v_budget_vs_expenditure")
*/
class BudgetReport
{
/**
* @ORM\Column( type = "datetime")
*/
protected $dateFrom;
/**
* @ORM\Column( type = "string", length = 90)
* @ORM\Id
*/
protected $description;
/**
* @ORM\Column(type = "decimal", scale = 4)
*/
protected $budget_value;
/**
* @ORM\Column( type = "decimal", scale = 4)
*/
protected $expenditure_value;
/**
* Set date from
*
* @param \DateTime $dateFrom
*
* @return ExpenditureRegister
*/
public function setDateFrom( $dateFrom )
{
$this->dateFrom = $dateFrom;
return $this;
}
/**
* Get date from
*
* @return \DateTime
*/
public function getDateFrom()
{
return $this->dateFrom;
}
/**
* Set description
*
* @param string $description
*
* @return Product
*/
public function setDescription( $description )
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set budget_value
*
* @param double $budget_value
*
* @return ExpenditureRegister
*/
public function setBudgetValue( $budget_value )
{
$this->budget_value = $budget_value;
return $this;
}
/**
* Get budget value
*
* @return double
*/
public function getBudgetValue()
{
return $this->budget_value;
}
/**
* Set expenditure value
*
* @param double $expenditure_value
*
* @return ExpenditureRegister
*/
public function setExpenditureValue( $expenditure_value )
{
$this->expenditure_value = $expenditure_value;
return $this;
}
/**
* Get expenditure value
*
* @return double
*/
public function getExpenditureValue()
{
return $this->expenditure_value;
}
}
Next, to generate Controllers, Views and handle routing, run the command below; and you shall be guided through a series of questions to establish what assets need to be generated and how.
The important thing is for you to indicate the correct shortcut name for the entities we just created (this is the first piece of information the wizard will ask for) which will be AppBundle:BudgetRegister, AppBundle:ExpenditureRegister and AppBundle:BusinessReport.
For the BusinessReport, do not generate write actions when asked by the console wizard because it is read only. Below is an example flow of control with the wizard, which is launched by typing app/console generate:doctrine:crud on your console
Welcome to the Doctrine2 CRUD generatorAt this point, if you selected different routes in your wizard from the ones indicated in our header.html.twig, you should edit the header.html.twig file and update it with the proper paths.
This command helps you generate CRUD controllers and templates.
First, give the name of the existing entity for which you want to generate a CRUD
(use the shortcut notation like AcmeBlogBundle:Post)
The Entity shortcut name: AppBundle:BudgetRegister
By default, the generator creates two actions: list and show.
You can also ask it to generate "write" actions: new, update, and delete.
Do you want to generate the "write" actions [no]? yes
Determine the format to use for the generated CRUD.
Configuration format (yml, xml, php, or annotation) [annotation]:
Determine the routes prefix (all the routes will be "mounted" under this
prefix: /prefix/, /prefix/new, ...).
/budget
Summary before generation
You are going to generate a CRUD controller for "AppBundle:BudgetRegister"
using the "annotation" format.
Do you confirm generation [yes]?
CRUD generation
Generating the CRUD code: OK
Generating the Form code: OK
Updating the routing: OK
Everything is OK! Now get to work :).
Reporting
Mapping The Report
The report is generated by a form that is mapped to the Business Report entity. This entity is mapped to a view that has four fields.
The budget date (date budget item was input), the budgeted amount, the actual amount expended. We would like to add some additional fields via twig. CodeLobster will again come forth with HTML hints where we are embedding HTML.
Additional Fields
Our twig list (show) view file for the report now looks like this.
{% extends 'base.html.twig' %}
{% block body %}
<h1>BudgetReport list</h1>
<table>
<thead>
<tr>
<th>Datefrom</th>
<th>Description</th>
<th>Budget_value</th>
<th>Expenditure_value</th>
<th>Money Saved</th>
<th>Savings Ratio</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for budgetReport in budgetReports %}
<tr>
<td><a href="{{ path('budgetreport_show', { 'id': budgetReport.description }) }}">{{ budgetReport.dateFrom.format( 'Y-m-d' ) }}</a></td>
<td>{{ budgetReport.description }}</td>
<td>{{ budgetReport.budgetvalue }}</td>
<td>{{ budgetReport.expenditurevalue }}</td>
<td>{{ budgetReport.budgetvalue - budgetReport.expenditurevalue }}</td>
{% set budgetVariance = budgetReport.budgetvalue - budgetReport.expenditurevalue %}
{% set varianceRatio = budgetVariance / budgetReport.budgetvalue %}
{% if varianceRatio < 0 %}
{% set backgroundColor = "background-color: #b33a3a" %}
{% elseif (varianceRatio - 0.15) < 0 %}
{% set backgroundColor = "background-color: #ffb347" %}
{% else %}
{% set backgroundColor = "background-color: #32cd32" %}
{% endif %}
<td style = "{{backgroundColor}}" >{{ (varianceRatio * 100) | round(2,'floor') }}%</td>
<td>
<ul>
<li>
<a href="{{ path('budgetreport_show', { 'id': budgetReport.description }) }}">show</a>
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
We utilize twig's conditional block functionality to establish which color the report should be. If you are having savings over your budget above 15%, it shall be green; if you have breached the 15% savings on a downward trend, but haven't yet overspent, it becomes orange. The moment you overspend it becomes warning red.
We also utilize the ability of twig to assign variables via the {% set ... %} statement. We also use filters to format our numbers; the round filter for the variance ratio field, which is a percentage of the money saved (or lost :P ) against the budgeted amount. We also use the abs filter in one of our if statements. Example of the report can be seen below.
Conclusion
The Symfony framework comes equipped with many useful features. Codelobster IDE strives to provide full framework feature support, and this tutorial indicates how you can maneuver from DB to logic to front end programming of your favorite framework within the IDE.
There are some areas I have not touched like Unit Testing and Debugging on this IDE, which you can further explore in the Codelobster Web site.
Our demo application is rather rough around the edges too but has demonstrated a data driven application can be built with minimal effort provided you pick the right tools for the job.
Try it yourself and download Codelobster IDE now and experience the gains in productivity when you want to develop PHP framework based applications.
If you liked this article or have questions about developing PHP applications with Codelobster, post a comment here.
You need to be a registered user or login to post a comment
Login Immediately with your account on:
Comments:
No comments were submitted yet.