Friends and Relationships [ 112 ] addTag( 'previous', '' ); } else { $this->registry->getObject('template')->getPage()-> addTag( 'first', "<a href='members/list/'>First page</a>" ); $this->registry->getObject('template')->getPage()-> addTag( 'previous', "<a href='members/list/" . ( $pagination->getCurrentPage() - 2 ) . "'>Previous page</a>" ); } if( $pagination->isLast() ) { $this->registry->getObject('template')->getPage()-> addTag( 'next', '' ); $this->registry->getObject('template')->getPage()-> addTag( 'last', '' ); } else { $this->registry->getObject('template')->getPage()-> addTag( 'first', "<a href='members/list/" . $pagination->getCurrentPage() . "'>Next page</a>" ); $this->registry->getObject('template')->getPage()-> addTag( 'previous', "<a href='members/list/" . ( $pagination->getNumPages() - 1 ) . "'>Last page</a>" ); } } } To actually display the results of the lookup to the user, we need a template to form our members' list view. This is essentially a copy of the main template le, with a template loop for the members' information. This is saved in the views/default/ templates/members/list.tpl.php le: <div id="main"> <div id="rightside"> </div> <div id="content"> <h1>DINO SPACE! Members List</h1> <! START members > <p><strong>{name}</strong></p> <p>Keeper of <strong>{dino_name}</strong> a <strong>{dino_gender} {dino_breed}</strong></p> 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 [ 113 ] <hr /> <! END members > <p>Viewing page {page_number} of {num_pages}</p> <p>{first} {previous} {next} {last}</p> </div> </div> This then displays our users list, which currently only contains me, as shown below!: Paginated users by letter As the site grows, but before we have such a large user base we need to consider listing more relevant users, we are going to end up with a paginated list, which isn't particularly easy to navigate. For example, if we have 20 users listed on each page and 100 pages of results, if our user wants to quickly jump to users with a surname beginning with P, it may take them several attempts. To make this easier, we can also provide ltering by alphabetical character, so the user can click on P and be taken to a list of users with surnames beginning with P. These lists may also be long, so they too should be paginated. The required model method to do this takes an additional parameter, which is the letter the surname should start with. This is sanitized and then passed to the query—the query works slightly differently by searching for spaces in the user's name, then taking the word before the nal space and comparing the rst letter of this word to the letter passed: /** * Generated paginated members list by surname * @param String $letter * @param int $offset the offset * @return Object pagination object 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 [ 114 ] */ public function listMembersByLetter( $letter='A', $offset=0 ) { $alpha = strtoupper( $this->registry->getObject('db')-> sanitizeData( $letter ) ); require_once( FRAMEWORK_PATH . 'lib/pagination/pagination.class.php'); $paginatedMembers = new Pagination( $this->registry ); $paginatedMembers->setLimit( 25 ); $paginatedMembers->setOffset( $offset ); $query = "SELECT u.ID, u.username, p.name, p.dino_name, p.dino_gender, p.dino_breed FROM users u, profile p WHERE p.user_id=u.ID AND u.active=1 AND u.banned=0 AND u.deleted=0 AND SUBSTRING_INDEX(p.name,' ', -1)LIKE'".$alpha."%' ORDER BY SUBSTRING_INDEX(p.name,' ', -1) ASC"; $paginatedMembers->setQuery( $query ); $paginatedMembers->setMethod( 'cache' ); $paginatedMembers->generatePagination(); return $paginatedMembers; } Let's take a look at how this code ts together in our controller. The only real difference is the method we call in our model: private function listMembersAlpha( $alpha='A', $offset=0 ) { Require and create our members model: require_once( FRAMEWORK_PATH . 'models/members.php'); $members = new Members( $this->registry ); Call the listMembersByLetter method to get our pagination object: $pagination = $members->listMembersByLetter( $alpha, $offset ); if( $pagination->getNumRowsPage() == 0 ) { If there are no members, show that view: $this->registry->getObject('template')-> buildFromTemplates('header.tpl.php', 'members/invalid.tpl.php' , 'footer.tpl.php'); } else { 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 [ 115 ] If there are members, show that view, and insert the appropriate data into the view: $this->registry->getObject('template')-> buildFromTemplates('header.tpl.php', 'members/list.tpl.php', 'footer.tpl.php'); $this->registry->getObject('template')->getPage()-> addTag( 'members', array( 'SQL', $pagination->getCache() ) ); $this->registry->getObject('template')->getPage()-> addTag( 'letter', " - Letter: " . $alpha ); $this->registry->getObject('template')->getPage()-> addTag( 'page_number', $pagination->getCurrentPage() ); $this->registry->getObject('template')->getPage()-> addTag( 'num_pages', $pagination->getNumPages() ); if( $pagination->isFirst() ) { $this->registry->getObject('template')->getPage()-> addTag( 'first', ''); $this->registry->getObject('template')->getPage()-> addTag( 'previous', '' ); } else { $this->registry->getObject('template')->getPage()-> addTag( 'first', "<a href='members/alpha/".$alpha."/'>First page</a>" ); $this->registry->getObject('template')->getPage()-> addTag( 'previous', "<a href='members/alpha/".$alpha."/" . ( $pagination->getCurrentPage() - 2 ) . "'>Previous page</a>" ); } if( $pagination->isLast() ) { $this->registry->getObject('template')->getPage()-> addTag( 'next', '' ); $this->registry->getObject('template')->getPage()-> addTag( 'last', '' ); } else { $this->registry->getObject('template')->getPage()-> addTag( 'first', "<a href='members/alpha/".$alpha."/" . $pagination->getCurrentPage() . "'>Next page</a>" ); $this->registry->getObject('template')->getPage()-> addTag( 'previous', "<a href='members/alpha/".$alpha."/" . ( $pagination->getNumPages() - 1 ) . "'>Last page</a>" ); } } } 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 [ 116 ] We need to slightly update our members' list template; it needs a template variable to display the currently active letter, and the letters A – Z as links to lter down the list: <div id="main"> <div id="rightside"> </div> <div id="content"> <h1>DINO SPACE! Members List {letter}</h1> <! START members > <p><strong>{name}</strong></p> <p>Keeper of <strong>{dino_name}</strong> a <strong>{dino_gender} {dino_breed}</strong></p> <hr /> <! END members > <p>Viewing page {page_number} of {num_pages}</p> <p>{first} {previous} {next} {last}</p> <p> <a href="members/alpha/A/">A</a> <a href="members/alpha/B/">B</a> <a href="members/alpha/C/">C</a> <a href="members/alpha/D/">D</a> <a href="members/alpha/E/">E</a> <a href="members/alpha/F/">F</a> <a href="members/alpha/G/">G</a> <a href="members/alpha/H/">H</a> <a href="members/alpha/I/">I</a> <a href="members/alpha/J/">J</a> <a href="members/alpha/K/">K</a> <a href="members/alpha/L/">L</a> <a href="members/alpha/M/">M</a> <a href="members/alpha/N/">N</a> <a href="members/alpha/O/">O</a> <a href="members/alpha/P/">P</a> <a href="members/alpha/Q/">Q</a> <a href="members/alpha/R/">R</a> <a href="members/alpha/S/">S</a> <a href="members/alpha/T/">T</a> <a href="members/alpha/U/">U</a> <a href="members/alpha/V/">V</a> <a href="members/alpha/W/">W</a> <a href="members/alpha/X/">X</a> 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 [ 117 ] <a href="members/alpha/Y/">Y</a> <a href="members/alpha/Z/">Z</a> </p> </div> </div> Now if we go to http://ourwebsite/members/ we should see the following: Searching for users The user lists in themselves are primarily useful if a user spots someone they know of, or if the results display some information about them that the user can relate to, for example, the breed of their dinosaur, which may make them think "they also have a T-Rex, I'll connect with them!". We should also have a search feature, so that our users can search for other users: /** * Search for members based on their name * @param String $filter name * @param int $offset the offset * @return Object pagination object */ public function filterMembersByName( $filter='', $offset=0 ) { $filter = ( $this->registry->getObject('db')-> sanitizeData( urldecode( $filter ) ) ); require_once( FRAMEWORK_PATH . 'lib/pagination/pagination.class.php'); $paginatedMembers = new Pagination( $this->registry ); $paginatedMembers->setLimit( 25 ); 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 [ 118 ] $paginatedMembers->setOffset( $offset ); $query = "SELECT u.ID, u.username, p.name, p.dino_name, p.dino_gender, p.dino_breed FROM users u, profiles p WHERE p.user_id=u.ID AND u.active=1 AND u.banned=0 AND u.deleted=0 AND p.name LIKE'%".$filter."%' ORDER BY p.name ASC"; $paginatedMembers->setQuery( $query ); $paginatedMembers->setMethod( 'cache' ); $paginatedMembers->generatePagination(); return $paginatedMembers; } Our controller now needs a method to process the search request. One important thing to note is that if we have performed a search, we can paginate because the search eld is encoded with urlencode and passed in the URL, whereas when we rst search, the name is in the name POST eld. We need to detect which is which, and process accordingly. We can use an extra parameter in the method to indicate where the name data is: private function searchMembers( $search=true, $name='', $offset=0 ) { require_once( FRAMEWORK_PATH . 'models/members.php'); $members = new Members( $this->registry ); if( $search == true ) { If we are searching, take the name from the POST data: // we are performing the search $pagination = $members->filterMembersByName( urlencode( $_POST['name'] ), $offset ); $name = urlencode( $_POST['name'] ); } else { If we are not searching, take the name from the URL (passed to this method directly): // we are paginating search results $pagination = $members->filterMembersByName( $name, $offset ); } if( $pagination->getNumRowsPage() == 0 ) { $this->registry->getObject('template')-> buildFromTemplates('header.tpl.php', 'members/invalid.tpl.php' , 'footer.tpl.php'); } 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 [ 119 ] else { $this->registry->getObject('template')-> buildFromTemplates('header.tpl.php', 'members/search.tpl.php' , 'footer.tpl.php'); $this->registry->getObject('template')->getPage()-> addTag( 'members', array( 'SQL', $pagination->getCache() ) ); $this->registry->getObject('template')->getPage()-> addTag( 'public_name', urldecode( $name ) ); $this->registry->getObject('template')->getPage()-> addTag( 'encoded_name', $name ); $this->registry->getObject('template')->getPage()-> addTag( 'page_number', $pagination->getCurrentPage() ); $this->registry->getObject('template')->getPage()-> addTag( 'num_pages', $pagination->getNumPages() ); Our pagination links require a reference to the name we are searching for: if( $pagination->isFirst() ) { $this->registry->getObject('template')->getPage()-> addTag( 'first', ''); $this->registry->getObject('template')->getPage()-> addTag( 'previous', '' ); } else { $this->registry->getObject('template')->getPage()-> addTag( 'first', "<a href='members/search-results/". $name."/'>First page</a>" ); $this->registry->getObject('template')->getPage()-> addTag( 'previous', "<a href='members/search-results/". $name."/" . ( $pagination->getCurrentPage() - 2 ) . "'>Previous page</a>" ); } if( $pagination->isLast() ) { $this->registry->getObject('template')->getPage()-> addTag( 'next', '' ); $this->registry->getObject('template')->getPage()-> addTag( 'last', '' ); } else { $this->registry->getObject('template')->getPage()-> addTag( 'first', "<a href='members/search-results/". 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 [ 120 ] $name."/" . $pagination->getCurrentPage() . "'>Next page</a>" ); $this->registry->getObject('template')->getPage()-> addTag( 'previous', "<a href='members/search-results/" .$name. "/" . ( $pagination->getNumPages() - 1 ) . "'>Last page</a>" ); } } } In our controllers constructor, we need to perform our detection (if a search is being performed or not) and pass a suitable $search parameter to the searchMembers method: case 'search': $this->searchMembers( true, '', 0 ); break; case 'search-results': $this->searchMembers( false, $urlBits[2] , intval( isset( $urlBits[3] ) ? $urlBits[3] : 0 ) ); break; We also need a search box in our main members' list page, and a new template showing the results of the search: <form action="members/search" method="post"> <h2>Search for a member</h2> <label for="name">Their name</label><br /> <input type="text" id="name" name="name" value="" /><br /> <input type="submit" id="search" name="search" value="Search" /> </form> We now have a fully working search feature, as shown below: 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 [ 121 ] Custom relationships Before we can connect to a user as a friend, we need to dene the types of relationship our social network will support. Within Dino Space, we should have relationships for: • Friends: For users who are friends with one another • Colleagues: For users who are colleagues with one another • Jogging buddies: For users who take their dinosaurs to the same morning jogging group We may, of course, wish to extend this as the site grows and changes as time goes on. To facilitate these relationships we are going to need two database tables, one to maintain a list of types of relationships, and one to maintain a list of relationships between users. Relationship types To represent the relationship types in the database, we could use the following database structure, for a relationship_types table: Field Type Description ID Integer, Auto Increment, Primary Key A unique ID for the relationship type Name Varchar The name of the relationship type, for example, friend Plural_name Varchar Plural version of the relationship type, for example, friends Active Boolean If this relationship type is active, and should users be able to form such relationships? Mutual Boolean Does this relationship require it to be a mutual connection, or can users connect without the permission of the other? 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 . $this->registry->getObject('template' )-& gt; buildFromTemplates('header.tpl .php& apos;, 'members/list.tpl .php& apos;, 'footer.tpl .php& apos;); $this->registry->getObject('template' )-& gt;getPage( )-& gt;. $this->registry->getObject('template' )-& gt;getPage( )-& gt; addTag( 'first', ''); $this->registry->getObject('template' )-& gt;getPage( )-& gt;. $this->registry->getObject('template' )-& gt;getPage( )-& gt; addTag( 'members', array( 'SQL', $pagination->getCache() ) ); $this->registry->getObject('template' )-& gt;getPage( )-& gt;