define('ROOT_PATH', dirname(__FILE__).'/..');
require_once ROOT_PATH . '/config/sf_requires.php';
require_once ROOT_PATH . '/config/app_requires.php';
$app = new LogAnalyzer(); $app->run()->send();
There we define the root path, and then we include two configuration files, one which will take care of requiring the Symfony libraries and the other that will require the application files.
Then we instantiate the application class and we run it.
Now let's check what's inside the sf_requires.php file:
define('SF_LIB_PATH', ROOT_PATH . '/lib/vendor/symfony/lib');
require_once SF_LIB_PATH . '/utils/sfToolkit.class.php';
require_once SF_LIB_PATH . '/utils/sfParameterHolder.class.php';
require_once SF_LIB_PATH . '/event_dispatcher/sfEventDispatcher.php';
require_once SF_LIB_PATH . '/request/sfRequestHandler.class.php';
require_once SF_LIB_PATH . '/request/sfRequest.class.php';
require_once SF_LIB_PATH . '/request/sfWebRequest.class.php';
require_once SF_LIB_PATH . '/response/sfResponse.class.php';
require_once SF_LIB_PATH . '/response/sfWebResponse.class.php';
First we define the location of the Symfony libraries and then we proceed to include the required files.
The only new class here is the sfRequestHandler which Fabien describes in his presentations, the other ones I just took from a symfony 1.3 distrubution.
With those files included, we are done with what refers to symfony, then we have to include the application files. So the contents of app_requires.php will be:
require_once ROOT_PATH . '/lib/dba/MongoLogReader.class.php';
require_once ROOT_PATH . '/lib/dba/CollectionModel.class.php';
require_once ROOT_PATH . '/apps/LogAnalyzer.class.php';
Besides the LogAnalyzer class we include two classes that will take care of querying the log database. As we can see, the LogAnalyzer class will reside under the apps folder and then others under lib/dba.
So now let's check what's inside the LogAnalyzer class.
public function __construct()
{
$this->dispatcher =
new sfEventDispatcher();
$this->dispatcher->connect('application.load_controller', array($this, 'loadController'));
}
On instantiation we create a new instance of the sfEventDispatcher and we connect our application to the application.loadController event which will be fired by the sfRequestHandler::handleRaw() method. There we tell it that the loadController method of our application will process the request.
Then we have the run method:
public function run()
{
$request = new sfWebRequest($this->dispatcher);
$handler = new sfRequestHandler($this->dispatcher);
$response = $handler->handle($request);
return $response;
}
Here we initialize a sfWebRequest object to start parsing the request parameters. Then we instantiate our sfRequestHandler and we call the handle method. The handle method will return a response object, which is the one where we call send() in the index.php file to output the response to the browser.
When the sfRequestHandler start to do it's job it will fire the application.load_controller event for which we set up the following listener:
public function loadController(sfEvent $event)
{
$event->setReturnValue(array(array($this, 'execute'), array($this->dispatcher, $event['request'])));
return true;
}
There we say that the method execute of the LogAnalyzer class will take care of generatiing the response data out of the request.
And finally the code for the execute method:
public function execute($dispatcher, $request)
{
$response = new sfWebResponse($dispatcher);
$response->setContent($this->render($this->getTemplateValues($request)));
return $response;
}
There we instantiate a sfWebResponse. The content of this one will be the result of the proteced method render. –Here I must say that is possible to create our own View class to
handle this part, but for this example I preffer to build it like this–.
protected function render($values)
{
extract($values);
ob_start();
ob_implicit_flush(0);
require(ROOT_PATH . '/apps/template.php');
return ob_get_clean();
}
This method expects an array with all the variables that will be used in the template. This values are extracted from the array and inserted in the current scope by calling the extract function. As we can see, the template is a
simple php file that is required from the apps folder. This file is plain PHP code embedded into HTML.
The getTemplateValues method take will get the data out of the database, plus interpreting the request:
protected function getTemplateValues($request)
{
$values = array();
$values['sf_request'] = $request;
$values['collections'] = $this->getCollections();
$values['priorities'] = $this->getPriorities();
$values['cursor'] = $this->getLogs($request);
$values['pageNumber'] = $request->getParameter('page', 1);
$values['cursor']->skip(($values['pageNumber'] - 1) * $this->maxPerPage)->limit($this->maxPerPage);
$values['hasMore'] = $values['cursor']->count() > ($this->maxPerPage * $values['pageNumber']);
$values['filterParams'] = $this->buildFilterParams($request);
return $values;
}
And that's it! Which such a simple structure we can leverage the power of the sfRequestHandler class which will be at the core of the new Symfony version. We know that symfony does very well for complex projects, but sometimes I felt like it was too big for a simple application like this one. With this new component I think that this distinction will be gone.
THE CODE:
The
application code and the
sfMongoDBLogger class can be found
here.
You will need to setup a virtual host in order to run this application.
RESOURCES/REQUIREMENTS:
If you setup MongoDB as explained here http://www.mongodb.org/display/DOCS/Getting+Started, you should be able to run this project without problems –Give it a try, MongoDB is pretty easy to setup and the documentation is very good–
For the installation instructions of the PHP native driver go
here
IMPORTANT:
Even if this should be implicit, keep in mind that this are my personal views on the subjects. This is by no means an official statement from the Symfony project. Is just what I believe this new component will be based on Fabien presentation.