Created
December 7, 2010 05:12
-
-
Save redspider/731489 to your computer and use it in GitHub Desktop.
Model session example rewrite
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 | |
/* Model class for Sessions | |
A session represents an independent teaching segment. The following rules | |
definitely apply: | |
1. A session has one single owner, the expert. | |
2. A session has the following properties associated directly with it: | |
2.1. A title, indicating the topic of the segment. | |
2.2. A level rating, indicating the difficulty | |
2.3. A price, in a single as-yet-undetermined currency, possibly 0. | |
2.4. A duration, indicating how long the segment will last | |
2.5. A description, a free-text description of the segment | |
3. A session has at least one date and time. | |
4. A session has zero or more users, subscribed to one time each. | |
The following rules may apply: | |
5. A session title, and other topic-related information, cannot be changed | |
by the owner after a user has subscribed. (Prevents bait-and-switch) | |
6. A user subscription is not complete until they have paid, if a price | |
is required. | |
7. A session may have a maximum subscription size. | |
8. A session may have a minimum subscription size, below which the expert will not | |
teach the class. | |
9. Additional properties may include: | |
9.1. Objectives, a list of objectives the segment aims to achieve. | |
9.2. Pre-requisites, a list of recommended (not enforced) requirements for | |
getting the most out of the segment. | |
9.3. Teaching style, a free-form text description of the style of teaching used | |
9.4. Related sessions, a one-to-many map to other sessions, possibly generated | |
automatically. | |
10. Participants who completed the course may be able to leave comments/reviews. | |
*/ | |
/* | |
The following class demonstrates a simple implementation of the above 'definite' | |
requirements. | |
Style notes: | |
* Namespace once. If your method is within Model_session, it's not necessary | |
to put "session" into the method name explicitly. | |
* Err in favor of explicitness. If you have multiple id references floating | |
around, avoid the use of $id. | |
* Use guard clauses, return as soon as possible. | |
* Name variables appropriately. $data is rarely a good explanation. | |
* Variables being handed to functions should be as explicit as possible. No $post_data. | |
* Updates and deletes should return rows affected. Inserts should return the insert ID | |
* Name things as best you can, session_master and session_instance is not clear - what are they really? | |
* Avoid using variables where you don't need them | |
* Don't hesitate to use them if you think you do. | |
* Comment things that aren't obvious. | |
* Always put braces around IF conditions. You never know when you might add | |
another statement and indentation can go awry. | |
*/ | |
/* | |
SQL Schema notes (May require INNODB engine) | |
CREATE TABLE session_status ( | |
qname VARCHAR(32) PRIMARY KEY | |
); | |
CREATE TABLE session ( | |
id INT PRIMARY KEY AUTO_INCREMENT, | |
owner_id INT REFERENCES user(id), | |
title VARCHAR(255), | |
status VARCHAR(32) REFERENCES session_status(qname), | |
level ? | |
price DECIMAL(10,2), | |
duration INT, | |
description TEXT | |
); | |
CREATE TABLE session_time ( | |
id INT PRIMARY KEY AUTO_INCREMENT, | |
session_id INT REFERENCES session(id), | |
starts_at TIMESTAMP | |
); | |
CREATE TABLE subscription ( | |
id INT PRIMARY KEY AUTO_INCREMENT, | |
session_id INT REFERENCES session(id), | |
session_time_id INT REFERENCES session_time(id), | |
user_id INT REFERENCES user(id) | |
); | |
*/ | |
class SessionTimeInUseException extends Exception {}; | |
class Model_session extends Model { | |
function Model_session() | |
{ | |
parent::Model(); | |
} | |
/* Retrieve all available sessions. By nature this is a temporary | |
function - when there are 10,000+ sessions it will not be practical. | |
Set $hidden to TRUE to retrieve all sessions, not just ones with | |
status 'visible'. | |
*/ | |
function find($hidden = FALSE) { | |
if ($hidden == FALSE) | |
{ | |
$this->db->where('status','visible'); | |
} | |
$sessions = $this->db->get('session'); | |
return $sessions->result(); | |
} | |
/* Add a new session, status defaults to 'hidden' */ | |
function add($owner, $title, $level, $price, $duration, $description) | |
{ | |
$this->db->insert('session', array( | |
'owner' => $owner, | |
'title' => $title, | |
'level' => $level, | |
'price' => $price, | |
'duration' => $duration, | |
'description' => $description, | |
'status' => 'hidden' | |
)); | |
return $this->db->insert_id(); | |
} | |
/* Remove a session completely */ | |
function delete($session_id) | |
{ | |
$this->db->delete('session', array('id' => $session_id)); | |
return $this->db->affected_rows(); | |
} | |
/* Retrieve a single session based on ID */ | |
function get($session_id) | |
{ | |
$sessions = $this->db->get('session')->where('id', $session_id); | |
if ($sessions->num_rows() == 0) | |
{ | |
return NULL; | |
} | |
return $sessions->row(); | |
} | |
/* Update a sessions basic data */ | |
function update($session_id, $title, $level, $price, $duration, $description) | |
{ | |
$this->db->where('id', $session_id); | |
$this->db->update('session', array( | |
'owner' => $owner, | |
'title' => $title, | |
'level' => $level, | |
'price' => $price, | |
'duration' => $duration, | |
'description' => $description | |
)); | |
return $this->db->affected_rows(); | |
} | |
/* Set status for a session */ | |
function set_status($session_id, $status) | |
{ | |
assert(in_array($status, array('visible','hidden'))); // Debug check to verify status is valid | |
$this->db->where('id', $session_id); | |
$this->db->update('session', array('status' => $status)); | |
return $this->db->affected_rows(); | |
} | |
/* Add a time to a session */ | |
/* Rule deviation: add_time does not enforce the minimum-one-time | |
rule, for ease of UI purposes. A no-times session can (and does) | |
exist during creation */ | |
function add_time($session_id, $starts_at) | |
{ | |
$this->db->insert('session_time', array( | |
'session_id' => $session_id, | |
'starts_at' => $starts_at | |
)); | |
return $this->db->insert_id(); | |
} | |
/* Delete a time from a session */ | |
function delete_time($session_id, $time_id) | |
{ | |
/* This check enforces the rule that times that have subscribers cannot be | |
removed. It should not normally be needed however, it's a backstop to | |
UI failure. | |
Note that if the database has referential integrity and transactions | |
this check is redundant, it's merely here to illustrate a failure | |
condition being handled */ | |
if ($this->count_subscribers($session_id, $time_id) > 0) | |
{ | |
throw new SessionTimeInUseException('This session time currently has subscribers'); | |
} | |
$this->db->delete('session_time', array('session_id' => $session_id, 'id' => $time_id)); | |
return $this->db->affected_rows(); | |
} | |
/* Retrieve times currently available for a session */ | |
function find_times($session_id) | |
{ | |
$times = $this->db->get('session_time')->where('session_id', $session_id); | |
return $this->result(); | |
} | |
/* Subscribe user to a given session at a given time */ | |
/* In the event the user is already subscribed, will return | |
ID of existing subscription */ | |
function subscribe($session_id, $time_id, $user_id) | |
{ | |
// Search for existing subscription | |
$subscription = $this->get('subscription')->where(array( | |
'session_id' => $session_id, | |
'time_id' => $time_id, | |
'user_id' => $user_id | |
)); | |
// If it exists | |
if ($subscription->num_rows() > 0) | |
{ | |
$row = $subscription->row(); | |
// Return the ID | |
return $row->id; | |
} | |
// Otherwise insert a new one | |
$this->db->insert('subscription', array( | |
'session_id' => $session_id, | |
'time_id' => $time_id, | |
'user_id' => $user_id | |
)); | |
return $this->db->insert_id(); | |
} | |
/* Remove a subscription */ | |
function unsubscribe($subscription_id) | |
{ | |
$this->db->delete('subscription', array('subscription_id' => $subscription_id)); | |
return $this->db->affected_rows(); | |
} | |
/* List subscribers for a given time */ | |
/* Returns an array of users, augmented with a subscription_id */ | |
function find_subscribers($session_id, $time_id) | |
{ | |
// Use an INNER JOIN to link tables 'subscription' and 'user', to avoid making | |
// multiple calls to the database to obtain the user details for subscribers. | |
$subscribers = $this->db->query(' | |
SELECT | |
subscription.id AS subscription_id, user.* | |
FROM | |
subscription JOIN user ON user.id=subscription.user_id | |
WHERE | |
session_id=? AND time_id=?', array($session_id, $time_id)); | |
return $subscribers->result(); | |
} | |
/* Count subscribers for a given time */ | |
function count_subscribers($session_id, $time_id) | |
{ | |
$this->db->where('session_id', $session_id); | |
$this->db->where('time_id', $time_id); | |
return $this->db->count_all_results(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment