From 1ae178334023d36a224b06c371a47a5c3e0aad3d Mon Sep 17 00:00:00 2001 From: wei <> Date: Mon, 14 May 2007 02:51:03 +0000 Subject: remove unfinished docs. --- demos/time-tracker/index.php | 9 +- demos/time-tracker/protected/application.xml | 2 +- .../protected/pages/Docs/CreateBusinessCode.page | 127 ------------- .../protected/pages/Docs/DatabaseDesign.page | 42 ----- .../protected/pages/Docs/GettingStarted.page | 67 ------- demos/time-tracker/protected/pages/Docs/Home.page | 10 - .../protected/pages/Docs/Introduction.page | 64 ------- demos/time-tracker/protected/pages/Docs/Layout.php | 7 - demos/time-tracker/protected/pages/Docs/Layout.tpl | 45 ----- .../protected/pages/Docs/MoreTests.page | 140 -------------- .../protected/pages/Docs/TopicList.php | 8 - .../protected/pages/Docs/TopicList.tpl | 27 --- .../pages/Docs/UserClassAndExceptions.page | 202 -------------------- .../protected/pages/Docs/UsingSQLMap.page | 210 --------------------- .../pages/Docs/WritingFunctionalTest.page | 39 ---- .../protected/pages/Docs/WritingUnitTest.page | 86 --------- demos/time-tracker/protected/pages/Docs/config.xml | 12 -- demos/time-tracker/protected/pages/Docs/db.png | Bin 26521 -> 0 bytes .../protected/pages/Docs/functional_test1.png | Bin 92502 -> 0 bytes .../protected/pages/Docs/functional_test2.png | Bin 104726 -> 0 bytes .../time-tracker/protected/pages/Docs/preface.page | 19 -- .../time-tracker/protected/pages/Docs/project1.png | Bin 338441 -> 0 bytes .../protected/pages/Docs/unit_test1.png | Bin 55855 -> 0 bytes .../protected/pages/Docs/unit_test2.png | Bin 87439 -> 0 bytes .../protected/pages/Docs/unit_test3.png | Bin 61923 -> 0 bytes .../protected/pages/TimeTracker/MainLayout.tpl | 1 - demos/time-tracker/protected/pages/Welcome.page | 34 +--- 27 files changed, 8 insertions(+), 1143 deletions(-) delete mode 100644 demos/time-tracker/protected/pages/Docs/CreateBusinessCode.page delete mode 100644 demos/time-tracker/protected/pages/Docs/DatabaseDesign.page delete mode 100644 demos/time-tracker/protected/pages/Docs/GettingStarted.page delete mode 100644 demos/time-tracker/protected/pages/Docs/Home.page delete mode 100644 demos/time-tracker/protected/pages/Docs/Introduction.page delete mode 100644 demos/time-tracker/protected/pages/Docs/Layout.php delete mode 100644 demos/time-tracker/protected/pages/Docs/Layout.tpl delete mode 100644 demos/time-tracker/protected/pages/Docs/MoreTests.page delete mode 100644 demos/time-tracker/protected/pages/Docs/TopicList.php delete mode 100644 demos/time-tracker/protected/pages/Docs/TopicList.tpl delete mode 100644 demos/time-tracker/protected/pages/Docs/UserClassAndExceptions.page delete mode 100644 demos/time-tracker/protected/pages/Docs/UsingSQLMap.page delete mode 100644 demos/time-tracker/protected/pages/Docs/WritingFunctionalTest.page delete mode 100644 demos/time-tracker/protected/pages/Docs/WritingUnitTest.page delete mode 100644 demos/time-tracker/protected/pages/Docs/config.xml delete mode 100644 demos/time-tracker/protected/pages/Docs/db.png delete mode 100644 demos/time-tracker/protected/pages/Docs/functional_test1.png delete mode 100644 demos/time-tracker/protected/pages/Docs/functional_test2.png delete mode 100644 demos/time-tracker/protected/pages/Docs/preface.page delete mode 100644 demos/time-tracker/protected/pages/Docs/project1.png delete mode 100644 demos/time-tracker/protected/pages/Docs/unit_test1.png delete mode 100644 demos/time-tracker/protected/pages/Docs/unit_test2.png delete mode 100644 demos/time-tracker/protected/pages/Docs/unit_test3.png (limited to 'demos') diff --git a/demos/time-tracker/index.php b/demos/time-tracker/index.php index 9a03ed65..7954aeb9 100644 --- a/demos/time-tracker/index.php +++ b/demos/time-tracker/index.php @@ -6,7 +6,8 @@ $frameworkPath='../../framework/prado.php'; $assetsPath=$basePath."/assets"; $runtimePath=$basePath."/protected/runtime"; -$sqliteDbDir = $basePath."/protected/App_Data/SQLite"; +$sqlite_dir = $basePath."/protected/App_Data/SQLite"; +$sqlite_db = $sqlite_dir.'/time-tracker.db'; if(!is_file($frameworkPath)) die("Unable to find prado framework path $frameworkPath."); @@ -14,8 +15,10 @@ if(!is_writable($assetsPath)) die("Please make sure that the directory $assetsPath is writable by Web server process."); if(!is_writable($runtimePath)) die("Please make sure that the directory $runtimePath is writable by Web server process."); -if(!is_writable($sqliteDbDir)) - die("Please make sure that the directory $sqliteDbDir is writable by Web server process."); +if(!is_writable($sqlite_dir)) + die("Please make sure that the directory $sqlite_dir is writable by Web server process."); +if(!is_writable($sqlite_db)) + die("Please make sure that the sqlite database file $sqlite_dir is writable by Web server process."); require_once($frameworkPath); diff --git a/demos/time-tracker/protected/application.xml b/demos/time-tracker/protected/application.xml index d9e4d01c..bae5f126 100644 --- a/demos/time-tracker/protected/application.xml +++ b/demos/time-tracker/protected/application.xml @@ -17,7 +17,7 @@ --> + configFile="Application.App_Data.sqlite-sqlmap"> diff --git a/demos/time-tracker/protected/pages/Docs/CreateBusinessCode.page b/demos/time-tracker/protected/pages/Docs/CreateBusinessCode.page deleted file mode 100644 index e5afa572..00000000 --- a/demos/time-tracker/protected/pages/Docs/CreateBusinessCode.page +++ /dev/null @@ -1,127 +0,0 @@ - -

