Student Manager Application built using the Slim PHP Microframework, MySQL, and AngularJS.
- MySQL:
/Applications/MAMP/Library/bin/mysql -u root -p
, Password:root
- PHP:
tail -f /Applications/MAMP/logs/php_error.log
-
Directory Structure:
api - vendor - .htaccess - index.php app - assets - partials - app.js - controllers.js - services.js test - studentlist.controller.test.js .bowerrc .gitignore bower.json composer.json gulpfile.js index.html karma.conf.js package.json README.md
-
Set up directories
mkdir -p api app app/partials test
-
Set up empty config files
touch .bowerrc .gitignore bower.json composer.json gulpfile.js karma.conf.js package.json
-
Set up
.bowerrc
{ "directory": "app/assets/lib/", "analytics": false }
-
Set up
.gitignore
.DS_Store node_modules app/assets/lib/ composer.lock api/vendor
-
Set up
bower.json
{ "name": "student-manager-example", "version": "0.0.0", "dependencies": { "angular": "~1.4.4", "angular-resource": "~1.4.4", "angular-ui-router": "~0.2.15", "foundation": "~5.5.2", "foundation-icon-fonts": "*" }, "analytics": false, "devDependencies": { "angular-mocks": "~1.4.4" } }
-
Set up
composer.json
{ "config": { "vendor-dir": "api/vendor" }, "require": { "slim/slim": "^2.6", "vrana/notorm": "dev-master" } }
-
Set up
gulpfile.js
// Dependencies var gulp = require('gulp'); var sass = require('gulp-sass'); var rename = require('gulp-rename'); var watch = require('gulp-watch'); // Configuration var sassSource = './app/assets/scss/**/*.scss'; var sassDestination = './app/assets/css'; var jsSource = './app'; var sassOptions = { errLogToConsole: true, outputStyle: 'expanded' }; // Sass gulp.task('sass', function () { return gulp .src(sassSource) .pipe(sass(sassOptions).on('error', sass.logError)) .pipe(rename('style.css')) .pipe(gulp.dest(sassDestination)) }); // Watch gulp.task('default',function() { gulp.watch(sassSource,['sass']); });
-
Set up
karma.conf.json
// Karma configuration // Generated on Mon Aug 24 2015 15:35:10 GMT-0500 (CDT) module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['jasmine'], // list of files / patterns to load in the browser files: [ 'app/assets/lib/angular/angular.js', 'app/assets/lib/angular-resource/angular-resource.js', 'app/assets/lib/angular-ui-router/release/angular-ui-router.js', 'app/assets/lib/angular-mocks/angular-mocks.js', 'app/*.js', 'test/*.test.js' ], // list of files to exclude exclude: [ ], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { }, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ['progress'], // web server port port: 9876, // enable / disable colors in the output (reporters and logs) colors: true, // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, //logLevel: config.LOG_DEBUG, // enable / disable watching file and executing tests whenever any file changes autoWatch: false, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ['PhantomJS'], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: true }) }
-
Set up
package.json
{ "name": "student-manager-example", "version": "0.0.1", "description": "A student management tool.", "author": "Austin Reilly <austinjreilly@gmail.com>", "readme": "README.md", "repository": { "type": "git", "url": "https://github.com/austinjreilly/student-manager-example" }, "private": true, "devDependencies": { "gulp": "^3.9.0", "gulp-rename": "^1.2.2", "gulp-sass": "^2.0.4", "gulp-watch": "^4.3.5", "jasmine-core": "^2.3.4", "karma": "^0.13.9", "karma-chrome-launcher": "^0.2.0", "karma-jasmine": "^0.3.6", "karma-phantomjs-launcher": "^0.2.1", "phantomjs": "^1.9.18" }, "scripts": { "postinstall": "bower install", "test": "karma start karma.conf.js" } }
-
Install packages:
`composer install` `npm install`
- Create new repository on GitHub
git init
git add *
git commit -m ""Setting up configuration files
git push -u origin master
- New branch:
git checkout -b api
cd api
touch .htaccess index.php
-
Add to
api/.htaccess
:RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php [QSA,L] ```
-
Add to
api/index.php
<?php require 'vendor/autoload.php'; $app = new \Slim\Slim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run();
-
Go to http://localhost/student-manager-example/api/hello/austin
-
/Applications/MAMP/Library/bin/mysql -u root -p
-
Type the password
root
-
CREATE DATABASE student_manager_example;
-
USE student_manager_demo;
-
Create the table:
CREATE TABLE students ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(30) NOT NULL, last_name VARCHAR(30) NOT NULL, fall_test_score INT(3) NOT NULL, spring_test_score INT(3) NOT NULL, final_test_score INT(3) NOT NULL );
-
Seed the database:
INSERT INTO students(first_name,last_name,fall_test_score,spring_test_score,final_test_score) VALUES ('John','Lennon','120','131','140'), ('Paul','McCartney','115','141','129'), ('George','Harrison','141','153','149'), ('Ringo','Starr','100','88','93');
-
Add Database Configuration to index.php
$dbhost = 'localhost'; $dbuser = 'root'; $dbpass = 'root'; $dbname = 'student_manager_example'; $dbmethod = 'mysql:dbname='; $dsn = $dbmethod.$dbname; $pdo = new PDO($dsn, $dbuser, $dbpass); $db = new NotORM($pdo);
-
Show all students
$app->get('/students', function() use($app, $db){ $students = array(); foreach ($db->students() as $student) { $students[] = array( 'id' => $student['id'], 'first_name' => $student['first_name'], 'last_name' => $student['last_name'], 'fall_test_score' => $student['fall_test_score'], 'spring_test_score' => $student['spring_test_score'], 'final_test_score' => $student['final_test_score'] ); } $app->response()->header("Content-Type", "application/json"); echo json_encode( $students ); });
- Test:
curl -X GET http://localhost/student-manager-example/api/students
- Test:
-
Add a new student
$app->post('/students', function() use($app, $db){ $app->response()->header("Content-Type", "application/json"); $json = $app->request->getBody(); $data = json_decode($json, true); error_log(print_r($data,true)); $result = $db->students->insert($data); if ($result != false){ echo json_encode(array( 'status' => true, 'result' => $result )); } else { echo json_encode(array( 'status' => false )); } });
- Test:
curl -X POST -H "application/json" -d '{"first_name":"Stuart","last_name":"Sutcliffe","fall_test_score":"100","spring_test_score":"109","final_test_score":"0"}' http://localhost/student-manager-example/api/students
- Test:
-
Show an single student
$app->get('/students/:id', function($id) use ($app, $db) { $app->response()->header("Content-Type", "application/json"); $student = $db->students()->where('id', $id); if($data = $student->fetch()){ echo json_encode(array( 'status' => true, 'id' => $data['id'], 'first_name' => $data['first_name'], 'last_name' => $data['last_name'], 'fall_test_score' => $data['fall_test_score'], 'spring_test_score' => $data['spring_test_score'], 'final_test_score' => $data['final_test_score'] )); } else { echo json_encode(array( 'status' => false, 'message' => "Student with ID $id does not exist." )); } });
- Test:
curl -X GET http://localhost/student-manager-example/api/students/1
- Test:
-
Update a student
$app->put('/students/:id', function($id) use($app, $db){ $app->response()->header("Content-Type", "application/json"); $student = $db->students()->where("id", $id); if ($student->fetch()) { $json = $app->request->getBody(); $data = json_decode($json, true); error_log(print_r($json,true)); $result = $student->update($data); echo json_encode(array( 'status' => true, 'message' => "Student updated successfully." )); } else { echo json_encode(array( 'status' => false )); } });
- Test: curl -X PUT -d '{"first_name":"John W."}' -H "Content-Type: application/json;" http://localhost/student-manager-example/api/students/1
-
Delete a student
$app->delete('/students/:id', function($id) use($app, $db){ $app->response()->header("Content-Type", "application/json"); $student = $db->students()->where('id', $id); if($student->fetch()){ $result = $student->delete(); echo json_encode(array( 'status' => true, 'message' => "Student deleted successfully." )); } else{ echo json_encode(array( 'status' => false, 'message' => "Student with ID $id does not exist." )); } }); * Test: curl -i -X DELETE http://localhost/student-manager-example/api/students/5
-
Run the application!
$app->run();
URL | HTTP Verb | POST Body | Result |
---|---|---|---|
http://localhost/student-manager-example/api/students | GET | empty | Returns all students |
http://localhost/student-manager-example/api/students | POST | JSON String | Creates new student |
http://localhost/student-manager-example/api/students/:id | GET | empty | Returns single student |
http://localhost/student-manager-example/api/students/:id | PUT | JSON String | Updates and existing student |
http://localhost/student-manager-example/api/students/:id | DELETE | empty | Deletes existing student |
-
touch index.html
-
Add to index.html <!doctype html> <title>Student Manager</title>
<script src="app/assets/lib/angular/angular.js"></script> </head> <body> <div class="row"> <h1>Student Manager</h1> <div ng-init="hello='world'"> <h1>{{hello}}</h1> <input type="text" ng-model="hello"> </div> </div> </body> </html>
-
touch app/app.js
-
Include
app/app.js
inindex.html
-
Add
ng-app="StudentManager"
directive to<html>
tag -
Add to
app/app.js
angular.module('StudentManager', ['ngResource','ngRoute'])
.controller('MainCtrl', function($scope){ $scope.hello = 'world'; });
-
Create application files:
touch app/controllers.js app/services.js
-
Create partial files:
touch app/partials/_form.html app/partials/student-add.html app/partials/student-edit.html app/partials/student-view.html app/partials/students.html
-
Include necessary files in
<head>
ofindex.html
<script src="app/assets/lib/angular/angular.js"></script> <script src="app/assets/lib/angular-resource/angular-resource.js"></script> <script src="app/assets/lib/angular-ui-router/release/angular-ui-router.js"></script> <script src="app/app.js"></script> <script src="app/controllers.js"></script> <script src="app/services.js"></script>
-
Set up dependencies in
app/app.js
var StudentManagerApp = angular.module('StudentManagerApp',[ 'ui.router', 'ngResource', 'StudentManagerControllers', 'StudentManagerServices' ] );
-
Create empty
StudentManagerServices
andStudentManagerServices
modules, inapp/controllers.js
andapp/services.js
, respectivelyvar StudentManagerControllers = angular.module('StudentManagerControllers',[]); var StudentManagerServices = angular.module('StudentManagerServices',[]);
-
Create factory using
$http
service inapp/services.js
StudentManagerServices.factory('slimAPI', function($http){ return { list: function(callback){ $http({ method: 'GET', url: 'http://localhost/student-manager-example/api/students', }).success(callback); }, }; });
-
Create controller in
app/controllers.js
StudentManagerControllers.controller('StudentListController',function($scope,slimAPI){ slimAPI.list(function(result){ $scope.students = result; }); });
-
Create state in
app/app.js
-
Create partial in
app/partials/students.html
-
Create view in
index.php
by adding<div ui-view></div>
element/directive.= -
Repeat for viewing single student, editing existing student, deleting existing students, and adding a new student
- Create scss directory and files
mkdir app/assets/scss app/assets/css
touch app/assets/scss/custom.scss app/assets/scss/main.scss
- Run
gulp sass
to compile CSS - Uncomment CSS include in
index.html
- Add sorting to student listing page
- Add search filter to student listing page
- Install karma, go through
karma.conf.js
creation process optionally by runningkarma init
touch test/studentlist.controller.test.js
- Create test
- Run test using
npm test
- Unit testing for API
- API Improvements
- Class average test scores
- Charts and graphs
- Integration with Khan Academy API