How to create a software solution (using CakePHP 2.x) without wasting time

When building a software solution the most important aspect is to have many attainable milestones. 
-> This is primarily important so that the client / boss / etc can be included in the process and is able to comment early.  
        -> For a programmer the worst thing is to program a system which wasn't properly thought through and then the programmer can to modify the database for the changes. This is a time waster, but worst is it takes away the excitement and makes the programmer lose confidence in the project
-> We have found that if you create many milestones in development, not only do you NOT get burnt out, you enjoy the process. 

Below are the steps we follow to build a software solution including our init files to get a basic clickable system linked to the database up quickly
-> This init system because our ADMIN view 
-> At the point we then created the VIEWS for the required functions

# 1 > Create a description of the project

The first step is to simply write out (NOT TALK) the concept
-> Doing this in writing forces the programmer to starting thinking about the edge cases
-> This also forces the manager / boss / client to think about what is required

I suggest this is done in email form (NOT AS AN ATTACHMENT) so both parties can read this easily whever they have a free moment

Ensure this description is agreed by both parties in writing (NO SIGNATURES) just a 'Yes that is what i want in an email'
-> There can still be changes later, but at this point you want to ensure both parties are exciting about the developement

# 2 > Database Schema

After the descrption is agreed it is the programmer that need to create a database schema which will handle the requirements.
-> We use GOOGLE docs here at undoLogic, but you can use any software you like. 

It needs to be visual showing connections between tables, so even non-technical people can give there input
-> Using this method is also a great tool to include the client / boss/ etc into the development process EARLY so they can give feedback before you waste time building

See the example this is the structure for SimuCheck.com - an online resource for people that love flight simulators. 
-> Notice the arrows indicating the connections between the database tables
-> One way arrow is a belongsTO so ONE thing belongs to another
-> Two arrows is a HasAndBelongsTOMany so multiple users can use multiple things 

# 3 > Meetings

At this point it's important to sit down together and go over the software or a conference call
-> This is the time to make changes, as it's easy to change the lines add new database tables etc. 
-> In our example it is a simple system, but for complex systems you will have lines connecting under over etc. 
-> DO NOT RUSH THIS STEP - Especially as a programmer you need to let this sink in - sleep on it - look at it the next day with a fresh mind and ensure all the arrows go in the correct direction.
-> Your client / boss / etc will not understand this completely but it will give a mutual confidence between. During the discussion add elements, add notes etc. 
-> This brings both parties together in a non stressful collaboration

# 4 > Create DATABASE

It is now time to create the BASIC database 
-> Using the fields that were on the example database create the tables in your database (we like to use PHPMYADMIN)
-> Prefix your tables to keep similar systems organized as the software grows: eg in our inventory system let's prefix our tables with inv_ NOTE: tables that are similar between systems like 'users' do not need a prefix
-> All tables should have an 'id' (even databases for complex joins - always nice to do a lookup with a unique column)

In your browser logon to phpmyadmin (assumes you have Mamp installed)
-> eg localhost/phpmyadmin
-> change to the database of your project (click Database - choose the database name)
-> at the bottom 'create table' add the name of the table you are creating (it SHOULD be plural and underscores replace spaces)
-> ensure all tables have 'id', [INDEX primary key + A_I checked {auto increment}] 'created' [datetime], 'modified' [datetime]
-> Other fields like 'names' are normally varchar 250 (adjust as neccessary)
-> dates should be DATETIME
-> the storage engine should be 'InnoDB'
-> click 'save'

Repeat with all the tables you are creating

# 5 > Create ADMIN pages

Now that our database has been created we are ready to create our ADMIN pages. 

After the software is launched only programmers will have access to this page and the pages that will be customer facing will be created later. 
-> This is a way to quickly get a clickable system saving data into the database. 

Create a controller, model and views for each database table (in our example we will be using the databse inv_items)



# 6 > Create Controller

This file goes into "app/Controller" - named: ProductsController.php
-> The controller is plural like the database

