Como estender os Controllers do Code Igniter

Estender um controller do Code Igniter é uma boa forma de carregar um controller com algumas pré-definições, como validação de usuário, carregamento de bibliotecas ou definições de idioma.

Como estender um controller?

Para estender um controller (ou qualquer outra parte do Code Igniter) o primeiro passo é criar um arquivo que será carregado automaticamente e terá a classe que iremos utilizar.

No meu caso, para estender um controller vou criar um arquivo chamado MY_Controller.php no diretório:

application/core/MY_Controller.php

Dentro este arquivo crio uma classe para estender meu controller, por exemplo:

class MY_AdminController extends CI_Controller {}

E dentro desta classe eu adiciono o que for necessário para o meu controller extendido.

class MY_AdminController extends CI_Controller {
	function __construct(){
		parent::__construct();
		header('Content-type: text/html; charset=utf-8');
		$user = new User();
		if(!$user->getLogged()){
			die('There is only one god and his name is Death, and there is only one thing we say to Death: "Not today"');
		}
	}
}

E no arquivo do seu controller, ao invés de estender o controller você extende o MY_AdminController ou qualquer outro nome que você tenha utilizado.

Você pode ter mais do que uma extensão de controller, no mesmo arquivo de extensão (MY_Controller.php) é só criar uma outra classe e estender o mesmo Controller.

class MY_AdminController extends CI_Controller {}
class MY_App extends CI_Controller {}

Uma outra forma de estender é utilizar os hooks, que permitem um controle mais sensível de onde e o que carregar.

E você, como faz isso nas suas aplicações?

Usando CasperJS para testar a navegação em um site

CasperJS é uma ferramenta poderosa para gerar scripts de navegação e testes com o PhantomJS. Quer saber mais sobre ele? Veja o site, é a melhor referência.
Vou me ater a mostrar um script que testa uma navegação simples aqui do And After: carregar a home, testar o título, clicar no primeiro link do menu, verificar se a url está correta e checar se um certo nó do DOM existe.

O grande barato do CasperJS é poder programar (fazer o script) de teste funcional, simulando carregamento de páginas, clique em elementos, preenchimento de formulários, etc… (relativamente todo tipo de interação possível do usuário) e com isso automatizar testes.

Bem, vamos ao código (comentado linha-a-linha), que é efetivamente simples e direto:

//carrega o casperjs
var casper = require('casper').create();

//define a página inicial
casper.start("https://andafter.org");

//testa, usando a API de testes, se o título da página é And After
casper.then(function() {
    this.test.assertEquals(this.getTitle(), "And After", "Título da página é And After");
});

//então clica no primeiro link dentro da ul#level-2
casper.then(function() {
    this.click("ul#level-2 li a:first-child");
});

//testa se o endereço aberto tem a url da categoria Carreira
casper.then(function() {
    this.test.assertEquals(this.getCurrentUrl(), "https://andafter.org/categoria/carreira", "Navega para primeiro item do menu, que é de categoria Carreira");
    //e testa se existe um elemento ul.posts
    this.test.assertExists("ul.posts", "Deve existir ul.posts, que lista os posts");
});

//roda o script
casper.run(function() {
    this.test.renderResults(true);
});

 

Tendo esse script salvo em um arquivo chamado teste.js é só rodar na linha de comando:
casperjs test teste.js

O output será algo como:

CasperJS - outpu

Usando resolve e reject da Deferred() da jQuery

O objeto Deferred da jQuery é extremamente poderoso e ainda não muito utilizado. Uma coisa interessante nele é registrar cadeias de chamadas (callbacks) e poder associá-las a estados de sucesso (resolve) ou erro (reject) de funções, sejam elas síncronas ou assíncronas.

Para o exemplo, preferi usar chamadas síncronas, visando a simplicidade. A idéia é que o usuário clique na área verde (uma div de id green) de uma página (o que vamos tratar como sucesso). Se clicar na vermelha (uma div de id red), trataremos como erro.

No javascript, vamos então criar um objeto Deferred:

var def = $.Deferred();

Vamos então a esse objeto associar alguns handlers:

– quando ele for resolvido:

def.done(function() {
   alert("Você clicou na área verde :-)");
});

– quando for rejeitado:

def.fail(function() {
   alert("Você clicou na área vermelha :-(");
});

– quando for tanto rejeitado como resolvido

def.always(function() {
   alert("Obrigado por testar!");
});

Com isso, temos 2 cadeias definidas já. Em caso do objeto ser resolvido, será dado um alert que indica que usuário clicou na área verde e depois um outro agradecendo o teste. Em caso de ser rejeitado, um alert falando que clicou na área vermelha e aí a mesma mensagem de agradecimento é invocada.

Para resolver ou rejeitar, é simples:

$("div#green").on("click", def.resolve);
$("div#red").on("click", def.reject);

Vejam no jsFiddle esse exemplo: http://jsfiddle.net/3J5zA/2/

Existem usos mais complexos e interessantes para o objeto Deferred, o exemplo feito é para mostrar como criar cadeias de resolve e reject de forma simples e invocá-las. Na documentação oficial pode-se encontrar mais explicações sobre os métodos usados, bem como de outros.

Google Analytics apenas em produção

O Google Analytics é uma ferramenta espetacular para trabalhar com métricas de navegação e e-commerce, implementei o "tracking" de diversos eventos no Eu Compraria que estão nos auxiliando a melhorar a interface e identificar os produtos mais procurados, pontos de desistência de compra, etc.

Mas como executamos diversos testes isso estava influenciando um pouco nossas métricas, então fui pesquisar como executar os códigos do Analytics apenas em produção (sem gambiarras) e descobri na documentação que tem uma variável do próprio GA que cuida disso.

Bloqueando o Google Analytics

O Analytics permite que você bloqueie a execução de determinada conta ativa na página através da criação de uma variável.

window['ga-disable-UA-XXXXXX-Y'] = true;

Substitua o UA-XXXXXX-Y pelo código identificador da sua conta no Google Acalytics e todas as páginas que tiverem esta variável (javascript) como true não irão armazenar cookies ou enviar dados para os servidores do Analaytics.

Com o Code Igniter adicionei no header das minhas views uma verificação do ENVIRONMENT da aplicação, sempre que for diferente de produção esta variável do Analytics é criada. Assim nem ambiente de homologação e nem ambiente de desenvolvimento afetam as métricas dos meus projetos. 🙂

Code Igniter – como carregar um helper em um model?

Nos últimos meses estou trabalhando bastante em dois projetos com Code Igniter: um manager (MongoDB + Code Igniter) e o sistema de e-commerce do Eu Compraria!

Aprendi vários macetes do framework e também melhorei o conhecimento que eu achava que tinha de MVC, antes eu fazia um uso excessivo de helpers para manipular coisas que hoje vejo que deveria estar no model – e em algumas funções precisei usar helpers no model, e não queria sobrecarregar a aplicação carregando este helper sempre.

Como carregar um helper em um model?

Em um controller normal carregar um helper do Code Igniter é muito simples:

$this->load->helper('url');

O $this no controller se refere a instância do Code Igniter e é ela que carrega os helpers, models e tudo mais do CI. Quando estamos em um model (estou usando Data Mapper e recomendo que você faça o mesmo) o $this é uma referência do própio objeto do model.

Para isso no model temos que recuperar a instância do CI e depois fazer com que esta carregue o helper (ou qualquer outra parte do código que o sistema precise), por exemplo:

$ci = get_instance();
$ci->load->helper('url');

Pronto, agora você pode carregar helpers, models e bibliotecas onde quiser: model, helper e bibliotecas.