1. Trang chủ
  2. » Công Nghệ Thông Tin

Practical Web 2.0 Applications with PHP phần 9 pdf

60 372 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Cấu trúc

  • Practical Web 2.0 Applications with PHP

    • Contents at a Glance

    • Contents

    • About the Author

    • About the Technical Reviewer

    • Introduction

      • Who This Book Is For

      • How This Book Is Structured

      • Prerequisites

      • Downloading the Code

      • Contacting the Author

    • Application Planning and Design

      • What Is Web 2.0?

      • Database Connectivity

      • Web Site Templates

      • Web Site Features

        • Main Home Page and User Home Page

        • User Registration

        • Account Login and Management

        • User Blogs

        • Web Site Search

        • Application Management

      • Other Aspects of Development

        • Search-Engine Optimization

        • PHPDoc-Style Commenting

        • Security

        • Application Logging

        • Maintainability and Extensibility

      • Version Control and Unit Testing

      • Summary

    • Setting Up the Application Framework

      • Web Server Setup

        • Operating System

        • Installing the Apache HTTP Server

        • Installing MySQL 5

        • Installing PHP 5.2.3

      • Application Filesystem Structure

        • Web Root Directory

        • Data Storage Directory

        • PHP Classes Directory

        • Templates Directory

        • Full Directory Structure

      • Installing the Zend Framework

      • Configuring the Web Server

        • Creating a Virtual Host in Linux

        • Creating a Virtual Host in Windows

        • Restarting Your Web Server

      • Setting Up the Database

      • Using the Model-View-Controller Pattern

        • Separating Application Logic from Presentation Logic

        • Directing All Requests to index.php

        • Introduction to the Zend_Controller Class

        • How Requests Work with Zend_Controller

        • Creating the IndexController

      • Defining Application Settings

      • Connecting to the Database

        • Testing the Database Connection

      • The Smarty Template Engine

        • Why Not Use a Different Template Engine?

          • Improving Smarty Performance

          • Using a Metalanguage for Templates

        • Downloading and Installing Smarty

        • Automatic View Rendering with Zend_Controller

        • Integrating Smarty with the Web Site Controllers

      • Adding Logging Capabilities

        • Writing to the Log File

      • Summary

    • User Authentication, Authorization, and Management

      • Creating the User Database Table

        • Timestamps

        • User Profiles

      • Introduction to Zend_Auth

        • Instantiating Zend_Auth

        • Authenticating with Zend_Auth

      • Introduction to Zend_Acl

        • A Zend_Acl Example

      • Combining Zend_Auth, Zend_Acl, and Zend_ Controller_Front

      • Managing User Records with DatabaseObject

        • The DatabaseObject_User Class

        • Using DatabaseObject_User

      • Managing User Profiles

        • Using Profile_User

        • Integrating Profile_User with DatabaseObject_User

      • Summary

    • User Registration, Login, and Logout

      • Adding User Registration to the Application

        • Creating the Form Processor for User Registration

          • The Initial FormProcessor_UserRegistration Class

          • The usernameExists() Method

          • The IsValidUsername() Method

          • Adding Username Validation to FormProcessor_UserRegistration

          • Validating the User’s Name

          • Validating the User’s E-mail Address

          • The Complete FormProcessor_UserRegistration Class

        • Displaying the Registration Form and Processing Registrations

          • The Initial AccountController Class

          • Developing the Templates

          • Handling the Form Submission

        • Adding CAPTCHA to the User Registration Form

          • Circumventing CAPTCHA

          • CAPTCHA and Accessibility

          • PEAR’s Text_CAPTCHA

          • Generating a CAPTCHA Image

          • Adding the CAPTCHA Image to the Registration Form

          • Validating the CAPTCHA Phrase

        • Adding E-mail Functionality

      • Implementing Account Login and Logout

        • Creating the Login Template

        • Adding the Account Controller Login Action

        • Logging Successful and Failed Login Attempts

        • Logging Users Out of Their Accounts

      • Dealing with Forgotten Passwords

        • Resetting a User’s Password

        • Functions for Resetting Passwords

      • Implementing Account Management

        • Creating the Account Home Page

        • Updating the Web Site Navigation

        • Allowing Users to Update Their Details

      • Summary

    • Introduction to Prototype and Scriptaculous

      • Downloading and Installing Prototype

        • Prototype Documentation

      • Selecting Objects in the Document Object Model

        • The $() Function

        • The getElementsByClassName() Function

        • The $$() Function

        • The getElementsBySelector() Function

      • Prototype’s Hash Object

      • Other Element Extensions

        • Showing and Hiding Elements

        • Retrieving Dimensions of Elements

        • Managing Classes of Elements

        • Manipulating Strings with Prototype

      • Ajax Operations in Prototype

        • Ajax Request Options

        • Ajax Callback Functions

          • The XMLHttpRequest Callback Argument

        • JavaScript Object Notation (JSON)

        • An Ajax.Request Example

          • Handling XML Data from an Ajax Request

          • Handling XML That Isn’t Well Formed

          • Completing the onFailure Error Handler

          • The Complete Ajax.Request Example

      • Event Handling in Prototype

        • Observing an Event

        • Finding Out Which Element an Event Occurred On

        • Canceling an Event

      • Creating JavaScript Classes in Prototype

        • Creating a Class

        • Binding Function Calls to Objects

      • From Prototype to Scriptaculous

        • Prebuilt Controls

        • Drag and Drop

        • Visual Effects

        • DOM Element Builder

        • JavaScript Unit Testing

      • Downloading and Installing Scriptaculous

      • Combining Prototype, Scriptaculous, Ajax, and PHP in a Useful Example

        • Creating the Main HTML Page: index.php

        • Styling the Application: styles.css

        • Creating and Populating the Database: schema.sql

        • Managing the List Items on the Server Side: items.php

          • Connecting to the Database

          • Retrieving the List Items

          • Processing and Saving the List Order

        • Processing Ajax Requests on the Server Side: processor.php

          • Handling the Load Action

          • Handling the Save Action

        • Creating the Client-Side Application Logic: scripts.js

          • Application Settings

          • Initializing the Application with init()

          • Updating the Status Container with setStatus()

          • Loading the List of Items with loadItems()

          • Handling the Response from the Ajax Request in loadItems()

          • Handling a Change to the List Order with saveItemOrder()

          • Handling the Response from the Ajax Request in saveItemOrder()

      • Summary

    • Styling the Web Application

      • Adding Page Titles and Breadcrumbs

        • The Breadcrumbs Class

        • Generating URLs

          • Generating URLs in Controller Actions

          • Generating URLs in Smarty Templates

        • Setting the Title and Trail for Each Controller Action

        • Creating a Smarty Plug-In to Output Breadcrumbs

        • Displaying the Page Title

      • Integrating the Design into the Application

        • Creating the Static HTML

        • Moving the HTML Markup into Smarty Templates

          • Modifying header.tpl

          • Modifying footer.tpl

          • Highlighting the Active Navigation Section

      • Constructing the CSS

        • Specifying Media Types and Loading the CSS File

        • Creating the Application CSS

          • Creating the Three-Column Layout

          • Styling the Page Header

          • Styling the Tabbed Navigation Bar

          • Setting the Global Styles

          • Styling the Page Content

        • Creating a Print-Only Style Sheet

          • Modifying the Screen Style Sheet

        • The Full Application Style Sheet

      • Styling the Application Web Forms

      • Loading Prototype and Scriptaculous

      • Implementing Client-Side Form Validation

        • Adding JSON Support to CustomControllerAction

        • Modifying the Form Processor

        • Modifying the Registration Controller Action

          • Detecting Ajax Requests

          • Returning Form Errors Using JSON

        • Creating the JavaScript Form Validator

          • Initializing the UserRegistrationForm JavaScript Class

          • Hiding Form Errors

          • Displaying Form Errors

          • Handling the Form Submission

          • Handling the Form Validation Response

        • Loading the UserRegistrationForm Class

      • Summary

    • Building the Blogging System

      • Creating the Database Tables

      • Setting Up DatabaseObject and Profile Classes

        • Creating the DatabaseObject_BlogPost Class

        • Creating the Profile_BlogPost Class

      • Creating a Controller for Managing Blog Posts

        • Extending the Application Permissions

        • The BlogmanagerController Actions

        • Linking to Blog Manager

      • Creating and Editing Blog Posts

        • Creating the Blog Post Submission Form Template

        • Instantiating FormProcessor_BlogPost in editAction()

        • Implementing the FormProcessor_BlogPost Class

        • Generating a Permanent Link to a Blog Post

        • Filtering Submitted HTML

          • Why Filter Embedded JavaScript?

          • Types of Filtering

          • Implementing the cleanHtml() Method

        • Creating a New Blog Post

      • Previewing Blog Posts

        • Creating the Preview Action

        • Implementing the Preview Template

        • Requesting Confirmation for User Actions

      • Updating the Status of a Blog Post

        • Completing setstatusAction()

        • Notifying the User

          • Adding FlashMessenger to CustomControllerAction

          • Writing Messages to FlashMessenger

          • Outputting FlashMessenger Messages on the Web Site

      • Summary

    • Extending the Blog Manager

      • Listing Blog Posts on the Blog Manager Index

        • Fetching Blog Posts from the Database

          • Creating the _GetBaseQuery() Method

          • Creating the GetPostsCount() Function

          • Creating the GetPosts() Function

          • Retrieving a Monthly Summary of Posts

        • Assigning Recent Posts and the Monthly Summary to the Template

        • Displaying Recent Posts in the Template

        • Displaying the Monthly Summary

          • Calling the Smarty Plug-in in the Side Columns

          • Including Additional Data in the Side Column Sometimes

      • Ajaxing the Blog Monthly Summary

        • Creating the Ajax Request Output

        • The BlogMonthlySummary JavaScript Class

        • Installing the BlogMonthlySummary Class

        • Notifying the User About the Content Update

          • Managing Message Containers

          • Updating the Messages Container with BlogMonthlySummary

      • Integrating a WYSIWYG Editor

        • Downloading and Installing FCKeditor

        • Configuring FCKeditor

        • Loading FCKeditor in the Blog Editing Page

      • Summary

    • Personalized User Areas

      • Controlling User Settings

        • Presenting Customizable Settings to Users

        • Processing Changes to User Settings

        • Creating Default User Settings

      • The UserController Class

        • Routing Requests to UserController

          • Creating a New Route

          • Injecting the Route into the Router

          • Dynamically Generating URLs for Custom Routes

          • Generating Other Required Routes

        • Handling Requests to UserController

      • Displaying the User’s Blog

        • Displaying the Blog Index Page

          • Implementing the indexAction() Method

          • Displaying Blog Posts on the User Home Page

        • Displaying Individual Blog Posts

          • Loading Live Blog Posts Using the URL

          • Implementing the viewAction() Method

          • Displaying the Blog Post Details

          • Creating the Template for postNotFoundAction()

        • Generating Blog Archive Links

        • Displaying the Monthly Archive

          • Implementing the archiveAction() Method

      • Populating the Application Home Page

        • Loading Recent Public Posts

        • Implementing the Application Home Page

          • Loading Multiple User Records

          • Retrieving the Latest Posts for the Home Page

          • Creating the Application Home Page Template

      • Summary

    • Implementing Web 2.0 Features

      • Tags

        • Implementing Tagging

        • Managing Blog Post Tags

        • Displaying a User’s Tags on Their Blog

        • Displaying a Tag Space

          • Retrieving Posts Based on a Tag

          • Routing Requests to the Tag Space

          • Handling Requests to the Tag Space

          • Outputting the Tag Space

        • Displaying Tags on Each Post

      • Web Feeds

        • Data Formats for Web Feeds

        • Creating an Atom Feed with Zend_Feed

        • Adding the Feed to UserController

        • Linking to Your Feed

        • Other Feed Options

      • Microformats

        • An Example of Using Microformats

        • Why Use Microformats?

          • The Firefox Operator Plug-In

        • Microformatting Your Tags

      • Allowing Users to Create a Public Profile

        • Allowing Users to Create a Public Profile

          • Processing the User Details Form

          • Displaying the User Profile Options

        • Displaying a User’s Profile

      • Summary

    • A Dynamic Image Gallery

      • Storing Uploaded Files

        • Creating the Database Table for Image Data

        • Controlling Uploaded Images with DatabaseObject

      • Uploading Files

        • Setting the Form Encoding

        • Adding the Form

        • Specifying the File Input Type

        • Setting the Maximum File Size

        • Handling Uploaded Files

          • Creating the Blog Manager Action Handler

          • Creating the Image-Upload Form Processor

          • Writing Files to the Filesystem

      • Sending Images

      • Resizing Images

        • Creating Thumbnails

          • Determining the Width and Height of the Thumbnail

          • Determining the Input and Output Functions

          • Generating the Thumbnail Filename

          • Creating the Thumbnail

        • Linking the Thumbnailer to the Image Action Handler

          • Generating an Image Hash

          • Generating Image Filenames

          • Updating imageAction() to Serve the Thumbnail

      • Managing Blog Post Images

        • Automatically Loading Blog Post Images

        • Displaying Images on the Post Preview

        • Deleting Blog Post Images

        • Using Scriptaculous and Ajax to Delete Images

          • Modifying the PHP Deletion Code

          • Creating the BlogImageManager JavaScript Class

          • Loading BlogImageManager in the Post Preview

        • Deleting Images when Posts Are Deleted

        • Reordering Blog Post Images

          • Drag and Drop

          • Saving the Order to Database

          • Adding Sortable to BlogImageManager

      • Displaying Images on User Blogs

        • Extending the GetPosts() Function

        • Displaying Thumbnail Images on the Blog Index

        • Displaying Images on the Blog Details Page

        • Displaying Larger Images with Lightbox

          • Installing Lightbox

          • Loading Lightbox on the Blog Details Page

          • Linking the Blog Post Images to Lightbox

      • Summary

    • Implementing Site Search

      • Introduction to Zend_Search_Lucene

        • Comparison to MySQL Full-Text Indexing

        • Zend_Search_Lucene Field Types

        • Field Naming

      • Indexing Application Content

        • Indexing Multiple Types of Data

        • Creating a New Zend_Search_Lucene_Document

        • Retrieving the Index Location

        • Building the Entire Index

        • Indexing and Unindexing a Single Blog Post

          • Adding a Single Blog Post to the Index

          • Removing a Blog Post from the Index

        • Triggering Search Index Updates

          • When a Post Is Created

          • When a Post Is Updated

          • When a Post Is Deleted

          • When a Post’s Tags Are Changed

      • Creating the Search Tool

        • Adding the Search Form

        • Handling Search Requests

        • Querying the Search Index

        • Displaying Search Results

        • Types of Searches

      • Adding Autocompletion to the Search Tool

        • Providing Search Suggestions

        • Creating an Action Handler to Return Search Results

        • Retrieving Search Suggestions

        • Loading the SearchSuggestor Class

        • Displaying Search Suggestions

        • Adding Mouse Navigation to Results

        • Adding Keyboard Navigation to Results

      • Summary

    • Integrating Google Maps

      • Google Maps Features

        • Geocoding

        • Displaying Maps

          • Map Controls

          • Map Overlays

        • Controlling Maps

      • Planning Integration

        • Limitations of Google Maps

        • Browser Compatibility

        • Documentation and Resources

        • Creating a Google Maps API Key

      • Adding Location Storage Capabilities

        • Creating the Database Table

        • Creating the DatabaseObject_BlogPostLocation Class

        • Modifying Blog Posts to Load Locations

      • Creating Our First Map

        • Creating a New Blog Manager Controller Action

          • Linking to the locationsAction() Function

        • Displaying Your First Google Map

          • Loading the Google Maps API

          • Beginning the BlogLocationManager JavaScript Class

          • Loading BlogLocationManager

      • Managing Locations on the Map

        • Handling Location Management Ajax Requests

          • The New Location Form Processor

          • Creating the locationsManage Controller Action

        • Creating the Address Lookup Form

        • Extending the BlogLocationManager JavaScript Class

          • Required Methods

          • Class Initialization

          • The loadMap() Function

          • The zoomAndCenterMap() Function

          • Adding Locations with addMarkerToMap()

          • Removing Markers Using removeMarkerFromMap()

          • Checking to See Whether a Marker Exists with hasMarker()

          • Displaying Saved Locations with loadLocationsSuccess()

          • Handling the Add Location Form Submission

          • Handling the Geocoder Response with createPoint()

          • Handling Successful Location Creation

          • Saving New Coordinates for Dragged Locations

          • Handling the Response from Saving a Dragged Location

          • Removing Markers from the Map

          • Confirming the Deletion of the Marker

          • Unloading the Map

        • Using BlogLocationManager

      • Displaying the Map on Users’ Public Blogs

        • Outputting Locations Using the Geo Microformat

        • Creating the BlogLocations Class

        • Updating the Blog Post Display Template

      • Summary

    • Deployment and Maintenance

      • Application Logging

        • E-mailing Critical Errors to an Administrator

          • Creating the Log Writer

          • Specifying the E-mail Recipient

          • Adding the EmailLogger Writer to Zend_Log

        • Using Application Logs

      • Site Error Handling

        • Objectives of Error Handling

        • Handling Predispatch Errors

          • Notifying the User of Errors

          • Catching Errors

        • Application Runtime Errors

          • Creating the Error Display Templates

      • Web Site Administration

        • Administrator Section Features

          • User Management

          • Blog Post Management

          • Auditing Application Logs

        • Implementing Administration

          • Permissions

          • Creating the AdminController Class

      • Application Deployment

        • Different Configurations for Different Servers

          • Telling the Bootstrap Which Configuration to Use

        • Deploying Application Files with Rsync

      • Backup and Restore

        • Exporting a Database

        • Importing a Database

      • Summary

    • Index