Create Business Code

-

We start the design with the database, the entity relationships are shown -in the diagram below.

- - class="figure" /> - -

Now we can begin to create and test some business code. Let us begin -with the Project defintions. First, we add some properties or fields.

- -<?php -class Project -{ - public $ActualDuration = 0; - public $CreatorUserName = ''; - public $CompletionDate = 0; - public $DateCreated = 0; - public $Description = ''; - public $EstimateDuration = 0; - public $ID = 0; - public $ManagerUserName = ''; - public $Name = ''; -} -?> - - -

All the fields should be self explainatory. The ManagerUserName -is the user name of the project manager. Notice that the fields -are public, later on, we can change some or all of them to be private and -provide some accessor and mutators (i.e. getters and setters). If we want -we can let the Project class inherit TComponent such -that the getters and setters can be used like properties, such as those -found in Prado.

- -

Business Services, test case first

-

Next we want to add users to the project. For this, we start with some -unit tests. We are going to design the business logic around the concept of -Data Access Objects (DAO).

- - -<?php -Prado::using('Application.APP_CODE.*'); -class ProjectDaoTestCase extends UnitTestCase -{ - function testProjectDaoCanCreateNewProject() - { - $project = new Project(); - $project->Name = "Project 1"; - - $dao = new ProjectDao(); - - $this->assertTrue($dao->createNewProject($project)); - $this->assertEqual($dao->getProjectByID(1), $project); - } -} -?> - -

So what are we doing here? First we create a new Project named -"Project 1". Then we create a new ProjectDao so we can insert new projects -and retrieve it. We assert that a project will be create sucessfully using -assertTrue($do->createNewProject(...)). We also assert that -getProjectByID(1) will return an instance of Project class -with same data (the reference may be different).

- -

If we run the above unit test case, nothing is going to pass since we have -not even defined the ProjectDao class. So lets do that first and import -the class in the tests as well.

- -

We will create a base Dao class as follows, and we save as BaseDao.php -in our APP_CODE directory.

- -<?php -class BaseDao -{ - private $_connection; - - public function setConnection($connection) - { - $this->_connection = $connection; - } - - protected function getConnection() - { - return $this->_connection; - } -} -?> - -

And finally our ProjectDao class.

- -<?php -Prado::using('Application.APP_CODE.BaseDao'); -class ProjectDao extends BaseDao -{ -} -?> - -

If we run the unit test again, we get an error message something like -"Fatal error: Call to undefined method ProjectDao::createNewProject() in ...". -So let us, fix this. -

- -class ProjectDao extends BaseDao -{ - public function createNewProject($project) - { - } - - public function getProjectByID($projectID) - { - } -} - -

Run the unit test again, we see a different type of error now. This time -the unit test runner complians that the test fails.

-

Write Test, Run Test, Repeat

-
-At this point, you may notice a repetition -
    -
  1. write some test code,
  2. -
  3. run the test, test gives an error,
  4. -
  5. write some actual code to only remove the previous error,
  6. -
  7. goto step 1 to repeat.
  8. -
-
-

We shall see how we can make this test pass in the next section.

-
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/DatabaseDesign.page b/demos/time-tracker/protected/pages/Docs/DatabaseDesign.page deleted file mode 100644 index a306ba2c..00000000 --- a/demos/time-tracker/protected/pages/Docs/DatabaseDesign.page +++ /dev/null @@ -1,42 +0,0 @@ - -

Database Design

-

The design steps we take in the sample application is a data centric -approach. That is, we shall consider the application from the view -point of data structures, data relationships and data integrity. -Other designs starting from object and class design are also possible but is -not the approach taken in this guide. -

- -

Entity Relationship

-

We start with the following entity relationship diagram. -The bold text in each entity indicate required fields. The underline -text indicates the entity's primary key, also denoted as PK. -Entity relationships are indicated with arrows between entities and -each relationship field is denoted with FK.

- -

The four entities: Project, Categories, TimeEntry -and ProjectMembers -makes up the time tracker application. While the other entities: -Users, UserRoles, RoleTypes and Signon are -usually found in applications where membership or user management is required.

- - class="figure" /> -

- -
Info: -See Database Normalization -for a quick reference regarding database design and normalization, especially the -3rd normal form (3NF). -
- -

