Skip to content

Instantly share code, notes, and snippets.

@redspider
Created December 7, 2010 05:12
Show Gist options
  • Save redspider/731489 to your computer and use it in GitHub Desktop.
Save redspider/731489 to your computer and use it in GitHub Desktop.
Model session example rewrite
<?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