Nội dung

{ var json = transport.responseText.evalJSON(true); this.showSuggestions(json); }, showSuggestions : function(suggestions) { this.clearSuggestions(); if (suggestions.size() == 0) return; var ul = Builder.node('ul'); for (var i = 0; i < suggestions.size(); i++) { var li = $(Builder.node('li')); li.update(suggestions[i]); ul.appendChild(li); } this.container.appendChild(ul); }, Another thing we do in the showSuggestions() function is clear any existing terms before new ones are shown. We do this using clearSuggestions(), which is shown in Listing 12-37. This is called regardless of whether any search suggestions have been found; if there are no suggestions, there is nothing to show, and if there are suggestions, then we want to show only the new ones, not ones that were previously there. Listing 12-37. Removing Existing Search Suggestions from the Search Container (SearchSuggestor.class.js) clearSuggestions : function() { this.container.getElementsBySelector('ul').each(function(e) { e.remove(); }); this.query = null; } }; One more minor change we must now make is to the loadSuggestions() function. Cur- rently in this function if the search term is empty, then we don’t bother performing this Ajax request. We must now make it so in addition to not performing the Ajax request, the current list of suggestions is hidden. The reason we add this is because if the user highlights the search input and presses Backspace, the term would be deleted but the suggestions would remain. The code in Listing 12-38 fixes this issue. CHAPTER 12 ■ IMPLEMENTING SITE SEARCH 459 9063Ch12CMP3 11/13/07 9:26 PM Page 459 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Listing 12-38. Clearing Suggestions When the Search Term Is Cleared (SearchSuggestor.class.js) loadSuggestions : function() { var query = $F(this.input).strip(); if (query.length == 0) this.clearSuggestions(); if (query.length == 0 || query == this.query) return; // other code }, Adding Mouse Navigation to Results Although the search suggestions are now being displayed when the user begins to enter a search term, it is not yet possible to do anything useful with these suggestions. The first thing we are going to do is allow users to click one of the suggestions. This will trigger the search form being submitted using the selected term. To do so, we must first handle the mouseover, mouseout, and click events for each list item. The functionality we want to occur for each event is as follows: • When the mouse is over a suggestion, highlight the suggestion. We do this by creating a new CSS style called .active and adding it using the Prototype addClassName() method. • When the mouse moves away from a suggestion, remove the .active class using removeClassName(). • When a search term is clicked, replace the term currently in the search input with the clicked term and then submit the form. First, we will add the new CSS style. We will simply make the active item display with a red background and white text. Listing 12-39 shows the new CSS selector we add to styles.css. Listing 12-39. Styling the Active Search Suggestion (styles.css) #search li.active { background : #f22; color : #fff; cursor : pointer; } Now we use the observe() method to handle the three events discussed earlier. Listing 12-40 shows the code we add to the showSuggestions() function to observe these events, as well as the suggestionClicked() function that we call from within the click event. CHAPTER 12 ■ IMPLEMENTING SITE SEARCH460 9063Ch12CMP3 11/13/07 9:26 PM Page 460 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Listing 12-40. Handling the Mouse Events with the Search Suggestions (SearchSuggestor.class.js) // other code showSuggestions : function(suggestions) { this.clearSuggestions(); if (suggestions.size() == 0) return; var ul = Builder.node('ul'); for (var i = 0; i < suggestions.size(); i++) { var li = $(Builder.node('li')); li.update(suggestions[i]); li.observe('mouseover', function(e) { Event.element(e).addClassName('active') }); li.observe('mouseout', function(e) { Event.element(e).removeClassName('active') }); li.observe('click', this.suggestionClicked.bindAsEventListener(this)); ul.appendChild(li); } this.container.appendChild(ul); }, suggestionClicked : function(e) { var elt = Event.element(e); var term = elt.innerHTML.strip(); this.input.value = term; this.input.form.submit(); this.clearSuggestions(); }, // other code CHAPTER 12 ■ IMPLEMENTING SITE SEARCH 461 9063Ch12CMP3 11/13/07 9:26 PM Page 461 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com As you can see, in the suggestionClicked() event handler, the first thing we do is determine which suggestion was clicked using the Event.element() function. We can then determine what the search term is by retrieving the innerHTML property of the element (we also use strip() to clean up this code in case extra whitespace is added to it). We then update the value of the form element and submit the form. Additionally, we clear the suggestions after one has been clicked, preventing the user from clicking a different sug- gestion while the form is being submitted. Adding Keyboard Navigation to Results The final thing we do to improve the search suggestions is to add keyboard controls to the sug- gestions. Essentially what we want to be able to do is let the user choose a suggestion using their up and down arrow keys. The keyboard handling rules we will add are as follows: •If the user presses the down arrow and no term has been highlighted (that is, set to use the .active class), then select the first term. •If the user presses the down arrow and a suggestion is highlighted, move to the next suggestion. If the user presses down when the last suggestion is highlighted, then select no suggestion so the user can hit Enter on what they have typed so far. •If the user presses up and no term is selected, then select the last suggestion. •If the user presses up and a suggestion is highlighted, move to the previous suggestion. Select no suggestion if up is pressed when the first suggestion is selected. •Submit the search form with the highlighted term when Enter is pressed. •Hide the suggestions if the Escape key is pressed. As you can probably tell, the work involved with adding keyboard controls is slightly more involved than adding mouse controls. The first thing we are going to do is to write some utility functions to help us select items and to determine which item is selected. Listing 12-41 shows the getNumberOfSuggestions() function that we add to SearchSuggestor.class.js, which simply counts the number of list items present and returns that number. This is helpful in determining the item index of the next or previous item when using the arrow keys. Listing 12-41. Determining the Number of Suggestions Showing to the User (SearchSuggestor.class.js) SearchSuggestor = Class.create(); SearchSuggestor.prototype = { // other code CHAPTER 12 ■ IMPLEMENTING SITE SEARCH462 9063Ch12CMP3 11/13/07 9:26 PM Page 462 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com getNumberOfSuggestions : function() { return this.container.getElementsBySelector('li').size(); }, ■Note When you add this function and the other new functions in this section to SearchSuggestor.class.js, make sure the comma is correctly placed after the close brace of each func- tion in the class (except for the final one). Next we write a function to select an item (that is, to apply the .active class) based on its numerical index in the list of items. This list is zero-indexed. Listing 12-42 shows the selectSuggestion() class, which works by looping over all list items and adding the .active class if it matches the passed-in argument. Note that this function also deselects every other list item. In effect we can use this function to ensure no items are selected at all by passing an invalid index (such as -1). Listing 12-42. Selecting a Single Suggestion Based on Its Index (SearchSuggestor.class.js) selectSuggestion : function(idx) { var items = this.container.getElementsBySelector('li'); for (var i = 0; i < items.size(); i++) { if (i == idx) items[i].addClassName('active'); else items[i].removeClassName('active'); } }, Next, we write a function to determine the index of the item that is currently selected, shown in Listing 12-43. This is in some ways the opposite of the selectSuggestion() function. It works almost identically, but rather than updating the class name, it checks instead for the presence of the .active class. If no items are currently selected, then -1 is returned. Listing 12-43. Determining the Index of the Selected Suggestion (SearchSuggestor.class.js) getSelectedSuggestionIndex : function() { var items = this.container.getElementsBySelector('li'); for (var i = 0; i < items.size(); i++) { if (items[i].hasClassName('active')) return i; CHAPTER 12 ■ IMPLEMENTING SITE SEARCH 463 9063Ch12CMP3 11/13/07 9:26 PM Page 463 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com } return -1; }, Now we write a function called getSelectedSuggestion(), which is shown in Listing 12-44. This function is identical to getSelectedSuggestionIndex() except that it returns the actual search term that is selected rather than its index in the list. We will use this function when the user hits Enter while a term is selected. Listing 12-44. Determining the Search Suggestion That Is Currently Selected (SearchSuggestor.class.js) getSelectedSuggestion : function() { var items = this.container.getElementsBySelector('li'); for (var i = 0; i < items.size(); i++) { if (items[i].hasClassName('active')) return items[i].innerHTML.strip(); } return ''; } }; The final thing we must do is modify the onQueryChanged() function, which is the event handler we defined that is called whenever a key is pressed in the search input. Currently, all the function does is clear any existing timers and set a new timer for fetching suggestions. We will now add handlers for specific keys to this function (in addition to the timer-handling code). Listing 12-45 shows the code we use to handle the Enter key being pressed. When the user hits Enter, if a suggestion is highlighted, then we want to populate the search input with this term and submit the form. If no term is highlighted, then we submit the form with whatever the user has typed so far. When the search term populates the input, we clear the suggestions, just as we did in the mouse-handling code. Also, note that we leave the call to clearTimeout() in front of the switch() statement. This is because we will be returning from the keys handled in the switch() statement, but we still want to cancel the timer. All normal key presses will travel beyond the switch() statement and trigger the new timer. Listing 12-45. Searching on the Selected Term When the User Hits Enter (SearchSuggestor.class.js) SearchSuggestor = Class.create(); SearchSuggestor.prototype = { // other code CHAPTER 12 ■ IMPLEMENTING SITE SEARCH464 9063Ch12CMP3 11/13/07 9:26 PM Page 464 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com onQueryChanged : function(e) { clearTimeout(this.timer); switch (e.keyCode) { case Event.KEY_RETURN: var term = this.getSelectedSuggestion(); if (term.length > 0) { this.input.value = term; this.clearSuggestions(); } return; Next we handle the Escape key being pressed. This case is fairly simple, because all we need to do is to hide the search suggestions, as shown in Listing 12-46. Listing 12-46. Hiding the Search Suggestions When the User Hits Escape case Event.KEY_ESC: this.clearSuggestions(); return; We now handle the trickier case where the user presses the down arrow key. According to the rules we specified earlier in this section, we want to select the first term if no term is selected; otherwise, we want to select the next term. As another special case, if the last term is selected, then pressing the down arrow should result in no suggestion being selected. Listing 12-47 shows the code we use to determine which suggestion should now be selected as a result of the down arrow being pressed. We make use of the utility functions we just created to help with this. Listing 12-47. Selecting the Next Item When the Down Arrow Is Pressed (SearchSuggestor.class.js) case Event.KEY_DOWN: var total = this.getNumberOfSuggestions(); var selected = this.getSelectedSuggestionIndex(); if (selected == total - 1) // currenty last item so deselect selected = -1; else if (selected < 0) // none selected, select the first selected = 0; else // select the next selected = (selected + 1) % total; this.selectSuggestion(selected); Event.stop(e); return; CHAPTER 12 ■ IMPLEMENTING SITE SEARCH 465 9063Ch12CMP3 11/13/07 9:26 PM Page 465 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com To handle the case where the up arrow is pressed, we basically just do the opposite of the down arrow calculations. Listing 12-48 shows the code for this case. This code also includes the final call of the function to initiate the new timer. Note that this won’t be called for presses of the Enter, Escape, up arrow, and down arrow keys, because we’ve returned from each of them in this function. Listing 12-48. Selecting the Previous Suggestion When the Up Arrow Is Pressed (SearchSuggestor.class.js) case Event.KEY_UP: var total = this.getNumberOfSuggestions(); var selected = this.getSelectedSuggestionIndex(); if (selected == 0) // first item currently selected, so deselect selected = -1; else if (selected < 0) // none selected, select the last item selected = total - 1; else // select the previous selected = (selected - 1) % total; this.selectSuggestion(selected); Event.stop(e); return; } this.timer = setTimeout(this.loadSuggestions.bind(this), this.delay * 1000); }, // other code }; If you now type a search term in the search box (assuming some existing searches have already taken place), you will be shown a list of suggestions for your search, as shown in Figure 12-3. You might want to add some extra functionality to the tool in the future, such as display- ing the number of results that would be returned if the user were to perform the given search. The difficulty in providing features such as this is that they are resource intensive. You need to perform the search of each term in real time (not recommended) to determine how many results the search would return, or you need to cache the result counts so the data can be accessed quickly. In any case, you need to be aware of the implications of adding features like this to your server. Even the suggestion lookup tool as it is results in a new HTTP request and database query each time, so imagine if you had hundreds or thousands of people using the search tool at any one time. CHAPTER 12 ■ IMPLEMENTING SITE SEARCH466 9063Ch12CMP3 11/13/07 9:26 PM Page 466 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 12-3. Search suggestions are now being displayed below the search input. Summary In this chapter, we created a fully functioning search engine for our web application using Zend_Search_Lucene. We achieved this by creating a search index for all of the blog posts in the application. We altered the blog management code so the index is automatically maintained when posts are created, updated, or deleted. Next we added a search form to the website to allow users to find blog posts. The powerful querying syntax of Lucene meant posts could be found based on several criteria, including the title, the body, or its tags. Finally, we improved the search form to behave similarly to Google’s Suggest interface. This provides users with some suggestions on what to search for, based on the tags registered users have applied to their blog posts. In the next chapter, we will be looking closely at Google Maps. We will extend the blog func- tionality so users can add locations to their blog posts and display those maps accordingly. CHAPTER 12 ■ IMPLEMENTING SITE SEARCH 467 9063Ch12CMP3 11/13/07 9:26 PM Page 467 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 9063Ch12CMP3 11/13/07 9:26 PM Page 468 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... action, we initialize the $ret array, which will hold the return data 4 89 9063Ch13CMP3 11/15/07 8:20 AM Page 490 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 490 CHAPTER 13 ■ INTEGRATING GOOGLE MAPS Listing 13-21 Initializing the Action Handler and Loading the Blog Post (BlogmanagerController .php) < ?php class BlogmanagerController extends CustomControllerAction { // other... the locationsmanageAction() function Listing 13-25 Processing the Move Location Request (BlogmanagerController .php) case 'move': $location_id = $request->getPost('location_id'); 491 90 63Ch13CMP3 11/15/07 8:20 AM Page 492 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 492 CHAPTER 13 ■ INTEGRATING GOOGLE MAPS $location = new DatabaseObject_BlogPostLocation($this->db); if ($location->loadForPost($post->getId(),... post 493 90 63Ch13CMP3 11/15/07 8:20 AM Page 494 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 494 CHAPTER 13 ■ INTEGRATING GOOGLE MAPS • zoomAndCenterMap(): This automatically zooms the map in as far as possible to display all of the locations This will be called when the map is initially loaded and also when a new location is added If there are no locations to work with, .. .90 63Ch13CMP3 11/15/07 8:20 AM Page 4 69 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 13 Integrating Google Maps A ll of the code we have developed so far in this book has been self-contained with no reliance on any outside services Frequently in your web development endeavors you will need to integrate features... and longitude -1 22.08 1783 would be returned These coordinates can then be used to mark locations on the displayed map Google provides two ways to access its geocoder The first method is to use their JavaScript interface to look up addresses This allows you to look up and add new points on your map from within the client-side web browser 4 69 9063Ch13CMP3 11/15/07 8:20 AM Page 470 Simpo PDF Merge and Split... $this->breadcrumbs->addStep('Manage Locations'); $this->view->post = $post; } } ?> 4 79 9063Ch13CMP3 11/15/07 8:20 AM Page 480 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 480 CHAPTER 13 ■ INTEGRATING GOOGLE MAPS If you were to now view this controller action (assuming you passed in a valid blog post ID in the URL of http://phpweb20/blogmanager/preview?id=PostId), an error would be displayed... locations.tpl, stored in /templates/blogmanager The added or changed lines are highlighted accordingly 90 63Ch13CMP3 11/15/07 8:20 AM Page 493 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 13 ■ INTEGRATING GOOGLE MAPS Listing 13-26 The Locations Management Template with Add Location Form (locations.tpl) {include file='header.tpl' section='blogmanager' maps=true} . 12 ■ IMPLEMENTING SITE SEARCH 467 90 6 3Ch12CMP3 11/13 /07 9 :26 PM Page 467 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 90 6 3Ch12CMP3 11/13 /07 9 :26 PM Page 468 Simpo PDF. IMPLEMENTING SITE SEARCH4 60 90 6 3Ch12CMP3 11/13 /07 9 :26 PM Page 4 60 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Listing 12- 40. Handling the Mouse Events with the Search Suggestions. Listing 12- 38 fixes this issue. CHAPTER 12 ■ IMPLEMENTING SITE SEARCH 4 59 90 6 3Ch12CMP3 11/13 /07 9 :26 PM Page 4 59 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Listing 12- 38.

Ngày đăng: 12/08/2014, 13:21

TỪ KHÓA LIÊN QUAN