MySQL database

-

We shall concentrate on the MySQL database for this sample application. -Other database engines are also possible and can be quite easily inter-changed. -The SQL script to initialize the MySQL database is given below. The following -SQL script was created using MySQL version 4.1.7. -

- - -<%= file_get_contents('protected/data/time-tracker-mysql.sql') %> - -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/GettingStarted.page b/demos/time-tracker/protected/pages/Docs/GettingStarted.page deleted file mode 100644 index 8b8e8e5e..00000000 --- a/demos/time-tracker/protected/pages/Docs/GettingStarted.page +++ /dev/null @@ -1,67 +0,0 @@ - -

Installation

-

There are a few pieces of tools and software needed throughout the development -of the application. First we shall setup the environment for coding and testing. We -shall from here on assume that you have access to a working installation of a web server with -PHP and possibilly a MySQL database server. The first thing to do is install Prado and some -testing tools.

- -

Download and Install Prado

-

The minimum requirement by PRADO is that the Web server support PHP 5. - For the Time Tracker sample application, you need Prado version 3.1 or greater.

-

Installation of PRADO mainly involves downloading and unpacking.

-
    -
  1. Go to pradosoft.com to grab the latest version of PRADO.
  2. -
  3. Unpack the PRADO release file to a Web-accessible directory.
  4. -
- -

You should at least first check that the demos bundled with the Prado distribution are - working. See the - quickstart tutorial for further instructions on running the demos. -

- -

Help! Nothing is working!

-

If you encounter problems in downloading, unpacking and running the demo applications, please - visit the forum to seek help. Please - do a search on the forum first or read the PDF version of the quickstart. - The friendly and wonderful people at the forum can better assist you if you can provide as much detail regarding your problem. - You should include in your post your server configuration details, the steps you took to reproduce your problem, and any error messages encountered.

- -

Create a new Prado application

-

Prado is bundled with a command line tool to create the necessary directory structure to run a hello world application. -The command tool prado-cli.php is a php script and can be found in the prado/framework/ directory. -To create a new application, go to your document root and type the following command in console. -

- -php prado/framework/prado-cli.php -t -c time-tracker - - -
Tip: -For linux and OS X environments, you can chmod u+x prado-cli.php -and change the first line of prado-cli.php to the location of your -PHP command line interpreter. Now, you may call the script from command line -like an executable. -
- -

A directory named time-tracker will be created containing the following. - -document_root/time-tracker/assets/ -document_root/time-tracker/index.php -document_root/time-tracker/protected/ -document_root/time-tracker/protected/.htaccess -document_root/time-tracker/protected/pages/ -document_root/time-tracker/protected/pages/Home.page -document_root/time-tracker/protected/runtime/ - -

The time-tracker directory is where we are going to put all our code for the Time Tracker application. -Since -t was passed into the prado-cli.php script, unit and functional test - skeletons are also created.

- -document_root/time-tracker/tests/ -document_root/time-tracker/tests/unit/ -document_root/time-tracker/tests/functional/ -document_root/time-tracker/tests/unit.php -document_root/time-tracker/tests/functional.php - - -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/Home.page b/demos/time-tracker/protected/pages/Docs/Home.page deleted file mode 100644 index 46876f26..00000000 --- a/demos/time-tracker/protected/pages/Docs/Home.page +++ /dev/null @@ -1,10 +0,0 @@ - -

Prado Time Tracker Implementation Guide

- -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/Introduction.page b/demos/time-tracker/protected/pages/Docs/Introduction.page deleted file mode 100644 index baa305f5..00000000 --- a/demos/time-tracker/protected/pages/Docs/Introduction.page +++ /dev/null @@ -1,64 +0,0 @@ - -

Introduction

- -

The Time Tracker is a fully functional sample application designed to - introduce you to many of Prado's features. This guide is a step by step - walkthrough starting from installation to deployment. The Time Tracker - application is based on the - ASP.NET's Time Tracker Starter Kit. -

- -

Time Tracker Overview

-

The Time Tracker is a business web application for keeping track of hours spent -on a project, with ability to handle multiple resources as well as multiple projects.

-

Basic Application Requirements

-

The functional requirements of the Time Tracker is based on the - ASP.NET's Time Tracker Starter Kit - description.

-

-

Create projects

-
    -
  • Define projection information like due dates, hours to complete, project - resources, and more.
  • -
  • Break down projects into tasks and track work on per-task basis.
  • -
-

Create and track tasks

-
    -
  • Track time spent each day by category and project.
  • -
-

Use reports to track progress

-
    -
  • Track overall progress across multiple projects, including estimated and actual work.
  • -
  • Track total work for team resources across multiple projects.
  • -
-

- -

Technologies and Design Approached Demonstrated

-
    -
  • Prado event-driven component framework
  • -
  • Separation of concerns: persistent storage, business logic, presentation
  • -
  • Object-Relational mapping using SQLMap
  • -
  • Unit testing and functional testing
  • -
  • Promote code reuse
  • -
- -

Requirements

-

The Time Tracker web application requires the following software and knowledge.

-

Software requirements

-

It is assumed that you are able to obtain and install the following pieces of software.

-
    -
  1. PHP version 5.0.4 or greater
  2. -
  3. A web server, such as Apache, able to run PHP scripts
  4. -
  5. MySQL database server version 4.1 or greater (alternatively SQLite or Postgres)
  6. -
  7. Prado version 3.1 or later
  8. -
