- Moodle Password: Lee
Last active
April 23, 2016 08:20
-
-
Save r8r/5fa3e7d0fc8d1fb93cad to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<table class="table"> | |
<thead> | |
<tr> | |
<th> | |
Title | |
</th> | |
<th> | |
Author | |
</th> | |
<th> | |
Price | |
</th> | |
<th> | |
<span class="glyphicon glyphicon-shopping-cart" aria-hidden="true"></span> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
<?php | |
foreach ($books as $book): | |
$inCart = ShoppingCart::contains($book->getId()); | |
?> | |
<tr> | |
<td><strong> | |
<?php echo Util::escape($book->getTitle()); ?> | |
</strong> | |
</td> | |
<td> | |
<?php echo Util::escape($book->getAuthor()); ?> | |
</td> | |
<td> | |
<?php echo Util::escape($book->getPrice()); ?> | |
</td> | |
<td class="add-remove"> | |
<?php if ($inCart): ?> | |
<form method="post" action="<?php echo Util::action('removeFromCart', array('bookId' => $book->getId())); ?>"> | |
<input type="submit" role="button" class="btn btn-default btn-xs btn-info" value="-" /> | |
</form> | |
<?php else: ?> | |
<form method="post" action="<?php echo Util::action('addToCart', array('bookId' => $book->getId())); ?>"> | |
<input type="submit" role="button" class="btn btn-default btn-xs btn-success" value="+" /> | |
</form> | |
<?php endif; ?> | |
</td> | |
</tr> | |
<?php endforeach; ?> | |
</tbody> | |
</table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
DROP DATABASE fh_scm4_bookshop; | |
CREATE DATABASE fh_scm4_bookshop; | |
USE fh_scm4_bookshop; | |
CREATE TABLE books ( | |
id int(11) NOT NULL AUTO_INCREMENT, | |
categoryId int(11) NOT NULL, | |
title varchar(255) NOT NULL, | |
author varchar(255) NOT NULL, | |
isbn varchar(255) NOT NULL, | |
price decimal(10,2) NOT NULL, | |
PRIMARY KEY (id), | |
KEY categoryId (categoryId) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8;; | |
CREATE TABLE categories ( | |
id int(11) NOT NULL AUTO_INCREMENT, | |
name varchar(255) NOT NULL, | |
PRIMARY KEY (id) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8;; | |
CREATE TABLE orderedbooks ( | |
id int(11) NOT NULL AUTO_INCREMENT, | |
orderId int(11) NOT NULL, | |
bookId int(11) NOT NULL, | |
PRIMARY KEY (id), | |
KEY orderId (orderId), | |
KEY bookId (bookId) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8;; | |
CREATE TABLE orders ( | |
id int(11) NOT NULL AUTO_INCREMENT, | |
userId int(11) NOT NULL, | |
creditCardNumber char(16) NOT NULL, | |
creditCardHolder varchar(255) NOT NULL, | |
PRIMARY KEY (id), | |
KEY userId (userId) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8;; | |
CREATE TABLE users ( | |
id int(11) NOT NULL AUTO_INCREMENT, | |
userName varchar(255) NOT NULL, | |
passwordHash char(40) NOT NULL, | |
PRIMARY KEY (id), | |
UNIQUE KEY userName (userName) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8;; | |
ALTER TABLE books | |
ADD CONSTRAINT books_ibfk_1 FOREIGN KEY (categoryId) REFERENCES categories (id) ON DELETE CASCADE ON UPDATE CASCADE; | |
ALTER TABLE orderedbooks | |
ADD CONSTRAINT orderedbooks_ibfk_2 FOREIGN KEY (bookId) REFERENCES books (id) ON DELETE CASCADE ON UPDATE CASCADE, | |
ADD CONSTRAINT orderedBooks_ibfk_1 FOREIGN KEY (orderid) REFERENCES orders (id) ON DELETE CASCADE ON UPDATE CASCADE; | |
ALTER TABLE orders | |
ADD CONSTRAINT orders_ibfk_1 FOREIGN KEY (userId) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE; | |
INSERT INTO categories VALUES (1, 'Mobile & Wireless Computing'); | |
INSERT INTO categories VALUES (2, 'Functional Programming'); | |
INSERT INTO categories VALUES (3, 'C / C++'); | |
INSERT INTO categories VALUES (4, '<< New Publications >>'); | |
INSERT INTO books VALUES (1, 1, 'Hello, Android: Introducing Google''s Mobile Development Platform', 'Ed Burnette', '9781934356562', 19.97); | |
INSERT INTO books VALUES (2, 1, 'Android Wireless Application Development', 'Shane Conder, Lauren Darcey', '0321743016', 31.22); | |
INSERT INTO books VALUES (5, 1, 'Professional Flash Mobile Development', 'Richard Wagner', '0470620072', 19.90); | |
INSERT INTO books VALUES (7, 1, 'Mobile Web Design For Dummies', 'Janine Warner, David LaFontaine', '9780470560969', 16.32); | |
INSERT INTO books VALUES (11, 2, 'Introduction to Functional Programming using Haskell', 'Richard Bird', '9780134843469', 74.75); | |
INSERT INTO books VALUES (12, 2, 'Scripting (Attacks) for Beginners - <script type="text/javascript">alert(''All your base are belong to us!'');</script>', 'John Doe', '1234567890', 9.99); | |
INSERT INTO books VALUES (14, 2, 'Expert F# (Expert''s Voice in .NET)', 'Antonio Cisternino, Adam Granicz, Don Syme', '9781590598504', 47.64); | |
INSERT INTO books VALUES (16, 3, 'C Programming Language (2nd Edition)', 'Brian W. Kernighan, Dennis M. Ritchie', '0131103628', 48.36); | |
INSERT INTO books VALUES (27, 3, 'C++ Primer Plus (5th Edition)', 'Stephan Prata', ' 9780672326974', 36.94); | |
INSERT INTO books VALUES (29, 3, 'The C++ Programming Language', 'Bjarne Stroustrup', '0201700735', 67.49); | |
INSERT INTO users VALUES (1, 'scm4', 'a8af855d47d091f0376664fe588207f334cdad22'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="panel panel-default"> | |
<div class="panel-heading"> | |
Please provide your credit card details for payment: | |
</div> | |
<div class="panel-body"> | |
<form class="form-horizontal" method="post" action="<?php echo Util::action('placeOrder'); ?>"> | |
<div class="form-group"> | |
<label for="nameOnCard" class="col-sm-4 control-label">Name on card:</label> | |
<div class="col-sm-8"> | |
<input type="text" class="form-control" id="nameOnCard" name="nameOnCard" placeholder="Your name please!" value="<?php echo htmlentities($nameOnCard); ?>"> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="cardNumber" class="col-sm-4 control-label">Card Number:</label> | |
<div class="col-sm-8"> | |
<input type="text" class="form-control" id="cardNumber" name="cardNumber" placeholder="try '1234567891234567'" value="<?php echo htmlentities($cardNumber); ?>"> | |
</div> | |
</div> | |
<div class="form-group"> | |
<div class="col-sm-offset-4 col-sm-8"> | |
<button type="submit" class="btn btn-default">Place Order</button> | |
</div> | |
</div> | |
</form> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Controller | |
* | |
* class handles POST requests and redirects | |
* the client after processing | |
* - demo of singleton pattern | |
*/ | |
class Controller extends BaseObject { | |
// static strings used in views | |
const ACTION = 'action'; | |
const METHOD_POST = 'POST'; | |
const PAGE = 'page'; | |
const CC_NAME = 'nameOnCard'; | |
const CC_NUMBER = 'cardNumber'; | |
const ACTION_ADD = 'addToCart'; | |
const ACTION_REMOVE = 'removeFromCart'; | |
const ACTION_ORDER = 'placeOrder'; | |
const ACTION_LOGIN = 'login'; | |
const USR_NAME = 'userName'; | |
const USR_PASSWORD = 'password'; | |
const ACTION_LOGOUT = 'logout'; | |
private static $instance = false; | |
/** | |
* | |
* @return Controller | |
*/ | |
public static function getInstance() { | |
if (!self::$instance) { | |
self::$instance = new Controller(); | |
} | |
return self::$instance; | |
} | |
private function __construct() { | |
} | |
/** | |
* | |
* processes POST requests and redirects client depending on selected | |
* action | |
* | |
* @return bool | |
* @throws Exception | |
*/ | |
public function invokePostAction() { | |
if ($_SERVER['REQUEST_METHOD'] != self::METHOD_POST) { | |
throw new Exception('Controller can only handle POST requests.'); | |
return null; | |
} elseif (!isset($_REQUEST[self::ACTION])) { | |
throw new Exception('Action not specified.'); | |
return null; | |
} | |
// now process the assigned action | |
$action = $_REQUEST[self::ACTION]; | |
switch ($action) { | |
case self::ACTION_ADD : | |
ShoppingCart::add((int) $_REQUEST['bookId']); | |
Util::redirect(); | |
break; | |
case self::ACTION_REMOVE : | |
ShoppingCart::remove((int) $_REQUEST['bookId']); | |
Util::redirect(); | |
break; | |
case self::ACTION_ORDER : | |
$user = AuthenticationManager::getAuthenticatedUser(); | |
// abort | |
if ($user == null) { | |
$this->forwardRequest(array('Not logged in.')); | |
break; | |
} | |
// else | |
if ($this->processCheckout($_POST[self::CC_NAME], $_POST[self::CC_NUMBER])) | |
break; | |
else | |
return null; | |
case self::ACTION_LOGIN : | |
//try to authenticate the given user | |
if (!AuthenticationManager::authenticate($_REQUEST[self::USR_NAME], $_REQUEST[self::USR_PASSWORD])) { | |
$this->forwardRequest(array('Invalid user name or password.')); | |
} | |
Util::redirect(); | |
break; | |
case self::ACTION_LOGOUT : | |
//sign out current user | |
AuthenticationManager::signOut(); | |
Util::redirect(); | |
break; | |
default : throw new Exception('Unknown controller action: ' . $action); | |
} | |
} | |
/** | |
* | |
* @param string $nameOnCard | |
* @param integer $cardNumber | |
* @return bool | |
*/ | |
protected function processCheckout($nameOnCard = null, $cardNumber = null) { | |
$errors = array(); | |
$nameOnCard = trim($nameOnCard); | |
if ($nameOnCard == null || strlen($nameOnCard) == 0) { | |
$errors[] = 'Invalid name on card.'; | |
} | |
if ($cardNumber == null || strlen($cardNumber) != 16 || !ctype_digit($cardNumber)) { | |
$errors[] = 'Invalid card number. Card number must be sixteen digits.'; | |
} | |
if (count($errors) > 0) { | |
$this->forwardRequest($errors); | |
return false; | |
} | |
//check cart | |
if (ShoppingCart::size() == 0) { | |
$this->forwardRequest(array('No items in cart.')); | |
} | |
//try to place a new order | |
$user = AuthenticationManager::getAuthenticatedUser(); | |
$orderId = DataManager::createOrder($user->getId(), ShoppingCart::getAll(), $nameOnCard, $cardNumber); | |
if (!$orderId) { | |
$this->forwardRequest(array('Could not create order.')); | |
} | |
//clear shopping card and redirect to success page | |
ShoppingCart::clear(); | |
Util::redirect('index.php?view=success&orderId=' . rawurlencode($orderId)); | |
return true; | |
} | |
/** | |
* | |
* @param array $errors : optional assign it to | |
* @param string $target : url for redirect of the request | |
*/ | |
protected function forwardRequest(array $errors = null, $target = null) { | |
//check for given target and try to fall back to previous page if needed | |
if ($target == null) { | |
if (!isset($_REQUEST[self::PAGE])) { | |
throw new Exception('Missing target for forward.'); | |
} | |
$target = $_REQUEST[self::PAGE]; | |
} | |
//forward request to target | |
// optional - add errors to redirect and process them in view | |
if (count($errors) > 0) | |
$target .= '&errors=' . urlencode(serialize($errors)); | |
header('location: ' . $target); | |
exit(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// mock data | |
$__categories = array(1 => new Category(1, "Mobile & Wireless Computing"), | |
2 => new Category(2, "Functional Programming"), | |
3 => new Category(3, "C / C++"), | |
4 => new Category(4, "<< New Publications >>")); | |
$__books = array(1 => new Book(1, 1, "Hello, Android:\nIntroducing Google's Mobile Development Platform", "Ed Burnette", 19.97), | |
2 => new Book(2, 1, "Android Wireless Application Development", "Shane Conder, Lauren Darcey", 31.22), | |
5 => new Book(5, 1, "Professional Flash Mobile Development", "Richard Wagner", 19.90), | |
7 => new Book(7, 1, "Mobile Web Design For Dummies", "Janine Warner, David LaFontaine", 16.32), | |
11 => new Book(11, 2, "Introduction to Functional Programming using Haskell", "Richard Bird", 74.75), | |
//book with bad title to show scripting attack - add for scripting attack demo only | |
12 => new Book(12, 2, "Scripting (Attacks) for Beginners - <script type=\"text/javascript\">alert('All your base are belong to us!');</script>", "John Doe", 9.99), | |
14 => new Book(14, 2, "Expert F# (Expert's Voice in .NET)", "Antonio Cisternino, Adam Granicz, Don Syme", 47.64), | |
16 => new Book(16, 3, "C Programming Language\n(2nd Edition)", "Brian W. Kernighan, Dennis M. Ritchie", 48.36), | |
27 => new Book(27, 3, "C++ Primer Plus\n(5th Edition)", "Stephan Prata", 36.94), | |
29 => new Book(29, 3, "The C++ Programming Language", "Bjarne Stroustrup", 67.49)); | |
$__users = array(1 => new User(1, "scm4", "a8af855d47d091f0376664fe588207f334cdad22")); //USER = scm4; PASSWORD = scm4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* DataManager | |
* Mysqli Version | |
* | |
* | |
* @package | |
* @subpackage | |
* @author John Doe <jd@fbi.gov> | |
*/ | |
class DataManager { | |
/** | |
* connect to the database | |
* | |
* note: alternatively put those in parameter list or as class variables | |
* | |
* @return connection resource | |
*/ | |
private static function getConnection() { | |
$con = new mysqli('localhost', 'root', 'root', 'fh_scm4_bookshop'); | |
if (mysqli_connect_errno()) { | |
die('Unable to connect to database.'); | |
} | |
return $con; | |
} | |
/** | |
* place query | |
* | |
* @return mixed | |
*/ | |
private static function query($connection, $query) { | |
$res = $connection->query($query); | |
if (!$res) { | |
die("Error in query \"" . $query . "\": " . $connection->error); | |
} | |
return $res; | |
} | |
/** | |
* get the key of the last inserted item | |
* | |
* @return integer | |
*/ | |
private static function lastInsertId($connection) { | |
return mysqli_insert_id($connection); | |
} | |
/** | |
* retrieve an object from the database result set | |
* | |
* @param object $cursor result set | |
* @return object | |
*/ | |
private static function fetchObject($cursor) { | |
return $cursor->fetch_object(); | |
} | |
/** | |
* remove the result set | |
* | |
* @param object $cursor result set | |
* @return null | |
*/ | |
private static function close($cursor) { | |
$cursor->close(); | |
} | |
/** | |
* close the database connection | |
* | |
* @param object $cursor resource of current database connection | |
* @return null | |
*/ | |
private static function closeConnection($connection) { | |
$connection->close(); | |
} | |
/** | |
* get the categories | |
* | |
* @return array of Category-items | |
*/ | |
public static function getCategories() { | |
$categories = array(); | |
$con = self::getConnection(); | |
$res = self::query($con, " | |
SELECT id, name | |
FROM categories; | |
"); | |
while ($cat = self::fetchObject($res)) { | |
$categories[] = new Category($cat->id, $cat->name); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $categories; | |
} | |
/** | |
* get the books per category | |
* | |
* @param integer $categoryId numeric id of the category | |
* @return array of Book-items | |
*/ | |
public static function getBooksForCategory($categoryId) { | |
$books = array(); | |
$con = self::getConnection(); | |
$categoryId = intval($categoryId); /* !!! */ | |
$res = self::query($con, " | |
SELECT id, categoryId, title, author, price | |
FROM books | |
WHERE categoryId = " . $categoryId . "; | |
"); | |
while ($book = self::fetchObject($res)) { | |
$books[] = new Book($book->id, $book->categoryId, $book->title, $book->author, $book->price); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $books; | |
} | |
/** | |
* get the books per search term | |
* | |
* note: search via LIKE | |
* | |
* @param string $term search term: book title string match | |
* @return array of Book-items | |
*/ | |
public static function getBooksForSearchCriteria($term) { | |
$books = array(); | |
$con = self::getConnection(); | |
$term = $con->real_escape_string($term); /* !!! */ | |
$res = self::query($con, " | |
SELECT id, categoryId, title, author, price | |
FROM books | |
WHERE title LIKE '%" . $term . "%'; | |
"); | |
while ($book = self::fetchObject($res)) { | |
$books[] = new Book($book->id, $book->categoryId, $book->title, $book->author, $book->price); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $books; | |
} | |
/** | |
* get the books per search term – paginated set only | |
* | |
* @param string $term search term: book title string match | |
* @param integer $offset start at the nth item | |
* @param integer $numPerPage number of items per page | |
* @return array of Book-items | |
*/ | |
public static function getBooksForSearchCriteriaWithPaging($term, $offset, $numPerPage) { | |
$con = self::getConnection(); | |
//query total count | |
$term = $con->real_escape_string($term); /* !!! */ | |
$res = self::query($con, " | |
SELECT COUNT(*) AS cnt | |
FROM books | |
WHERE title LIKE '%" . $term . "%'; | |
"); | |
$totalCount = self::fetchObject($res)->cnt; | |
self::close($res); | |
//query books to return | |
$books = array(); | |
$offset = intval($offset); /* !!! */ | |
$numPerPage = intval($numPerPage); /* !!! */ | |
$res = self::query($con, " | |
SELECT id, categoryId, title, author, price | |
FROM books | |
WHERE title LIKE '%" . $term . "%' | |
LIMIT " . $offset . ", " . $numPerPage . "; | |
"); | |
while ($book = self::fetchObject($res)) { | |
$books[] = new Book($book->id, $book->categoryId, $book->title, $book->author, $book->price); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return new PagingResult($books, $offset, $totalCount); | |
} | |
/** | |
* get the User item by id | |
* | |
* @param integer $userId uid of that user | |
* @return User | false | |
*/ | |
public static function getUserForId($userId) { | |
$user = null; | |
$con = self::getConnection(); | |
$userId = intval($userId); /* !!! */ | |
$res = self::query($con, " | |
SELECT id, userName, passwordHash | |
FROM users | |
WHERE id = " . $userId . "; | |
"); | |
if ($u = self::fetchObject($res)) { | |
$user = new User($u->id, $u->userName, $u->passwordHash); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $user; | |
} | |
/** | |
* get the User item by name | |
* | |
* @param string $userName name of that user - must be exact match | |
* @return User | false | |
*/ | |
public static function getUserForUserName($userName) { | |
$user = null; | |
$con = self::getConnection(); | |
$userName = $con->real_escape_string($userName); /* !!! */ | |
$res = self::query($con, " | |
SELECT id, userName, passwordHash | |
FROM users | |
WHERE userName = '" . $userName . "'; | |
"); | |
if ($u = self::fetchObject($res)) { | |
$user = new User($u->id, $u->userName, $u->passwordHash); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $user; | |
} | |
/** | |
* place to order with the shopping cart items | |
* | |
* note: wrapped in a transaction | |
* | |
* @param integer $userId id of the ordering user | |
* @param array $bookIds integers of book ids | |
* @param string $nameOnCard cc name | |
* @param string $cardNumber cc number | |
* @return integer | |
*/ | |
public static function createOrder($userId, $bookIds, $nameOnCard, $cardNumber) { | |
$con = self::getConnection(); | |
self::query($con, 'BEGIN;'); | |
$userId = intval($userId); /* !!! */ | |
$nameOnCard = $con->real_escape_string($nameOnCard); /* !!! */ | |
$cardNumber = $con->real_escape_string($cardNumber); /* !!! */ | |
self::query($con, " | |
INSERT INTO orders ( | |
userId | |
, creditCardNumber | |
, creditCardHolder | |
) VALUES ( | |
" . $userId . " | |
, '" . $cardNumber . "' | |
, '" . $nameOnCard . "' | |
); | |
"); | |
$orderId = self::lastInsertId($con); | |
$orderId = intval($orderId); /* !!! */ | |
foreach ($bookIds as $bookId) { | |
$bookId = intval($bookId); /* !!! */ | |
self::query($con, " | |
INSERT INTO orderedbooks ( | |
orderId, | |
bookId | |
) VALUES ( | |
" . $orderId . " | |
, " . $bookId . "); | |
"); | |
} | |
self::query($con, 'COMMIT;'); | |
self::closeConnection($con); | |
return $orderId; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* DataManager | |
* PDO Version | |
* | |
* | |
* @package | |
* @subpackage | |
* @author John Doe <jd@fbi.gov> | |
*/ | |
class DataManager { | |
private static $__connection; | |
/** | |
* connect to the database | |
* | |
* note: alternatively put those in parameter list or as class variables | |
* | |
* @return connection resource | |
*/ | |
private static function getConnection() { | |
if (!isset(self::$__connection)) { | |
self::$__connection = new PDO('mysql:host=localhost;dbname=fh_scm4_bookshop;charset=utf8', 'root', 'root'); | |
} | |
return self::$__connection; | |
} | |
/** | |
* place query | |
* | |
* note: using prepared statements | |
* see the filtering in bindValue() | |
* | |
* @return mixed | |
*/ | |
private static function query($connection, $query, $parameters = array()) { | |
$statement = $connection->prepare($query); | |
$i = 1; | |
foreach ($parameters as $param) { | |
if (is_int($param)) { | |
$statement->bindValue($i, $param, PDO::PARAM_INT); | |
} | |
if (is_string($param)) { | |
$statement->bindValue($i, $param, PDO::PARAM_STR); | |
} | |
$i++; | |
} | |
$statement->execute(); | |
return $statement; | |
} | |
/** | |
* get the key of the last inserted item | |
* | |
* @return integer | |
*/ | |
private static function lastInsertId($connection) { | |
return $connection->lastInsertId(); | |
} | |
/** | |
* retrieve an object from the database result set | |
* | |
* @param object $cursor result set | |
* @return object | |
*/ | |
private static function fetchObject($cursor) { | |
return $cursor->fetchObject(); | |
} | |
/** | |
* remove the result set | |
* | |
* @param object $cursor result set | |
* @return null | |
*/ | |
private static function close($cursor) { | |
$cursor->closeCursor(); | |
} | |
/** | |
* close the database connection | |
* | |
* note: not needed | |
* | |
* @param object $cursor resource of current database connection | |
* @return null | |
*/ | |
private static function closeConnection($connection) { | |
} | |
/** | |
* get the categories | |
* | |
* @return array of Category-items | |
*/ | |
public static function getCategories() { | |
$categories = array(); | |
$con = self::getConnection(); | |
$res = self::query($con, " | |
SELECT id, name | |
FROM categories; | |
"); | |
while ($cat = self::fetchObject($res)) { | |
$categories[] = new Category($cat->id, $cat->name); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $categories; | |
} | |
/** | |
* get the books per category | |
* | |
* note: see how prepared statements replace "?" with array element values | |
* | |
* @param integer $categoryId numeric id of the category | |
* @return array of Book-items | |
*/ | |
public static function getBooksForCategory($categoryId) { | |
$books = array(); | |
$con = self::getConnection(); | |
$res = self::query($con, " | |
SELECT id, categoryId, title, author, price | |
FROM books | |
WHERE categoryId = ?; | |
", array($categoryId)); | |
while ($book = self::fetchObject($res)) { | |
$books[] = new Book($book->id, $book->categoryId, $book->title, $book->author, $book->price); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $books; | |
} | |
/** | |
* get the books per search term | |
* | |
* note: search via LIKE | |
* | |
* @param string $term search term: book title string match | |
* @return array of Book-items | |
*/ | |
public static function getBooksForSearchCriteria($term) { | |
$books = array(); | |
$con = self::getConnection(); | |
$res = self::query($con, " | |
SELECT id, categoryId, title, author, price | |
FROM books | |
WHERE title LIKE ?; | |
", array("%" . $term . "%")); | |
while ($book = self::fetchObject($res)) { | |
$books[] = new Book($book->id, $book->categoryId, $book->title, $book->author, $book->price); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $books; | |
} | |
/** | |
* get the books per search term – paginated set only | |
* | |
* @param string $term search term: book title string match | |
* @param integer $offset start at the nth item | |
* @param integer $numPerPage number of items per page | |
* @return array of Book-items | |
*/ | |
public static function getBooksForSearchCriteriaWithPaging($term, $offset, $numPerPage) { | |
$con = self::getConnection(); | |
//query total count | |
$res = self::query($con, " | |
SELECT COUNT(*) AS cnt | |
FROM books | |
WHERE title LIKE ?; | |
", array("%" . $term . "%")); | |
$totalCount = self::fetchObject($res)->cnt; | |
self::close($res); | |
//query books to return | |
$books = array(); | |
$res = self::query($con, " | |
SELECT id, categoryId, title, author, price | |
FROM books | |
WHERE title | |
LIKE ? LIMIT ?, ?; | |
", array("%" . $term . "%", intval($offset), intval($numPerPage))); | |
while ($book = self::fetchObject($res)) { | |
$books[] = new Book($book->id, $book->categoryId, $book->title, $book->author, $book->price); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return new PagingResult($books, $offset, $totalCount); | |
} | |
/** | |
* get the User item by id | |
* | |
* @param integer $userId uid of that user | |
* @return User | false | |
*/ | |
public static function getUserForId($userId) { | |
$user = null; | |
$con = self::getConnection(); | |
$res = self::query($con, " | |
SELECT id, userName, passwordHash | |
FROM users | |
WHERE id = ?; | |
", array($userId)); | |
if ($u = self::fetchObject($res)) { | |
$user = new User($u->id, $u->userName, $u->passwordHash); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $user; | |
} | |
/** | |
* get the User item by name | |
* | |
* @param string $userName name of that user - must be exact match | |
* @return User | false | |
*/ | |
public static function getUserForUserName($userName) { | |
$user = null; | |
$con = self::getConnection(); | |
$res = self::query($con, " | |
SELECT id, userName, passwordHash | |
FROM users | |
WHERE userName = ?; | |
", array($userName)); | |
if ($u = self::fetchObject($res)) { | |
$user = new User($u->id, $u->userName, $u->passwordHash); | |
} | |
self::close($res); | |
self::closeConnection($con); | |
return $user; | |
} | |
/** | |
* place to order with the shopping cart items | |
* | |
* note: wrapped in a transaction | |
* | |
* @param integer $userId id of the ordering user | |
* @param array $bookIds integers of book ids | |
* @param string $nameOnCard cc name | |
* @param string $cardNumber cc number | |
* @return integer | |
*/ | |
public static function createOrder($userId, $bookIds, $nameOnCard, $cardNumber) { | |
$con = self::getConnection(); | |
self::query($con, 'BEGIN;'); | |
self::query($con, " | |
INSERT INTO orders ( | |
userId | |
, creditCardNumber | |
, creditCardHolder | |
) VALUES ( | |
? | |
, ? | |
, ? | |
); | |
", array($userId, $cardNumber, $nameOnCard)); | |
$orderId = self::lastInsertId($con); | |
foreach ($bookIds as $bookId) { | |
self::query($con, " | |
INSERT INTO orderedbooks ( | |
orderId | |
, bookId | |
) VALUES ( | |
? | |
, ? | |
);", array($orderId, $bookId)); | |
} | |
self::query($con, 'COMMIT;'); | |
self::closeConnection($con); | |
return $orderId; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php require_once('views/partials/header.php'); ?> | |
<div class="page-header"> | |
<h2>Welcome</h2> | |
</div> | |
<p>Welcome to the SCM4 book shop!</p> | |
<?php require_once('views/partials/footer.php'); ?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta charset="utf-8"> | |
<title>SCM4 Book Shop</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link href="assets/bootstrap/css/bootstrap.min.css" rel="stylesheet"> | |
<link href="assets/bootstrap/css/bootstrap-theme.min.css" rel="stylesheet"> | |
<link href="assets/main.css" rel="stylesheet"> | |
</head> | |
<body> | |
<div class="navbar navbar-inverse navbar-fixed-top"> | |
<div class="container-fluid"> | |
<!-- Brand and toggle get grouped for better mobile display --> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-navbar-collapse-1"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand" href="/">SCM 4 Bookshop (V 2.0)</a> | |
</div> | |
<div class="navbar-collapse collapse" id="bs-navbar-collapse-1"> | |
<ul class="nav navbar-nav"> | |
<li><a href="index.php">Home</a></li> | |
<li><a href="index.php?view=list">List</a></li> | |
<li><a href="index.php?view=search">Search</a></li> | |
<li><a href="index.php?view=checkout">Checkout</a></li> | |
</ul> | |
</div><!--/.nav-collapse --> | |
</div> | |
</div> | |
<div class="container"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$user = AuthenticationManager::getAuthenticatedUser(); | |
$cartSize = ShoppingCart::size(); | |
if (isset($_GET["errors"])) { | |
$errors = unserialize(urldecode($_GET["errors"])); | |
} | |
// /needed for shopping cart | |
?> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta charset="utf-8"> | |
<title>SCM4 Book Shop</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link href="assets/bootstrap/css/bootstrap.min.css" rel="stylesheet"> | |
<link href="assets/bootstrap/css/bootstrap-theme.min.css" rel="stylesheet"> | |
<link href="assets/main.css" rel="stylesheet"> | |
</head> | |
<body> | |
<div class="navbar navbar-inverse navbar-fixed-top"> | |
<div class="container-fluid"> | |
<!-- Brand and toggle get grouped for better mobile display --> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-navbar-collapse-1"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand" href="/">SCM 4 Bookshop (V 2.0)</a> | |
</div> | |
<div class="navbar-collapse collapse" id="bs-navbar-collapse-1"> | |
<ul class="nav navbar-nav"> | |
<li <?php if ($view === 'welcome') { ?>class="active"<?php } ?>><a href="index.php">Home</a></li> | |
<li <?php if ($view === 'list') { ?>class="active"<?php } ?>><a href="index.php?view=list">List</a></li> | |
<li <?php if ($view === 'search') { ?>class="active"<?php } ?>><a href="index.php?view=search">Search</a></li> | |
<li <?php if ($view === 'checkout') { ?>class="active"<?php } ?>><a href="index.php?view=checkout">Checkout</a></li> | |
</ul> | |
<ul class="nav navbar-nav navbar-right login"> | |
<li> | |
<a href="index.php?view=checkout"> | |
<span class="badge"><?php echo Util::escape($cartSize); ?></span> <span class="glyphicon glyphicon-shopping-cart" aria-hidden="true"></span></a> | |
</li> | |
<li class="dropdown"> | |
<?php if ($user == null): ?> | |
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> | |
Not logged in! | |
<b class="caret"></b> | |
</a> | |
<ul class="dropdown-menu" role="menu"> | |
<li> | |
<a href="index.php?view=login">Login now</a> | |
</li> | |
</ul> | |
<?php else: ?> | |
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> | |
Logged in as <span class="badge"><?php echo Util::escape($user->getUserName()); ?></span> | |
<b class="caret"></b> | |
</a> | |
</a> | |
<ul class="dropdown-menu" role="menu"> | |
<li class="centered"> | |
<form method="post" action="<?php echo Util::action('logout'); ?>"> | |
<input class="btn btn-xs" role="button" type="submit" value="Logout" /> | |
</form> | |
</li> | |
</ul> | |
<?php endif; ?> | |
</li> | |
</ul> <!-- /. login --> | |
</div><!--/.nav-collapse --> | |
</div> | |
</div> | |
</div> | |
<div class="container"> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('inc/bootstrap.inc.php'); | |
$view = isset($_REQUEST['view']) ? $_REQUEST['view'] : 'welcome'; | |
$postAction = isset($_REQUEST['action']) ? $_REQUEST['action'] : null; | |
/* if we have a form post, invoke the controller */ | |
if ($postAction != null) | |
Controller::getInstance()->invokePostAction(); | |
/** | |
* no post action - switch the views via GET parameter | |
**/ | |
if (file_exists(__DIR__ .'/views/' . $view .'.php')) | |
require_once('views/' . $view .'.php'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$categories = DataManager::getCategories(); | |
$categoryId = isset($_REQUEST['categoryId']) ? (int) $_REQUEST['categoryId'] : null; | |
$books = (isset($categoryId) && ($categoryId > 0)) ? DataManager::getBooksForCategory($categoryId) : null; | |
require('views/partials/header.php'); | |
?> | |
<div class="page-header"> | |
<h2>List of books by category</h2> | |
</div> | |
<ul class="nav nav-tabs"> | |
<?php foreach ($categories as $cat) : ?> | |
<li role="presentation" | |
<?php if ($cat->getId() === $categoryId) { ?>class="active" <?php } ?>> | |
<a href="<?php echo $_SERVER['PHP_SELF'] ?>?view=list&categoryId=<?php echo urlencode($cat->getId()); ?>"><?php echo Util::escape($cat->getName()); ?></a></span> | |
<?php endforeach; ?> | |
</ul> | |
<br /> | |
<?php if (isset($books)) : ?> | |
<?php | |
if (sizeof($books) > 0) : | |
require('views/partials/booklist.php'); | |
else : | |
?> | |
<div class="alert alert-warning" role="alert">No books in this category.</div> | |
<?php endif; ?> | |
<?php else : ?> | |
<div class="alert alert-info" role="alert">Please select a category.</div> | |
<?php endif; ?> | |
<?php | |
require('views/partials/footer.php'); | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//as soon as shopping cart is referenced the session context is created | |
SessionContext::create(); | |
/** | |
* ShoppingCart | |
* | |
* | |
* @package | |
* @subpackage | |
* @author John Doe <jd@fbi.gov> | |
*/ | |
class ShoppingCart extends BaseObject { | |
/** | |
* add an item to the shopping cart | |
* | |
* @param integer $bookId book id | |
* @return null | |
*/ | |
public static function add($bookId) { | |
} | |
/** | |
* remove an item from the shopping cart | |
* | |
* @param integer $bookId book id | |
* @return null | |
*/ | |
public static function remove($bookId) { | |
} | |
/** | |
* empty the shopping cart | |
* | |
* @return null | |
*/ | |
public static function clear() { | |
} | |
/** | |
* check if an item is in the shopping cart | |
* | |
* @param integer $bookId book id | |
* @return boolean | |
*/ | |
public static function contains($bookId) { | |
} | |
/** | |
* get number of shopping cart items | |
* | |
* @return integer | |
*/ | |
public static function size() { | |
} | |
/** | |
* get the shopping cart contents | |
* | |
* @return ShoppingCart object | empty array | |
*/ | |
public static function getAll() { | |
} | |
/** | |
* add an item to the shopping cart | |
* | |
* @return ShoppingCart object | empty array | |
*/ | |
private static function getCart() { | |
} | |
/** | |
* make shopping cart persistent | |
* | |
* @return null | |
*/ | |
private static function storeCart(array $cart) { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$orderId = isset($_REQUEST['orderId']) ? $_REQUEST['orderId'] : null; | |
require_once('views/partials/header.php'); | |
?> | |
<div class="page-header"> | |
<h2>Success!</h2> | |
</div> | |
<p>Thank you for your purchase.</p> | |
<?php if ($orderId != null) : ?> | |
<p>Your order number is <?php echo Util::escape($orderId); ?>.</p> | |
<?php endif; ?> | |
<?php /* TODO order Summary */ ?> | |
<?php | |
require_once('views/partials/footer.php'); | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Util extends BaseObject { | |
/** | |
* bereinigt den output | |
* | |
* @param string $string der string | |
* @return string | |
*/ | |
public static function escape($string) { | |
return nl2br(htmlentities($string)); | |
} | |
/** | |
* redirect mit optionaler url - HINWEIS - redirection attack möglich! | |
* | |
* @param string $string uri optional | |
* @return null | |
*/ | |
public static function redirect($page = null) { | |
if ($page == null) { | |
$page = isset($_REQUEST['page']) ? | |
$_REQUEST['page'] : | |
$_SERVER['REQUEST_URI']; | |
} | |
header("Location: $page"); | |
} | |
/** | |
* GET parameter "page" adds current page to action so that a redirect | |
* back to this page is possible after successful execution of POST action | |
* if "page" has been set before then just keep the current value (to avoid | |
* problem with "growing URLs" when a POST form is rendered "a second time" | |
* e.g. during a forward after an unsuccessful POS action) | |
* | |
* Be sure to check for invalid / insecure page redirects!! | |
* | |
* @param string $action uri optional | |
* @param string $params array key/value pairs | |
* @return null | |
*/ | |
public static function action($action, $params = null) { | |
$page = isset($_REQUEST['page']) ? | |
$_REQUEST['page'] : | |
$_SERVER['REQUEST_URI']; | |
$res = 'index.php?action=' . rawurlencode($action) . '&page=' . rawurlencode($page); | |
if (is_array($params)) { | |
foreach ($params as $name => $value) { | |
$res .= '&' . rawurlencode($name) . '=' . rawurlencode($value); | |
} | |
} | |
return $res; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php require_once('views/partials/header.php'); ?> | |
<div class="page-header"> | |
<h2>Welcome</h2> | |
</div> | |
<p>Welcome to the SCM4 book shop!</p> | |
<?php require_once('views/partials/footer.php'); ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment