Chapter 11
Rich Internet Applications
We are winding down now; you know the basics and I’m sure by now you have started trying to apply what you’ve learned to your own projects. Honestly, if you weren’t, I’d be worried since reading this book won’t make you a Zend Framework programmer, actually working with it will. No good web development book these days is complete without a section on Rich Internet Applications (RIA). Loosely defined, a RIA is any web based application that has features and functionality similar to traditional desktop applications. My guess is that someone mistakenly assumed that desktop applications were the end-all be-all of application design and that everything should strive to mimic them. More likely though is that developers have noticed that people are more comfortable with desktop applications and so they strive to build web applications that act like them. There are a lot of good RIAs currently in production. Zimbra’s mail client and productivity suite, Gmail, and Google Docs are the ones that come immediately to mind; however there are more out there and new ones being released daily. RIAs can take many forms. You can build them using Adobe’s Flash or Flex, Microsoft’s Silverlight, or a host of other technologies including my personal favorite, good old JavaScript. I’ve used fancier technologies and really love Adobe’s Flex but since this book is about Zend Framework, we’ll spend our time working the backend and not the frontend.
156 ” Rich Internet Applications
Making our Sample App Into an RIA
As usual, let’s talk about what we want to do before we dive into the code. The current index page does a great job of processing our request. When we give it a URL, it will analyze it and give us back the resulting keywords and, when appropriate, graphics of the keywords from Flickr. Everything is good except that to do all of this, it has to make a complete round trip to the server. If we prettied this page up with a lot of graphics and such, they would potentially get reloaded each time we submitted a new URL. Also, each request carries with it load on the server in the form of returning the page, style sheets, any JavaScript, etc. All of this is overhead that is not necessary thanks to JavaScript and Ajax. However, as both you and I know, the best reason for building out a RIA version of the page is simply because it’s cool. So, let’s build a RIA version of our main analysis page. One thing we need to do before we start though is to correct a grievous wrong. Way back near the beginning of the book we talked about models. Models should really contain all of your business logic. However, for the sake of clarity, we put all of our analysis code in the IndexController::extractAction(). Even back then we knew that while this was acceptable, it wasn’t the right place for it. So let’s make some adjustments. As always, you can follow along with me as I describe the changes or you can simply unpack example7.zip and skip ahead. So open IndexController.php and find extractAction(). As you can see a lot of that code is business logic but not all of it. Some of it is routine setup code and that we need to leave alone. Since it’s now really short, I’ll pass it in so we can discuss what we left and what we moved.
public function extractAction() { try {
Ok, all of this is housekeeping. Yes we could move filtering into a model but it really doesn’t make sense because it’s not business logic. So we grab the URL, sanitize it and do the token check. All of these tasks, while important, do not directly relate to analyzing the contents of a web page, therefore, they are not business logic.
/*
Rich Internet Applications ” 157
* get the token */ $token = Zend_Filter::get($this->getRequest()->getPost(’token’), ’ StripTags’); /* * Execute the token check */ if (!$this->tokenCheck($token)) { throw new Zend_Exception("I’m sorry but I think there has been an error. Please try again."); } // if (!$this->tokenCheck($token)) /* * Reset the token */ $this->generateToken(); /* * get the URL passed in from the form */ $url = Zend_Filter::get($this->getRequest()->getPost(’url’), ’StripTags’);
Now we instantiate our copy of WebProperty. The business logic of analyzing the contents of the page has been moved into WebProperty::Analyze(). Once we have our instance of WebProperty, we call analyze() to get our keyword list.
$wp = new WebProperty($url);
/* * Hand everything off to the view for output */ $this->view->url = $url ; $this->view->result = $wp->analyze(); $wp->save(); } catch (Zend_Exception $e) {
WebProperty::Analyze() throws exceptions so we still need to catch them. We could catch them in WebProperty::Analyze() but in this case, we are using our catch to build our notification to the user that there’s a problem. This would be a lot more difficult in WebProperty so we’ll catch them here.
158 ” Rich Internet Applications
/* * Deal with exceptions. */ $this->_helper->flashMessenger->addMessage("There was an error processing your request."); $this->_helper->flashMessenger->addMessage("Exception Type:".get_class($e) ); $this->_helper->flashMessenger->addMessage("Exception Message:".$e-> getMessage()); $this->_redirect(’/index/index’); return false; } return true; } // public function extractAction()
Now, let’s see what we moved. This code will all look familiar since it’s just been moved.
public function analyze() {
I know you are looking at this next block of code and screaming “filtering”! But no, this is a business logic decision. We made the decision that all URLs have to start with a protocol. Actually, here, instead of throwing an error and handing it back to the user, we just append one and try it. The worst case scenario is that we throw another exception later on.
if (substr(strtolower($this->_url),0,7)!=’http://’) { throw new Zend_Exception("All URLs must start with http://"); } // if (substr(strtolower($url),0,7)!=’http://’) /* * read page into memory * requires allow_url_fopen to be true */ $page = file_get_contents($this->_url); if (!$page) { throw new Zend_Exception("There was an error loading the url. ".$this-> _url);
Rich Internet Applications ” 159
} // if (substr(strtolower($url),0,7)!=’http://’) /* * strip out everything but the content * Many thanks to #phpc members ds3 and SlashLife for the RegEx */ $matches = array(); preg_match(’/]*>(.*?)<\/body\s*/isx’, $page, $matches); $content = $matches[1]; /* * Filter out the cruft */ $content = preg_replace(’/(