quinta-feira, 25 de fevereiro de 2010

Diferentes JS em cada ambiente do Symfony.

Quem estiver querendo colocar um site em produção vai provavelmente se deparar com a necessidade de "minificar" os arquivos de javascript. Porém é interessante manter os arquivos não minificados no projeto também para ficar mais fácil de editar e debugar. Porém o view.yml do symfony não permite configurações sensíveis ao ambiente. Assim achei uma solução bem simples em http://shout.setfive.com/2009/06/09/loading-different-javascript-css-files-in-different-environments/ que é a seguinte:

1 - Crie as configurações (um array) para cada ambiente no app.yml com o nome de "javascript_files".

2 - no view.yml faça referência a estas configurações usando:

javascripts: [< ? php echo implode(',',sfConfig::get('app_javascript_files');) ? >]


Depois me disseram que o view.yml está "deprecated", então fiz o seguinte:

Incluí uma partial em todos meus leiautes e nessa parcial usei:


< ?php if(sfConfig::get('sf_debug')): ? >
< ?php use_javascript('x') ?>
< ?php use_javascript('y') ?>
< ?php use_javascript('z') ?>
< ? else: ? >
< ?php use_javascript('xyz.min.js') ?>
< ? endif; ? >

quarta-feira, 24 de fevereiro de 2010

Tratando exceções no ambiente de produção.

* ver o primeiro comentário para ajustar um bug de loop eterno na homepage

Objetivo: Logar qualquer exceção não tratada no código, mandar um email para a equipe, gravar um log e mostrar uma mensagem para o usuário do tipo: "Estamos em manutenção para melhor servir." enquanto a equipe corre pra consertar o erro.

Como Fazer

1 Primeiro conectamos um listener ao evento de exception. Inicialmente pensei em capturar isso com um filtro, mas vi que existem algumas exceções que são capturadas que atrapalham o bom funcionamento do framework, como por exemplo a ReflectionException que vai gerar um 404 mais na frente.

Usando um listener para o evento, capturamos apenas aquilo que o symfony não trata:


class frontendConfiguration extends sfApplicationConfiguration {
public function configure() {
$this->dispatcher->connect(
'application.throw_exception',
array('MyExceptionHandler', 'listenToApplicationException')
);
}
...


2 Depois criamos a classe que ser chamada, contendo o método que vai "ouvir" o evento, conforme determinamos acima:


class MyExceptionHandler extends sfFilter {
static public function listenToApplicationException($event)
{
// pegamos a exception original
$e = $event->getSubject();
// para passar um link na mensagem mostrada ao usuário
sfLoader::loadHelpers(array('Url'));
try {
// manda email e grava um log
self::notify($e);
}
catch(Exception $e) {
// "Engulindo" propositadamente os
// erros quando envia email de log
// para não impedir que a mensagem
// seja mostrada para o usuário
}
if(self::isAjax()) {
// lembre dos requests de ajax...
// tratar de acordo com o framework do freguês.
}
else {
sfContext::getInstance()->getUser()->setFlash(
'feedback',
sprintf(
Messages::UNEXPECTED_ERROR,
url_for('@support')
)
);
sfContext::getInstance()->getController()->redirect('@homepage');
}
// este evento específico chama cada listener até que um responda que pôde tratar o evento. Por isso esse retorno.
return true;
}

static function notify(Exception $e) {
// cria a mensagem...manda email e registra um log.
self::log($mensagem);
}

static function log($message) {
$exceptionLogLocation = sfConfig::get('sf_log_dir') . '/exceptions.log';
$fileHandle = fopen($exceptionLogLocation, "a+");
fwrite($fileHandle, $message."\n");
}

static function isAjax(){
return sfContext::getInstance()->getRequest()->isXmlHttpRequest() ||
sfContext::getInstance()->getRequest()->hasParameter('iframeAjaxUpload');

}
}


3 Feito. Fica aqui mais uma dica de algumas coisas que você pode capturar e colocar na mensagem para facilitar a vida depois:



$username = sfContext::getInstance()->getUser()->getService()->getUsername();
$referer = sfContext::getInstance()->getRequest()->getReferer();
$uri = sfContext::getInstance()->getRequest()->getUri();
$ajax = self::isAjax() ? 'Yes' : 'No';
'time' => date('Y-m-d H:i:s'),
'exception_class' => get_class($e),
'exception_message' => $e->getMessage(),
'trace' => $e->__toString()