austinjreilly / student-manager

Demo CRUD application built using the Slim PHP Microframework, MySQL, and AngularJS

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Student Manager Demo

Student Manager Application built using the Slim PHP Microframework, MySQL, and AngularJS.

Debugging shortcuts

  • MySQL: /Applications/MAMP/Library/bin/mysql -u root -p, Password: root
  • PHP: tail -f /Applications/MAMP/logs/php_error.log

Initial Setup

  1. 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
    
  2. Set up directories

     mkdir -p api app app/partials test
    
  3. Set up empty config files

     touch .bowerrc .gitignore bower.json composer.json gulpfile.js karma.conf.js package.json
    
  4. Set up .bowerrc

     {
       "directory": "app/assets/lib/",
       "analytics": false
     }
    
  5. Set up .gitignore

     .DS_Store
     node_modules
     app/assets/lib/
     composer.lock
     api/vendor
    
  6. 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"
       }
     }
    
  7. Set up composer.json

     {
         "config": {
                "vendor-dir": "api/vendor"
         },
         "require": {
             "slim/slim": "^2.6",
             "vrana/notorm": "dev-master"
         }
     }
    
  8. 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']);
     });
    
  9. 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
       })
     }
    
  10. 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"
      }
    }
    
  11. Install packages:

    `composer install`
    `npm install`
    

Git Setup

  1. Create new repository on GitHub
  2. git init
  3. git add *
  4. git commit -m ""Setting up configuration files
  5. git push -u origin master

API Setup

  1. New branch: git checkout -b api
  2. cd api
  3. touch .htaccess index.php

API Hello World

  1. Add to api/.htaccess:

     RewriteEngine On
    
     RewriteCond %{REQUEST_FILENAME} !-f
     RewriteRule ^(.*)$ index.php [QSA,L]
     ```
    
  2. 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();
    
  3. Go to http://localhost/student-manager-example/api/hello/austin

Database setup

  1. /Applications/MAMP/Library/bin/mysql -u root -p

  2. Type the password root

  3. CREATE DATABASE student_manager_example;

  4. USE student_manager_demo;

  5. 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
     );
    
  6. 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');
    

Setting up routes

  1. 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);
    
  2. 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
  3. 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
  4. 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
  5. 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
             ));
         }
     });
    
  6. 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
    
  7. Run the application!

    $app->run();

REST API Summary

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

Angular Set Up

Angular Hello World

  1. touch index.html

  2. 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>
    

Angular Hello World with Controller

  1. touch app/app.js

  2. Include app/app.js in index.html

  3. Add ng-app="StudentManager" directive to <html> tag

  4. Add to app/app.js

    angular.module('StudentManager', ['ngResource','ngRoute'])

    .controller('MainCtrl', function($scope){ $scope.hello = 'world'; });

Angular Setup

  1. Create application files: touch app/controllers.js app/services.js

  2. 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

  3. Include necessary files in <head> of index.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>
    
  4. Set up dependencies in app/app.js

    var StudentManagerApp = angular.module('StudentManagerApp',[ 'ui.router', 'ngResource', 'StudentManagerControllers', 'StudentManagerServices' ] );

  5. Create empty StudentManagerServices and StudentManagerServices modules, in app/controllers.js and app/services.js, respectively

     var StudentManagerControllers = angular.module('StudentManagerControllers',[]);
     var StudentManagerServices = angular.module('StudentManagerServices',[]);
    

View List of All Students with Angular

  1. Create factory using $http service in app/services.js

     StudentManagerServices.factory('slimAPI', function($http){
         return {
                 list: function(callback){
                     $http({
                         method: 'GET',
                         url: 'http://localhost/student-manager-example/api/students',
                     }).success(callback);
                 },
             };
     });
    
  2. Create controller in app/controllers.js

     StudentManagerControllers.controller('StudentListController',function($scope,slimAPI){
         slimAPI.list(function(result){
             $scope.students = result;
         });
     });
    
  3. Create state in app/app.js

  4. Create partial in app/partials/students.html

  5. Create view in index.php by adding <div ui-view></div> element/directive.=

  6. Repeat for viewing single student, editing existing student, deleting existing students, and adding a new student

CSS

  1. Create scss directory and files mkdir app/assets/scss app/assets/css touch app/assets/scss/custom.scss app/assets/scss/main.scss
  2. Run gulp sass to compile CSS
  3. Uncomment CSS include in index.html

Angular Extras

  1. Add sorting to student listing page
  2. Add search filter to student listing page

Unit Test

  1. Install karma, go through karma.conf.js creation process optionally by running karma init
  2. touch test/studentlist.controller.test.js
  3. Create test
  4. Run test using npm test

Additional Features

  • Unit testing for API
  • API Improvements
  • Class average test scores
  • Charts and graphs
  • Integration with Khan Academy API

About

Demo CRUD application built using the Slim PHP Microframework, MySQL, and AngularJS


Languages

Language:CSS 86.4%Language:JavaScript 7.4%Language:HTML 6.2%