- -

In addition to software requirements, we assumed that you have some of the following knowledge.

-
    -
  • Access the internet to download packaged code, and unzip these code.
  • -
  • Understand the basic concepts of Object-Oriented programming in PHP such as - classes, objects, methods, etc.
  • -
  • Basic understanding of relational databases and Structured Query Language (SQL).
  • -
  • Knows how to have fun, this is mandatory.
  • -
-
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/Layout.php b/demos/time-tracker/protected/pages/Docs/Layout.php deleted file mode 100644 index e612d52d..00000000 --- a/demos/time-tracker/protected/pages/Docs/Layout.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/Layout.tpl b/demos/time-tracker/protected/pages/Docs/Layout.tpl deleted file mode 100644 index 291c2482..00000000 --- a/demos/time-tracker/protected/pages/Docs/Layout.tpl +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - -Home | -PradoSoft.com | -PDF Version | - - - - - - - - -
- - -
- -
-
- - - -
- - \ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/MoreTests.page b/demos/time-tracker/protected/pages/Docs/MoreTests.page deleted file mode 100644 index 5e598982..00000000 --- a/demos/time-tracker/protected/pages/Docs/MoreTests.page +++ /dev/null @@ -1,140 +0,0 @@ - - -

Finishing up test case

-

Up to this point, you should have the follow pieces of files and code. - class="figure" /> -

- -

So what else can we test for at this point? A few reasonable -tests are to see what happens if the project already exists, and what if -the username does not exists.

- -

First we shall refactor our test code since much of the setup code -for the mocked connection will need to be repeated in other test assertions. -We change the test case to have these three new methods. -

