Friends and Relationships [ 122 ] Relationships Our relationships table needs to relate two users together, as well as record the type of relationship. Since some relationships require mutual consent, we need to indicate if the non-requesting user accepted the request to connect. The following is a suitable structure for our relationships table: Field Type Description ID Integer, Primary Key, Auto Increment A unique ID for the relationship between the two users Type ID The type of relationship (a reference to the relationship_types table) Usera Integer The user who initiated the relationship, a relation to the users table Userb Integer The user who usera initiated a relationship with, a relation to the users table Accepted Boolean Indicates if this is a mutual relationship (which is only used if the relationship type is a mutual relationship) Adding friends Our users can see other users on the site, either by searching for them or viewing a list of users; from here we can add a link to enable the user to form a relationship. We have a suitable database structure to facilitate this, but we now need functionality to connect our users together. Forming a relationship Let's walk through what the process should be for our users to form relationships with each other: 1. View the listing of the user they wish to connect with. 2. Click on a link, or select a relationship type from a list and click on Submit. 3. Check for pre-existing relationships. 4. Check if the relationship type selected is active. 5. Create the relationship in the database. This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Chapter 4 [ 123 ] 6. If the relationship type is mutual, e-mail the other user an approval request message asking them to conrm the relationship. 7. If the relationship type isn't mutual, e-mail the other user a message informing them that someone has connected with them. We already have step one set up—the list of members. For step two, we either need a link on the user's name or a list of relationship types (which are links or part of a form submission). Since we have a number of relationship types, let's list the types of relationships in a drop-down list next to each member as part of a form the user can submit to create a relationship of that type. We should create a relationships model for listing relationships, and while we don't need to do this yet, we could use it to display lists of relationship types too. A simple method in the model that we can call from the controller to give us a list of relationship types will sufce. The method below can return a database cache of the results provided we instruct it to with the $cache parameter. public function getTypes( $cache=false ) { $sql = "SELECT ID as type_id, name as type_name, plural_name as type_plural_name, mutual as type_mutual FROM relationship_types WHERE active=1"; if( $cache == true ) { $cache = $this->registry->getObject('db')->cacheQuery( $sql ); return $cache; } else { $types = array(); while( $row = $this->registry->getObject('db')->getRows() ) { $types[] = $row; } return $types; } } This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Friends and Relationships [ 124 ] With a list of types at our disposal, we now need to get them and send them to the template, but only if we are logged in. This should be a new method in our members controller, which is called after the paginated list is generated (to prevent it being called when there are no results, and to ensure the code isn't duplicated in the different listing methods in the controller). private function formRelationships() { if( $this->registry->getObject('authenticate')->isLoggedIn() == true ) { require_once( FRAMEWORK_PATH . 'models/relationships.php'); $relationships = new Relationships( $this->registry ); $types = $relationships->getTypes( true ); If the user is logged in, then a template bit containing a form is placed next to each member listing, and within there a list of relationship types is dynamically inserted. $this->registry->getObject('template')->addTemplateBit( 'form_relationship', 'members/form_relationship.tpl.php'); $this->registry->getObject('template')->getPage()->addPPTag( 'relationship_types', array( 'SQL', $types ) ); } else { If the user isn't logged in, then we don't want to show them a form, so we set the tag to either nothing, or a placeholder comment. $this->registry->getObject('template')->getPage()->addTag( 'form_relationship', '<! relationship types dropdown >' ); } } We now need to change our member listing template to have a {form_relationship} tag within the members list loop. <! START members > <p><strong>{name}</strong></p> <p>Keeper of <strong>{dino_name}</strong> a <strong>{dino_gender} {dino_breed}</strong></p> {form_relationship} <hr /> <! END members > This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Chapter 4 [ 125 ] Finally, we need a template that is inserted into the template variable when the user is logged in (views/default/templates/members/form_relationship.tpl.php). This has a template loop within it, into which the relationship types are inserted. <form action="relationship/create/{ID}" method="post"> <select name="relationship_type"> <! START relationship_types > <option value="{type_id}">{type_name}</option> <! END relationship_types > </select> <input type="submit" name="create" value="Connect with {name}" /> </form> Now if we are logged in and take a look at our members list, we see a form next to each member allowing us to connect with them. This is a good start, but of course if we or a user clicks on the button, nothing is going to happen. We now need to create the relationship. To do this we are going to need a relationship model and a relationship controller. Relationship model This needs to encapsulate the data from the relationships table for a specic relationship, as well as delete, approve, and update existing relationships, and create new relationships. <?php class Relationship{ This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Friends and Relationships [ 126 ] As usual, we start with a number of class variables for the data the model represents to be stored in, and one for our registry too. private $registry; private $usera; private $userb; private $approved; private $id = 0; private $type; Our constructor needs to create a new relationship for us, if no ID is passed; otherwise, it needs to look up an existing relationship in the database. If an ID is passed, it simply queries the database and populates the class variables accordingly. /** * Relationship constructor * @param Registry $registry the registry * @param int $id the relationship ID * @param int $usera the id of user a * @param int $userb the id of user b * @param bool $approved if the relationship is approved * @param int $type the ID of the relationship type * @return void */ public function __construct( Registry $registry, $id=0, $usera, $userb, $approved=0, $type=0 ) { $this->registry = $registry; // if no ID is passed, then we want to create a new relationship if( $id == 0 ) { $this->createRelationship( $usera, $userb, $approved, $type ); } else { // if an ID is passed, populate based off that $sql = "SELECT * FROM relationships WHERE ID=" . $id; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->numRows() == 1 ) { $data = $this->registry->getObject('db')->getRows(); $this->populate( $data['ID'], $data['usera'], $data['userb'], $data['type'], $data['approved'] ); } } } This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Chapter 4 [ 127 ] When creating a new relationship, there are a number of checks that must be done rst: • It must check to ensure there isn't a pre-existing relationship; if there is, we can't create a new one! • If one doesn't exist, then it must check the type of relationship: ° If the relationship type is "mutual", then we set the approved eld to 1, to indicate that the recipient friend must approve the relationship ° The relationship is then created in the database /** * Create a new relationship where one currently doesn't exist, if one does exist, populate from that */ public function createRelationship( $usera, $userb, $approved=0, $type=0 ) { // check for pre-existing relationship $sql = "SELECT * FROM relationships WHERE (usera={$usera} AND userb={$userb}) OR (usera={$userb} AND userb={$userc})"; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->numRows() == 1 ) { // one exists: populate $data = $this->registry->getObject('db')->getRows(); $this->populate( $data['ID'], $data['usera'], $data['userb'], $data['type'], $data['approved'] ); } else { // one doesnt exist if( $type != 0 ) { // check type for mutual $sql = "SELECT * FROM relationship_types WHERE ID=" . $type; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->numRows() == 1 ) { $data = $this->registry->getObject('db')->getRows(); // auto approve non-mutual relationships if( $data['mutual'] == 0 ) This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Friends and Relationships [ 128 ] { $approved = 1; } } // create the relationsip $insert = array(); $insert['usera'] = $usera; $insert['userb'] = $userb; $insert['type'] = $type; $insert['approved'] = $approved; $this->registry->getObject('db')->insertRecords( 'relationships', $insert ); $this->id = $this->registry->getObject('db')->lastInsertID(); } } } The model also requires a setter method to update the approved status of the relationship. /** * Approve relationship * @return void */ public function approveRelationship() { $this->approved = true; } A delete method is also useful to delete the relationship. /** * Delete relationship * @return void */ public function delete() { $this->registry->getObject('db')->deleteRecords( 'relationships', 'ID=' . $this->id, 1 ); $this->id = 0; } This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Chapter 4 [ 129 ] We have our standard save method that either creates a new record or updates an existing one depending on whether the relationship is being created or saved. /** * Save relationship * @return void */ public function save() { $changes = array(); $changes['usera'] = $this->usera; $changes['userb'] = $this->userb; $changes['type'] = $this->type; $changes['accepted'] = $this->accepted; $this->registry->getObject('db')->updateRecords( 'relationships', $changes, "ID=" . $this->id ); } /** * Populate relationship object * @param int $id the user id * @param int $usera user a * @param int $userb user b * @param int $type the type * @param bool $approved * @return void */ private function populate( $id, $usera, $userb, $type, $approved ) { $this->id = $id; $this->type = $type; $this->usera = $usera; $this->userb = $userb; $this->approved = $approved; } } ?> Relationship controller With a relationship model in place to make creating, updating, and deleting relationships easy, we need a controller to process the user's request to create, approve, or reject a relationship. private function createRelationship( $userb ) { if( $this->registry->getObject('authenticate')->isLoggedIn() ) { This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Friends and Relationships [ 130 ] If we are logged in, then we take our user ID, the ID of the user we wish to connect with, and the relationship type, and create our relationship. $usera = $this->registry->getObject('authenticate')-> getUser()->getUserID(); $type = intval( $_POST['relationship_type'] ); require_once( FRAMEWORK_PATH . 'models/relationship.php'); $relationship = new Relationship( $this->registry, 0, $usera, $userb, $type, 0 ); if( $relationship->isApproved() ) { If the relationship is automatically approved, we can e-mail the user to tell them they have a new connection, and then display a message to the logged in user. // email the user, tell them they have a new connection /** * Can you remember how the email sending object works? */ $this->registry->errorPage('Relationship created', 'Thank you for connecting!'); } else { If the relationship isn't automatically approved, we can e-mail the user to tell them they have a new pending connection, and display a message to the logged in user. // email the user, tell them they have a new pending connection /** * Can you remember how the email sending object works? */ $this->registry->errorPage('Request sent', 'Thanks for requesting to connect!'); } } else { If the user isn't logged in, we display an error message. $this->registry->errorPage('Please login', 'Only logged in members can connect on this site'); // display an error } } This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com Chapter 4 [ 131 ] Now if we click on the Connect with button on the relationship form, our relationship is created and we are shown a conrmation message. This could be expanded in the future to use AJAX to display the notication on the previous page, without causing the page to reload. Mutual relationships—accepting or rejecting a request If a relationship type is mutual, we need users to be able to see, accept, and reject these requests. • View list of requests • Accept: Update the database record • Reject: Remove the database record Pending requests Pending requests can be found by querying the database for relationships where the userb column is the current logged in user, and the relationship isn't approved. This query should be in our relationships model. New model method Our model method should take parameters for usera, userb, and approved, and if either of the user parameters are set, lter based on those users, returning a cached query. public function getRelationships( $usera, $userb, $approved=0 ) { $sql = "SELECT t.name as type_name, t.plural_name as This material is copyright and is licensed for the sole use by RAYMOND ERAZO on 25th October 2010 3146 KERNAN LAKE CIRCLE, JACKSONVILLE, 32246 Download from www.eBookTM.com . $id; $this->registry->getObject('db' )-& gt;executeQuery( $sql ); if( $this->registry->getObject('db' )-& gt;numRows() == 1 ) { $data = $this->registry->getObject('db' )-& gt;getRows(); . $type; $this->registry->getObject('db' )-& gt;executeQuery( $sql ); if( $this->registry->getObject('db' )-& gt;numRows() == 1 ) { $data = $this->registry->getObject('db' )-& gt;getRows(); . $this->registry->getObject('db' )-& gt;executeQuery( $sql ); if( $this->registry->getObject('db' )-& gt;numRows() == 1 ) { // one exists: populate $data = $this->registry->getObject('db' )-& gt;getRows();