App::uses('AppController', 'Controller');
* Products Controller
class ProductsController extends AppController {

////////////////////////////////////////////////// ADMIN BASIC FUNCTIONS */
var $modelUsed = 'Product';
var $columns = array(
'id', 'name', 'created', 'modified'

function beforeFilter() {
$this->set('columns', $this->columns);
$this->set('model', $this->modelUsed);

var $searchFields = array(

private function connectedTables() {
//add other tables here which are connected eg if this belongs to users
$modelUsed = $this->modelUsed;
$users = $this->$modelUsed->User->find('list');
$this->set('users', $users);

function admin_index() {
$model = $this->modelUsed;

if (!empty($this->request->data)) {
$search = $this->request->data[ $model ][ 'search' ];
$this->paginate = array(
'conditions' => $this->setupSearch($this->searchFields, $model, $search)
} else {
//by default we need a limit to init the pagination
$this->paginate = array(
'limit' => 5


$records = $this->paginate();
$this->set('records', $records);
function setupSearch($arrayFields, $model, $searchTerm) {
$search = array();
$search[ 'OR' ] = array();
foreach ($arrayFields as $each) {
$search[ 'OR' ][ ] = array($model . '.' . $each . ' LIKE' => '%' . $searchTerm . '%');
return $search;

function admin_view($id = NULL) {

$model = $this->modelUsed;
$record = $this->$model->read(NULL, $id);
$this->set('record', $record);

function admin_edit($id = FALSE) {

$model = $this->modelUsed;

if (!empty($this->request->data)) {

if ($this->$model->save($this->request->data)) {
$this->Session->setFlash(__('Saved', TRUE));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('could not be saved', TRUE));
} elseif ($id == 'new') {

} else {
$this->request->data = $this->$model->read(NULL, $id);


function admin_delete($id = NULL) {
$model = $this->modelUsed;
if (!$id) {
$this->Session->setFlash(__('Invalid id', TRUE));
$this->redirect(array('action' => 'index'));
if ($this->$model->delete($id)) {
$this->Session->setFlash(__('deleted', TRUE));
$this->Session->setFlash(__('NOT deleted', TRUE));

////////////////////////////////////////////// END ADMIN BASIC FUNCTIONS */


# 7 > Create VIEW (admin_edit.ctp)

Create a VIEW in "app/View" named: admin_edit.ctp


//The columns are set from the controller and are auto generated below, so you can leave commented, but can be overridden here
//$model = 'Product';
//$columns = array(
// 'id',
// 'name',
// 'created',
// 'modified'

// if you want to leave out a specific column
// unset($columns[ array_search('column_to_leave_out', $columns) ]);
<?php echo $this->Form->create($model, array(
'novalidate' => true,
//'type' => 'file' //uncomment if you want to upload files
<legend><?php echo __('Edit ' . $model); ?></legend>
<?php foreach ($columns as $column): ?>
<?= $this->Form->input($column, array('label' => $column)); ?>
<?php endforeach; ?>

<?php if (0): //For a hasAndBelongsToMany ?>
<?= $this->Form->input('Pricing', array(
'options' => $pricingList,
'multiple' => 'checkbox',
)); ?>
<?php endif; ?>

<?php echo $this->Form->end(__('Submit', TRUE));?>

# 8 > Create VIEW (admin_index.ctp)

Create the INDEX (app/View/admin_index.ctp)

//$model = 'InvItem';
//$columns = array(
// 'id',
// 'name',
// 'created',
// 'modified'
<?= $model; ?> <?php echo $this->Html->link(__('Add new', TRUE), array(
// 'admin' => TRUE,
'action' => 'edit', 'new'
)); ?>

<?php echo $this->Form->create($model); ?>
<?php echo $this->Form->input('search'); ?>
<?php echo $this->Form->end(__('search', TRUE)); ?>

<h2><?php echo __($model); ?></h2>
<table cellpadding="0" cellspacing="0" class="display table table-striped table-hover">
<th class="actions"><?php echo __('Actions'); ?></th>
<?php foreach ($columns as $column): ?>
<th><?php echo $this->Paginator->sort($column, $column); ?></th>
<?php endforeach; ?>

<?php foreach ($records as $each): ?>
<td class="actions">
<?php echo $this->Html->link(__('Edit', TRUE), array(
'action' => 'edit', $each[ $model ][ 'id' ]
)); ?>
<?php echo $this->Html->link(__('Delete', TRUE), array(
'action' => 'delete', $each[ $model ][ 'id' ]
), NULL, sprintf(__('Are you sure you want to delete?', TRUE), $each[ $model ][ 'id' ])); ?>
<?php foreach ($columns as $column): ?>
<td><?php echo $each[ $model ][ $column ]; ?>&nbsp;</td>
<?php endforeach; ?>

<?php endforeach; ?>

<div class="previous_next">

<?php echo $this->paginator->prev('<< Previous ', array(
'class' => 'PrevPg'
), null, array(
'class' => 'disabled'
)); ?>

<?php echo $this->Paginator->numbers(); ?>

echo $this->paginator->counter(array(
'format' => ' of %pages% pages (showing %current% of %count%)'

<?php echo $this->paginator->next(' Next >>', array(
'class' => 'NextPg'
), null, array(
'class' => 'disabled'


# 9 > Bake

The models are created by using BAKE

in the terminal navigate to the base directory of your project

NOTE: If you are using docker, you need for first login to the docker shell:

cd docker
-> find the 'web'

docker exec -it docker_web_1 bash

-> You are now logged into the docker

-> cd /var/www/vhosts/website.com/
-> cd www
-> cd app (cd /path/to/app)
-> ./Console/cake bake

You are now in bake, click 'M' to create a model

It displays all your databases, choose the appropriate (eg local) these are defined in 'app/Config/database.php'

Type the number of the database table eg 21 for Product

type 'Y' for validation criteria - accept the defaults by pushing enter

It then asks 'Would you like to define model associations' type 'Y'

It will display all the connections, accept the ones which reflect the database concept

'Look okay' - Y

'PHPunit' - Y

# 10 > Admin click through

At this point you can now interact with the data using the admin windows
-> Add any links you need to link all the windows together. 
-> You should be able to navigate all the screens using only your mouse


# 11 > Concept views for approval (not programmed yet)

Now it is time to create the concept views. This ensures all features that will be programed are planned out visually.
-> Skipping this step will most likely lead to mis-understandings, and not realizing features are required from the client

This is a great way to allow the client to click around and understand the feeling of the software.
-> We have noticed that until you complete this step you don't get all the required info or feedback, but as soon as the client starts clicking around it forces to disclose any missing features which they thought would be included. 

It should NOT link to the database or have any database programming. Only for web-programming for the use of UX/UI should be completed at this point. All the features that will be completed and programmed should be visisble. Eg if there is a feature where a button is pushed and information is displayed simply have the button visible and when clicked it opens a hidden object where the infor will appear from the db.

If you are adding on NEW features to an already running software
-> Ensure sure you add the code so the view only displays these new features to ADMINS (If developers need different access create a PREFIX for DEV)
-> Simply surround links and features with 

<?php if (isset($isAdmin)): ?>
     <span class="admin">
New link OR feature goes here
<?php endif; //onlyAdmin ?>

Ensure you have a CSS style added to your styles as follows which will make all the NEW features (that are only visible to admins) yellow:

.admin {
     background-color: yellow;

Keep modifying this untill all the functionality is added and you can see everything 
-> These screens should look nice and your CSS developer should make them pretty and easy to navigate.

Get the client to approve the changes / features in email writing before you start programming
-> Hours at this stage represent Tens of hours of programming => Do not rush this step and revise until the client LOVES the software. 


# 12 > Programming

Now that all the non-working views of the features are approved it is time to program. 
-> This step should be completed without any input from the client. 
-> Ensure the admin only code is preserved. Only if you are creating a very complicated module should you create a separate branch. In general your code should be added in a non-destructive mannor to the master branch. After the code is approved the 'isAdmin' codes will be removed and then all users will be able to use the new code. 
-> Turn the lights down put on your favourite music and ensure you are not distracted for sessions. (Take breaks to check-in, but ensure you get into good programming strides without any distractions at all)
-> Consider the Pomodoro Technique to get the most done: https://en.wikipedia.org/wiki/Pomodoro_Technique


# 13 > Test-driven development

The most crutial of your systems should be completed using #testDrivenDevelopment.

Simple system are not required, but other it is important that we know for certain if a system is offline / broken

  1. Create test data in the fixture
  2. Create a test to prove the system 
  3. This forces to make your models thick and your controllers thin = Good practice
  4. After you create and prove your test you can easily integrate this into your controll on the live system and this ensure the actual program you wrote will be proved. 

CREATE Fixture (app/Test/Fixture/ProductFixture.php) with the following:

<?php class ProductFixture extends CakeTestFixture {
//import needs to be created in your database, is the same as local
public $import = array('table' => 'products', 'connection' => 'import');

//Records can be exported from PHPMyAdmin -> export -> phpArray
var $records = array(
array('id' => '1','name' => 'name here'),
array('id' => '2','name' => 'name 2 goes here'),
//others here



CREATE Test (app/Test/Case/Model/ProductTest.php) with the following:

App::uses('Product', 'Model');

* Product Test Case
class ProductTest extends CakeTestCase {

* Fixtures
* @var array
public $fixtures = array(
// add other fixtures which you need


* setUp method
* @return void
public function setUp() {
$this->Product = ClassRegistry::init('Product');

public function testFirstOne() {
        $before = $this->Product->find('all');
              $this->assertEquals('FIRST-GROUP', $before['InvGroup']['identifier'], 'wrong group to start');


* tearDown method
* @return void
public function tearDown() {




# 14 > Finalization

Now that the programming is completed you can show to the customer and have them test it out
-> Step by step instructions should be created for each feature for the client. 
-> Ensure you add this to a location that can be shared and found later (do not email it once instead add to some type of online shared resource)

After each feature is approved by the client, it is time to launch the feature to the PUBLIC
-> Remove the code <?php if (isset($isAdmin)): ?><span class="admin">
-> Remove the code  </span><?php endif; //admin ?>

Now the feature is visible and usable to all people using the website / software. 

For any corrections OR new features return to step #11