- - -function setupMockConnectionFor($project) -{ - $customer = new TimeTrackerUser(); - $customer->ID = 1; - $customer->Name = "Customer A"; - - $manager = new TimeTrackerUser(); - $manager->ID = 2; - $manager->Name = "Manager A"; - - $conn = $this->connection; - - //return the customer and manager - $conn->setReturnValue('queryForObject', - $customer, array('GetUserByName', 'Customer A')); - $conn->setReturnValue('queryForObject', - $manager, array('GetUserByName', 'Manager A')); - - //project does not exist - $conn->setReturnValue('queryForObject', - null, array('GetProjectByName', 'Project 1')); - - $param['project'] = $project; - $param['creator'] = $customer->ID; - $param['manager'] = $manager->ID; - - $conn->setReturnValue('insert', - true, array('CreateNewProject', $param)); - $conn->setReturnReference('queryForObject', - $project, array('GetProjectByID', 1)); -} - -function createNewTestProject() -{ - $project = new Project(); - $project->Name = "Project 1"; - $project->CreatorUserName = "Customer A"; - $project->ManagerUserName = "Manager A"; - - return $project; -} - -function assertProjectCreated($project) -{ - $this->assertTrue($this->dao->createNewProject($project)); - $this->assertEqual($this->dao->getProjectByID(1), $project); -} - - -

Our refactored test method testProjectDaoCanCreateNewProject() -is as follows.

- -function testProjectDaoCanCreateNewProject() -{ - $project = $this->createNewTestProject(); - - if(($conn = $this->connection) instanceof MockTSqlMapper) - { - $this->setupMockConnectionFor($project); - $conn->expectMinimumCallCount('queryForObject', 3); - $conn->expectAtLeastOnce('insert'); - } - - $this->assertProjectCreated($project); -} - - -

To test that the project already exists, we modify the mock -connection and test for an exception.

- -function testProjectExistsException() -{ - $project = $this->createNewTestProject(); - - if(($conn = $this->connection) instanceof MockTSqlMapper) - { - //make the project exist - $conn->setReturnValue('queryForObject', - $project, array('GetProjectByName', 'Project 1')); - $this->setupMockConnectionFor($project); - } - - try - { - $this->assertProjectCreated($project); - $this->fail(); - } - catch(TimeTrackerException $e) - { - $this->pass(); - } -} - - -

Other test method for testing missing customer and manager users -are done similarly. At this point, the test case file looks quite large. -We shall not add more tests to this case and we should rename the file -from ProjectDaoTestCase.php to CreateNewProjectTestCase.php. - -

Note: -A heirachical exception class may be more useful in testing for specific -exceptions. For simplicity, we decided to use only TimeTrackerException -for this small project. -
- -
Tip:Class, method and file naming is very -important to any project, the name should inform you at first glance what -is to be expected from the class, method or file. In addition, the naming -scheme should be uniform over the project. -
- -
Comment: -These test may be too ridget as any changes to the implementation may -actually cause the tests to fail. This is the case with grey-box/white-box -testing, you are actually testing the implementation. Black box tests may -be more preferable, as it should only test the class interface (the method or -function details such as parameters and may be return values). However, -with the use of database connection to retrive data within the objects under test, -it may be more suitable to do intergration tests. -
- -
- diff --git a/demos/time-tracker/protected/pages/Docs/TopicList.php b/demos/time-tracker/protected/pages/Docs/TopicList.php deleted file mode 100644 index ce827cc0..00000000 --- a/demos/time-tracker/protected/pages/Docs/TopicList.php +++ /dev/null @@ -1,8 +0,0 @@ - \ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/TopicList.tpl b/demos/time-tracker/protected/pages/Docs/TopicList.tpl deleted file mode 100644 index 53243578..00000000 --- a/demos/time-tracker/protected/pages/Docs/TopicList.tpl +++ /dev/null @@ -1,27 +0,0 @@ -
- -
- -
Prado Time Tracker Implementation Guide
- - -
Testing Business Code
- - -
- - - -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/UserClassAndExceptions.page b/demos/time-tracker/protected/pages/Docs/UserClassAndExceptions.page deleted file mode 100644 index f85e00be..00000000 --- a/demos/time-tracker/protected/pages/Docs/UserClassAndExceptions.page +++ /dev/null @@ -1,202 +0,0 @@ - -

More complete ProjectTestCase

-

-In creating a new project, we need to check the following: -

    -
  • that the project does not already exists, i.e. no duplicate project name
  • -
  • that the project creator and project manager exists when a new project is created
  • -
-So to perform this task, we will modified the test code. - -function testProjectDaoCanCreateNewProject() -{ - $project = new Project(); - $project->ID = 1; - $project->Name = "Project 1"; - $project->CreatorUserName = "Customer A"; - $project->ManagerUserName = "Manager A"; - - $customer = new TimeTrackerUser(); - $customer->ID = 1; - $customer->Name = "Customer A"; - - $manager = new TimeTrackerUser(); - $manager->ID = 2; - $manager->Name = "Manager A"; - - if(($conn = $this->connection) instanceof MockTSqlMapper) - { - //return the customer and manager - $conn->setReturnValue('queryForObject', - $customer, array('GetUserByName', 'Customer A')); - $conn->setReturnValue('queryForObject', - $manager, array('GetUserByName', 'Manager A')); - - //project does not exist - $conn->setReturnValue('queryForObject', - null, array('GetProjectByName', 'Project 1')); - - $param['project'] = $project; - $param['creator'] = $customer->ID; - $param['manager'] = $manager->ID; - - $conn->setReturnValue('insert', true, - array('CreateNewProject', $param)); - $conn->setReturnReference('queryForObject', - $project, array('GetProjectByID', 1)); - - //we expect queryForObject to be called 3 times - $conn->expectMinimumCallCount('queryForObject', 3); - $conn->expectAtLeastOnce('insert'); - } - - $this->assertTrue($this->dao->createNewProject($project)); - $this->assertEqual($this->dao->getProjectByID(1), $project); -} - -

-

It may seem very weird that there is so much code in the tests -and why we even bother to write all these test codes. Well, using the -above test code we have the following advantages.

-
Advantages of Mock -
    -
  1. we don't need a real database base connection to test the code, - this means we can start relying on tested code ealier
  2. -
  3. when a test fails we know that problem is not part of the database
  4. -
  5. when a test fail, we can quickly pin point the problem
  6. -
  7. the test suite gives us the confidence to refactor our code
  8. -
-
- -

Of couse, the test will not be able to cover the higher interactions, such as -the user interface, so intergration or functional web test will be used later on. -

- -

So how did we come up with the above tests? We started simple, then we -ask what sort of things it should handle. We assume that the connection object -work as expect or known to be unexpected and see how the method we want to test handle -these situations.

- -

If we run the above test, we will be faced with numerous errors. First will be -that the TimeTrackerUser can not be found.

- -

Creating a new User Class

-

Notice that the Project class contains CreatorUserName -and ManagerUserName properties. So at some point we -are going to need at least one User class. We shall name the class as -TimeTrackerUser and save it as APP_CODE/TimeTrackerUser.php - -<?php -Prado::using('System.Security.TUser'); -Prado::using('System.Security.TUserManager'); -class TimeTrackerUser extends TUser -{ - private $_ID; - - public function __construct() - { - parent::__construct(new TUserManager()); - } - - public function getID(){ return $this->_ID; } - - public function setID($value) - { - if(is_null($this->_ID)) - $this->_ID = $value; - else - throw new TimeTrackerUserException( - 'timetracker_user_readonly_id'); - } -} -?> - - -

Custom Exceptions

-

We enforce that the ID of the user to be read-only once it has been -set by throwing a custom exception. Prado's exception classes -uses a string key to find a localized exception string containing more -detailed description of the exception. The default exception messages -are stored in the framework/Exceptions/messages.txt. This -file location can be changed by overriding the getErrorMessageFile() -method of TException class. We define a custom exception class -for all Time Tracker application exceptions as TimeTrackerException -and save the class as APP_CODE/TimeTrackerException.php.

- - -<?php -class TimeTrackerException extends TException -{ - /** - * @return string path to the error message file - */ - protected function getErrorMessageFile() - { - return dirname(__FILE__).'/exceptions.txt'; - } -} -?> - - -

We then create a exceptions.txt file in the APP_CODE -directory with the following content.

- - -timetracker_user_readonly_id = Time tracker user ID is read-only. - - -

Additional parameters passed in the exception constructor can be -added the message string using {0} as the first additional parameter, -and {1} as the second additional parameter, and so on. -For example, suppose we want to raise the follow exception. -

- - -throw new TimeTrackerException('project_exists', $projectName); - - -

The exception error message in exceptions.txt may contain something like:

- -project_exists = Time tracker project '{0}' already exists. - - -

Completing the test case

-

From the unit test code, we can pretty much see what the implementation -for createNewProject() will look like.

- - -public function createNewProject($project) -{ - $sqlmap = $this->getConnection(); - $creator = $sqlmap->queryForObject('GetUserByName', $project->CreatorUserName); - $manager = $sqlmap->queryForObject('GetUserByName', $project->ManagerUserName); - $exists = $sqlmap->queryForObject('GetProjectByName', $project->Name); - if($exists) - { - throw new TimeTrackerException( - 'project_exists', $project->Name); - } - else if(!$creator || !$manager) - { - throw new TimeTrackerException( - 'invalid_creator_and_manager', - $project->Name, $project->CreatorUserName, - $project->ManagerUserName); - } - else - { - $param['project'] = $project; - $param['creator'] = $creator->ID; - $param['manager'] = $manager->ID; - return $sqlmap->insert('CreateNewProject', $param); - } -} - - -
Tip: -A hierachy of exception class can be used to have fine exception handling. -Since this is a small project and for simplicity, we shall use the application level -TimeTrackerException exception class for most exception cases. -
- -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/UsingSQLMap.page b/demos/time-tracker/protected/pages/Docs/UsingSQLMap.page deleted file mode 100644 index cc2abf4f..00000000 --- a/demos/time-tracker/protected/pages/Docs/UsingSQLMap.page +++ /dev/null @@ -1,210 +0,0 @@ - -

Using SQLMap Data Mapper

-

Before proceeding with write the code to pass the test in the previous section, we shall -make a design decision. We shall use SQLMap as our data access layer. Note -that SQLMap is only available offically in Prado 3.1 or later.

- -

SQLMap is an PHP implemenation of an Object/Relational -data mapper. SQLMap has the following basic features: -

    -
  1. collects all the Structured Query Language -(SQL) statements in an external XML file
  2. -
  3. Maps data return from database queries into PHP objects
  4. -
  5. Takes PHP objects as parameters in SQL queries
  6. -
-

SQLMap can be seen as a generic data mapper, rather than an -Object Relational Mapping (ORM) solution.

- -

The SQLMap API consists of the following methods. See the SQLMap manual -for further details.

- - -/* Query API */ -public function queryForObject($statementName, $parameter=null, $result=null); -public function queryForList($statementName, $parameter=null, $result=null, - $skip=-1, $max=-1); -public function queryForPagedList($statementName, $parameter=null, $pageSize=10); -public function queryForMap($statementName, $parameter=null, - $keyProperty=null, $valueProperty=null); -public function insert($statementName, $parameter=null) -public function update($statementName, $parameter=null) -public function delete($statementName, $parameter=null) - -/* Connection API */ -public function openConnection() -public function closeConnection() - -/* Transaction API */ -public function beginTransaction() -public function commitTransaction() -public function rollBackTransaction() - - -

Fetch and Inserting Data Using SQLMap

-

Back to our ProjectDao class, for testing we shall write the code -assuming we have an SQLMap client instance. The SQLMap client or connection -will be mocked or faked using a proxy. Later on, we shall extend our tests, -to become an intergration test, to include -a test database connection.

- -

Creating a SQLMap connection

-

We can test the ProjectDao using mock objects (see SimpleTest documentation -regarding mock objects) or using a real database connection. In PHP version -5.0.x, the sqlite database client is bundled by default. For version 5.1 or later -the sqlite database client is available as an extension. We shall use a sqlite -database to conduct our unit tests because the database can be restored to -the orginal state by just reloading the orginal database file.

- -
Comment: -Unit tests using mocked objects may be too ridget as we need to mock -the connection object and thus the test is of the grey-box/white-box variaity. -That is, you are actually testing the implementation of the object under test -consideration. Thus, any changes to the implementation of the objects -under tests may actually cause the tests to fail. Black box tests may -be more preferable, as it should only test the class interface (the method or -function details such as parameters and may be return values). A further -disadvantage when using mocks for complex database connection is the complexity -required in setting the mocks for testing. -
- -

First, let us define the database table for projects.

- -Field Type Null ----------------------------------------- -ProjectID INTEGER Yes -Name VARCHAR(255) No -Description VARCHAR(255) No -CreationDate INT No -Disabled INT(1) No -EstimateDuration INT No -CompletionDate INT No -CreatorID INTEGER No -ManagerID INTEGER Yes - - -

The corresponding SQLite query to create the table is given below.

- - -CREATE TABLE projects ( - ProjectID INTEGER PRIMARY KEY, - Name VARCHAR(255) NOT NULL, - Description VARCHAR(255) NOT NULL, - CreationDate INT NOT NULL, - Disabled INT(1) NOT NULL, - EstimateDuration INT NOT NULL, - CompletionDate INT NOT NULL, - CreatorID INTEGER NOT NULL, - ManagerID INTEGER -); -CREATE UNIQUE INDEX projects_Name ON projects(Name); -CREATE INDEX project_name_index ON projects(Name); - - -

Testing with Mocked Database Connection

-

At this point, we have said nothing about databases. To create some unit tests -for the ProjectDao class, we are not going to use real database connections -but using a Mocked TSqlMapper. We modifiy the test as follows. -

- -Prado::using('Application.APP_CODE.*'); -Prado::using('System.DataAccess.SQLMap.TSqlMapper'); - -Mock::generate('TSqlMapper'); - -class ProjectDaoTestCase extends UnitTestCase -{ - protected $dao; - protected $connection; - - function setup() - { - $this->dao= new ProjectDao(); - $this->connection = new MockTSqlMapper($this); - $this->dao->setConnection($this->connection); - } - - function testProjectDaoCanCreateNewProject() - { - $project = new Project(); - $project->Name = "Project 1"; - - if(($conn = $this->connection) instanceof MockTSqlMapper) - { - $conn->expectOnce('insert', array('CreateNewProject', $project)); - $conn->setReturnValue('insert', true); - - $conn->expectOnce('queryForObject', array('GetProjectByID', 1)); - $conn->setReturnReference('queryForObject', $project); - } - - $this->assertTrue($this->dao->createNewProject($project)); - $this->assertEqual($this->dao->getProjectByID(1), $project); - } -} - -

The test code looks slight more complicated because later on we want to -test the same assertions against some real data.

-

-In the first two lines above, we simply import the business code and the TSqlMapper class. -We generate a MockTSqlMapper, a mock class or TSqlMapper using -Mock::generate('TSqlMapper'). The method Mock::generate() is available from -the SimpleTest unit testing framework. An instance of MockTSqlMapper -will be used as our test connection for the DAO objects. This allows us to interogate the -internal workings of our ProjectDao class. -

- -

In the setup() (this method is called before every test method), we create an instance of -ProjectDao and set the connection to an instance of MockTSqlMapper. -

- -

Testing internal workings of ProjectDao

- -

So how do we test the internal workings of the ProjectDao createNewProject method? -First, we assume that the connection object will perform the correct database insertion and queries. -Then, we set the return value of the MockTSqlMapper instance to return what we have assumed. -In addition, we expect that the TSqlMapper method queryForObject is called only once with -the parameters we have assumed (e.g. $project). See the SimpleTest tutorial for further details regarding -unit testing with Mocks. -

- -

In our assertions and expectations, we have - -$conn->expectOnce('insert', array('CreateNewProject', $project)); -$conn->setReturnValue('insert', true); - -$this->assertTrue($this->dao->createNewProject($project)); - -This means that, we expect the createNewProject method in ProjectDao -to call the TSqlMapper method insert with parameter $project only once. -In addition, we assume that the returned value from the insert method of TSqlMapper -returns true. Finally, we test the createNewProject method with an assertion. -

- -

We now run the unit tests, we see that there are some failures or errors. Since we -have not created any code in ProjectDao that performs what we want, the tests will fail. -So lets make these test pass, we add some code to ProjectDao class. - - -class ProjectDao extends BaseDao -{ - public function createNewProject($project) - { - $sqlmap = $this->getConnection(); - return $sqlmap->insert('CreateNewProject', $project); - } - - public function getProjectByID($projectID) - { - $sqlmap = $this->getConnection(); - return $sqlmap->queryForObject('GetProjectByID', $projectID); - } -} - -

- -

If we run the unit tests again, we should see a green bar indicating that the tests have passed. -We can now proceed further and add more tests. Of course, the above test does not cover many -conditions, such as, what happens if the project already exists?, the details of these -tests is the subject of the next section. The full test suite can be found in the source. -

-
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/WritingFunctionalTest.page b/demos/time-tracker/protected/pages/Docs/WritingFunctionalTest.page deleted file mode 100644 index b4da0952..00000000 --- a/demos/time-tracker/protected/pages/Docs/WritingFunctionalTest.page +++ /dev/null @@ -1,39 +0,0 @@ - -

Writing a Functional Web Test

-

In addition to unit testing, we shall also do some functional -testing or web testing. Functional tests are, in this case, basically automated tests that will -interact with the overall web application, as if it was the user, while checking the output for correctness. -The functional test tool we shall use here is based on Selenium where the test cases can be written and run using PHP and SimpleTest. -

- -<?php -class HelloPradoTestCase extends SeleniumTestCase -{ - function test() - { - $this->open('../index.php'); - $this->assertTextPresent('Welcome to Prado!'); - } -} -?> - -

Save the code as HelloPradoTestCase.php in the document_root/time-tracker/tests/functional/ -directory.

- -

-Functional test cases are written very similar to unit test cases. The method such as -open($url) are those found in Selenium. All the methods available in Selenium are available. -

- -

Run your first unit test case from your browser

-

Point your browser to your development server's unit test case runner, e.g. - http://web-server-address/time-tracker/tests/functional.php. You should see the following - -

Figure 4: Functional test runner
-

-

Clicking on the All button, you should see - -

Figure 5: Functional test success
-

- -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page b/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page deleted file mode 100644 index 77bdcbe6..00000000 --- a/demos/time-tracker/protected/pages/Docs/WritingUnitTest.page +++ /dev/null @@ -1,86 +0,0 @@ - -

Writing a Unit Test

-

Before we begin to write our business logic and code, we shall -proceed with the path of test driven development (TDD), or at least take -some part of that process.

- -

Unit testing is a useful tool when we want to start to test - our individual business logic classes. - The tests/unit directory will be used to hold the unit test - cases and tests/functional directory - to hold the function test cases. -

- -

Write a unit test case

-

We will start be writing a very simple unit test case. Notice -that we are writing the test case first.

- -<?php -class ProjectTestCase extends UnitTestCase -{ - function testProjectClassExists() - { - $project = new Project(); - $this->pass(); - } -} -?> - -

Save the code as ProjectTestCase.php in the document_root/time-tracker/tests/unit/ -directory.

- -

Run your first unit test case from your browser

-

Point your browser to your development server's unit test case runner, e.g. - http://web-server-address/time-tracker/tests/unit.php. You should see the following - -

Figure 1: Unit test runner
-

-

Clicking on the ProjectTestCase.php link, you should see - -

Figure 2: Unit test failure
-

- -

Smallest step to make the test pass.

- -

Since we only wrote the test case and nothing else we expected -that the test case will fail at some point. Obviously, we need create -a class Project, so lets define the Project class.

- -<?php -class Project -{ -} -?> - -

We save the above code as time-tracker/protected/pages/APP_CODE/Project.php. - Where the APP_CODE directory will contain most of the business logic code - for the Time Tracker application.

-

Now, we also need to add the following line in our test case so as to -include the Project class file when running the tests.

- - -<?php -Prado::using('Application.APP_CODE.Project'); -class ProjectTestCase extends UnitTestCase -{ - ... -} -?> - - -
Info: -The statement Prado::using('Application.APP_CODE.Project') basically -loads the Project.php class file. It assumes that a class name Project has filename Project.php. -For futher details regarding Prado::using can be found in Prado Namespaces documentation. -
- -

Run the unit test runner again, we see that the test has passed. - -

Figure 3: Unit test success
-

-

-Later on, we shall write more test cases. See the -SimpleTest documentation -for detailed tutorial on writing test cases.

- -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/config.xml b/demos/time-tracker/protected/pages/Docs/config.xml deleted file mode 100644 index e8fdc3fe..00000000 --- a/demos/time-tracker/protected/pages/Docs/config.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/db.png b/demos/time-tracker/protected/pages/Docs/db.png deleted file mode 100644 index efdcc1e5..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/db.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/Docs/functional_test1.png b/demos/time-tracker/protected/pages/Docs/functional_test1.png deleted file mode 100644 index 33908734..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/functional_test1.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/Docs/functional_test2.png b/demos/time-tracker/protected/pages/Docs/functional_test2.png deleted file mode 100644 index fb507e72..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/functional_test2.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/Docs/preface.page b/demos/time-tracker/protected/pages/Docs/preface.page deleted file mode 100644 index fa0ba5c3..00000000 --- a/demos/time-tracker/protected/pages/Docs/preface.page +++ /dev/null @@ -1,19 +0,0 @@ - -

Prado Time Tracker

-

This documentation is complete walk-through guide detailing the -steps involved in implementating of the Prado Time Tracker web application. -

-

Target Audience

-

The guide is intended for readers who has some experience -using PHP version 5 and some basic concepts of Object-Oriented application -design. The guide will assume that the reader understands the primary concepts -of class, methods, class inheritance, and other basic features offered by PHP -version 5 or later.

-

Questions and Comments

-

Readers are encouraged to ask questions as the forum regarding any -particular aspect of this documentation. Comment and constructive criticisms -are most welcome. Questions and comments may be directed at -http://www.pradosoft.com/forum/. -

- -
\ No newline at end of file diff --git a/demos/time-tracker/protected/pages/Docs/project1.png b/demos/time-tracker/protected/pages/Docs/project1.png deleted file mode 100644 index a250a943..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/project1.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/Docs/unit_test1.png b/demos/time-tracker/protected/pages/Docs/unit_test1.png deleted file mode 100644 index 66b62e19..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/unit_test1.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/Docs/unit_test2.png b/demos/time-tracker/protected/pages/Docs/unit_test2.png deleted file mode 100644 index e33544d2..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/unit_test2.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/Docs/unit_test3.png b/demos/time-tracker/protected/pages/Docs/unit_test3.png deleted file mode 100644 index bbc04551..00000000 Binary files a/demos/time-tracker/protected/pages/Docs/unit_test3.png and /dev/null differ diff --git a/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl b/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl index ad33217b..44aea22b 100644 --- a/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl +++ b/demos/time-tracker/protected/pages/TimeTracker/MainLayout.tpl @@ -24,7 +24,6 @@ Visible=<%= !$this->User->getIsGuest() %> />

Help

-

Implementation Guide

Create new user link on the home page. New members are activated automatically, and are assigned to a role as specified in the application.xml file. You can manage users (for example, assign them to a role) when login as administrator. - For details, see Appendix A.

- +


Projects and Time Entries

@@ -132,37 +131,6 @@
  • Click Generate Report.
  • -
    - -

    Appendix A - Manually Managing Members and Roles

    -

    - Your Time Tracker Web site allows visitors to register as members. - Members have specific privileges defined by a role you assign to them. - A special administrative role has rights to perform all functions in the site.

    -

    - To create a user (member):

    -
      -
    1. Login as administrator, click Create New User. -
    2. -
    3. ...
    4. -
    -

    - To modify an existing member's role:

    -
      -
    1. Login as administrator, click List Users. -
    2. -
    3. ...
    4. -
    - -
    - -

    Appendix B - Publishing Your Site

    -

    - When you are ready to share the Web site with others, you can copy it to your Web server. - You need to know the File Transfer Protocol (FTP) address of your server, and if required, the user name and password assigned to you.

    -
      -
    1. ...
    2. -
    -- cgit v1.2.3