Você está na página 1de 67

Laravel: De Aprendiz a Arteso (Brazilian

Portuguese)
Taylor Otwell and Pedro Borges
This book is for sale at http://leanpub.com/laravel-pt-br
This version was published on 2013-10-01

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools
and many iterations to get reader feedback, pivot until you have the right book and build
traction once you do.
2013 Taylor Otwell and Pedro Borges

Tweet This Book!


Please help Taylor Otwell and Pedro Borges by spreading the word about this book on Twitter!
The suggested tweet for this book is:
Acabei de comprar o livro Laravel: De Aprendiz a Arteso por @taylorotwell.
Find out what other people are saying about the book by clicking on this link to search for this
hashtag on Twitter:
https://twitter.com/search/#

Contedo
Nota do Autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Injeo de Dependncia .
O Problema . . . . . .
Construa um Contrato
Indo Alm . . . . . . .
No muito Java? . . .

.
.
.
.
.

2
2
3
5
7

Container IoC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Vinculao Bsica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Resoluo Reflexiva . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8
8
10

Interface Como Contrato . . . . . . . . . . .


Tipagem Forte & Aves Aquticas . . . . . .
Um Exemplo de Contrato . . . . . . . . . .
Interfaces e o Desenvolvimento em Equipe

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

13
13
14
16

Provedores de Servios . . .
Como Inicializador . . . .
Como Organizador . . . .
Provedores de Inicializao
Provedores do Ncleo . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

18
18
19
21
22

Estrutura de Aplicao . . .
Introduo . . . . . . . . .
MVC est te Matando . . .
Adeus Models . . . . . . .
Tudo Sobre as Camadas .
Onde Colocar as Coisas .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

23
23
23
24
25
26

Arquitetura Aplicada: Desacoplando os Manipuladores


Introduo . . . . . . . . . . . . . . . . . . . . . . . . .
Desacoplando os Manipuladores . . . . . . . . . . . . .
Outros Manipuladores . . . . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

30
30
30
33

Extendendo o Framework
Introduo . . . . . . . .
Gerenciadores e Fbricas
Cache . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

35
35
35
36

.
.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

CONTEDO

Sesso . . . . . . . . . . . . . . . . .
Autenticao . . . . . . . . . . . . . .
Extenses Baseadas no Container IoC
Request . . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

37
38
40
41

Princpio da Responsabilidade nica . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Em Ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43
43
43

Princpio do Aberto/Fechado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Em Ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47
47
47

Princpio da Substituio de Liskov . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Em Ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51
51
51

Princpio da Segregao de Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Em Ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55
55
55

Princpio da Inverso de Dependncia . . . . . . . . . . . . . . . . . . . . . . . . . . .


Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Em Ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59
59
59

Nota do Autor
Desde que criei o framework Laravel, tenho recebido inmeros pedidos de um livro com
instrues que ajudem na construo de aplicaes bem estruturadas e complexas. Como cada
aplicao nica, tal livro deve conter conselhos gerais, mas que ao mesmo tempo so prticos
e podem ser facilmente aplicados a uma infinidade de projetos.
Assim, comearemos cobrindo os elementos fundamentais da injeo de dependncia. Em
seguida, analisaremos a fundo os provedores de servios e estruturas de aplicao. Tambm
veremos rapidamente sobre os princpios SOLID. Um profundo conhecimento destes tpicos lhe
dar uma base slida para todos os seus projetos no Laravel.
Caso voc tenha alguma pergunta sobre arquitetura avanada no Laravel ou queira que eu
adicione algo ao livro, por favor me escreva. Eu planejo expandir este livro baseado na opinio
da comunidade. Suas ideias so importantes!
Para finalizar, muito obrigado por fazer parte da comunidade Laravel. Todos vocs ajudaram a
tornar o desenvolvimento em PHP mais gostoso e prazeroso a milhares de pessoas ao redor do
mundo. Programe com alegria!
Taylor Otwell

Injeo de Dependncia
O Problema
A fundao do framework Laravel seu poderoso container de inverso de controle (inversion
of control ou IoC, em ingls). Para compreender o framework de verdade, necessrio ter
um bom entendimento sobre o funcionamento do container. Entretanto, ns devemos notar
que o container IoC simplesmente um mecanismo conveniente para se alcanar o padro de
desenvolvimento de programas injeo de dependncia (dependency injection, em ingls). O uso
do container no obrigatrio para se injetar dependncias, ele apenas facilita esta tarefa.
Em primeiro lugar, vamos explorar porque a injeo de dependncia vantajosa. Considere a
seguinte classe e mtodo:
1

class UserController extends BaseController {

public function getIndex()


{
$users = User::all();

3
4
5
6

return View::make('users.index', compact('users'));

8
9
10

Mesmo sendo um cdigo conciso, no podemos test-lo sem acessar um banco de dados. Em
outras palavras, o ORM Eloquent est fortemente acoplado ao nosso controlador. No podemos,
de forma alguma, usar ou testar este controlador sem usar tambm todo o Eloquent, incluindo a
necessidade de um banco de dados. Este cdigo tambm viola um princpio de desenvolvimento
de programas conhecido como separao de conceitos (separation of concerns ou SoC, em ingls).
Em outras palavras: nosso controlador sabe mais do que deveria. Controladores no precisam
saber de onde os dados vm, mas somente como acess-los. O controlador no precisa saber que
os dados esto disponveis em MySQL, apenas que eles existem em algum lugar.

Separao de conceitos
Toda classe deve ter apenas uma responsabilidade e esta responsabilidade deve ser
completamente encapsulada pela classe.

Sendo assim, melhor desacoplar completamente nossa camada web (controlador) da nossa
camada de acesso aos dados. Isso permitir migrar mais facilmente nossa implementao de

Injeo de Dependncia

armazenamento de dados e tornar o nosso cdigo mais fcil de ser testado. Pense na web
apenas como uma camada de transporte para a sua aplicao real.
Imagine que sua aplicao um monitor com portas para diversos cabos. Voc pode acessar as
funcionalidades do monitor via HDMI, VGA ou DVI. Pense na internet como sendo apenas um
cabo conectado sua aplicao. O monitor funciona independente do cabo utilizado. O cabo
apenas um meio de transporte, assim como HTTP um meio de transporte que leva sua
aplicao. Assim sendo, ns no queremos entupir nosso meio de transporte (o controlador) com
a parte lgica da aplicao. Isso permitir que qualquer camada de transporte, tais como uma
API ou aplicao mvel, acessem a lgica da nossa aplicao.
Por isso, ao invs de acoplar o controlador ao Eloquent, vamos injetar uma classe repositria.

Construa um Contrato
Em primeiro lugar, vamos definir uma interface e uma implementao correspondente:
1

interface UserRepositoryInterface {

public function all();

3
4
5

6
7

class DbUserRepository implements UserRepositoryInterface {

public function all()


{
return User::all()->toArray();
}

9
10
11
12
13
14

Injeo de Dependncia

Em seguida, vamos injetar uma implementao desta interface em nosso controlador:


1

class UserController extends BaseController {

public function __construct(UserRepositoryInterface $users)


{
$this->users = $users;
}

3
4
5
6
7

public function getIndex()


{
$users = $this->users->all();

8
9
10
11

return View::make('users.index', compact('users'));

12

13
14
15

Nosso controlador completamente ignorante quanto ao local onde os dados esto sendo
armazenados. Neste caso, esta ignorncia benfica! Os dados podem estar em um banco de
dados MySQL, MongoDB ou Redis. Nosso controlador no reconhece a diferena, isso no
sua responsabilidade. Fazendo essa pequena mudana, ns podemos testar nossa camada
web separadamente da nossa camada de dados, alm de podermos facilmente alternar nossa
implementao de armazenamento de dados.

Respeite os limites
Lembre-se de respeitar os limites da responsabilidade. Controladores e rotas servem
como mediadores entre HTTP e sua aplicao. Ao escrever uma aplicao de grande
porte, no polua-os com lgica de domnio.

Para solidificar nossa compreenso, vamos escrever uma teste rpido. Em primeiro lugar, vamos
simular (mock, em ingls) o repositrio vinculando-o ao container IoC da aplicao. Em seguida,
nos certificaremos de que o controlador invoca o repositrio devidamente:

Injeo de Dependncia
1
2
3
4
5
6

public function testIndexActionBindsUsersFromRepository()


{
// Preparar...
$repository = Mockery::mock('UserRepositoryInterface');
$repository->shouldReceive('all')->once()->andReturn(array('foo'));
App::instance('UserRepositoryInterface', $repository);

// Agir...
$response = $this->action('GET', 'UserController@getIndex');

8
9
10

// Conferir...
$this->assertResponseOk();
$this->assertViewHas('users', array('foo'));

11
12
13
14

Faa de conta
Neste exemplo, ns usamos uma biblioteca chamada Mockery. Esta biblioteca oferece
uma interface limpa e expressiva para fazer os mocks das suas classes. Mockery pode
ser facilmente instalado via Composer.

Indo Alm
Vamos considerar um outro exemplo para solidificar nossa compreenso. Suponha que ns
queremos notificar nossos clientes sobre as cobranas realizadas em suas contas. Para isso, vamos
definir duas interfaces, ou contratos. Estes contratos nos daro flexibilidade para mudar suas
implementaes no futuro.
1
2
3

interface BillerInterface {
public function bill(array $user, $amount);
}

4
5
6
7

interface BillingNotifierInterface {
public function notify(array $user, $amount);
}

Injeo de Dependncia

Continuando, vamos construir uma implementao do nosso contrato chamado BillerInterface:


1

class StripeBiller implements BillerInterface {

public function __construct(BillingNotifierInterface $notifier)


{
$this->notifier = $notifier;
}

3
4
5
6
7

public function bill(array $user, $amount)


{
// Cobrar o usurio via Stripe...

8
9
10
11

$this->notifier->notify($user, $amount);

12

13
14
15

Porque separamos a responsabilidade de cada classe, agora ns podemos facilmente injetar


vrias implementaes de notificao em nossa classe de cobrana. Por exemplo, ns poderamos
injetar um SmsNotifier ou um EmailNotifier. A cobrana no est mais preocupado com a
implementao da notificao, somente com o contrato. Enquanto uma classe estiver de acordo
com o contrato (interface), a cobrana ir aceit-la. Alm do mais, ns no apenas adicionamos
flexibilidade, mas tambm a possibilidade de testarmos a nossa cobrana separadamente dos
notificadores apenas injetando um mock BillingNotifierInterface.

Use interfaces
Escrever interfaces pode parecer muito trabalho extra, mas na verdade, elas tornam o
seu desenvolvimento mais rpido. Use interfaces para simular e testar todo o back-end
da sua aplicao antes de escrever uma nica linha de implementao!

Ento, como ns podemos injetar uma dependncia? simples assim:


1

$biller = new StripeBiller(new SmsNotifier);

Isso injeo de dependncia. Ao invs da cobrana se preocupar em notificar os usurios,


ns simplesmente lhe passamos um notificador. Uma mudana simples como esta pode fazer
maravilhas sua aplicao. Seu cdigo instantaneamente se torna mais fcil de manter, porque as
responsabilidades de cada classe foram claramente definidas. A testabilidade de suas aplicaes
aumentar consideravelmente porque agora voc pode injetar mocks de dependncias para isolar
o cdigo em teste.
Mas, e quanto aos containers IoC? Eles no so necessrios na injeo de dependncia? Claro
que no! Conforme veremos nos prximos captulos, containers tornam a injeo de dependncia
mais fcil de gerenciar, mas o seu uso no obrigatrio. Seguindo os princpios deste captulo,
voc j pode praticar injeo de dependncia em qualquer projeto, mesmo que voc ainda no
tenha um container sua disposio.

Injeo de Dependncia

No muito Java?
Uma crtica muito comum do uso de interfaces em PHP que elas tornam o seu cdigo muito
parecido com o Java. Em outras palavras, o cdigo se torna muito verbal. Voc precisa definir
uma interface e uma implementao; isso exigir algumas tecladas a mais.
Para aplicaes simples e menores, esta crtica pode at ser vlida. Muitas vezes, as interfaces
no so necessrias nestas aplicaes e perfeitamente ok no us-las. Se voc tem certeza que
a implementao no mudar, voc no precisa criar uma interface.
J para aplicaes maiores, as interfaces sero muito teis. As tecladas extras no sero nada em
comparao com a flexibilidade e testabilidade que voc ganhar. Poder mudar rapidamente a
implementao de uma contrato arrancar um uau do seu chefe, alm de permitir que voc
escreva um cdigo que facilmente se adapta a mudanas.
Para concluir, tenha em mente que este livro apresenta uma arquitetura muito pura. Caso voc
precise reduz-la para uma aplicao menor, no sinta-se culpado. Lembre-se, voc est tentando
programar com alegria. Se voc no gosta do que faz ou est programando por culpa, pare um
pouco e faa uma reavaliao.

Container IoC
Vinculao Bsica
Agora que j aprendemos sobre a injeo de dependncia, vamos explorar os containers de
inverso de controle (IoC). Containers IoC tornam o gerenciamento de suas classes muito mais
conveniente e o Laravel possui um container muito poderoso. O container IoC a pea central do
framework Laravel, sendo o responsvel por todos os componentes do framework funcionarem
juntos. Na realidade, a classe Application no Laravel extende a classe Container!

Container IoC
Containers de inverso de controle tornam a injeo de dependncias mais conveniente.
Uma classe ou interface definida apenas uma vez no container e ele administra a
resoluo e injeo desses objetos sempre que necessrio em sua aplicao.

Em uma aplicao Laravel, o container IoC pode ser acessado via a faade (fachada, em francs)
App. O container possui uma infinidade de mtodos, mas vamos iniciar com o bsico. Seguiremos
usando nossas BillerInterface e BillingNotifierInterface do captulo anterior; assumiremos que nossa aplicao usa o servio Stripe para processar pagamentos. A implementao
Stripe da interface pode ser vinculada (bind, em ingls) ao container da seguinte maneira:
1
2
3
4

App::bind('BillerInterface', function()
{
return new StripeBiller(App::make('BillingNotifierInterface'));
})

Note que dentro do resolvedor BillerInterface ns tambm resolvemos uma implementao


da BillingNotifierInterface. Vamos definir este vnculo tambm:
1
2
3
4

App::bind('BillingNotifierInterface', function()
{
return new EmailBillingNotifier;
});

Como voc pode ver, o container um local onde podemos armazenar closures que resolvem
vrias classes. Quando uma classe registrada no container, ns podemos resolv-la facilmente
em qualquer lugar da nossa aplicao. Ns tambm podemos resolver o vnculo de outros
containers dentro de um resolvedor.
https://stripe.com

Container IoC

Tem espinha?
O container IoC do Laravel um substituto do container Pimple, criado por Fabien
Potencier. Ento, se voc j est usando o Pimple em algum projeto, sinta-se vontade
e faa um upgrade para o componente Illuminate Container para obter alguns recursos
extras!

Aps comearmos a usar o container, ns podemos alterar a implementao da interface


mudando apenas uma linha. Como exemplo, considere o cdigo a seguir:
1

class UserController extends BaseController {

public function __construct(BillerInterface $biller)


{
$this->biller = $biller;
}

3
4
5
6
7
8

Quando este controlador instanciado pelo container IoC, o StripeBiller, que inclue o
EmailBillingNotifier, ser injetado nesta instncia. Se mais tarde ns quisermos mudar a
implementao do notificador, s precisaremos alterar o vnculo:
1
2
3
4

App::bind('BillingNotifierInterface', function()
{
return new SmsBillingNotifier;
});

Agora, no importa onde o notificador resolvido em nossa aplicao, ns sempre obteremos a


implementao SmsBillingNotifier. Usando esta arquitetura, nossa aplicao poder alternar
implementaes de vrios servios rapidamente.
A possibilidade de poder mudar as implementaes de uma interface com apenas uma linha de
cdigo realmente facinante. Por exemplo, imagine que ns queremos mudar o provedor de
servio SMS para o Twilio. Ns podemos desenvolver uma nova implementao do notificador
para o Twilio e alterar o vnculo. Se encontrarmos algum problema na transio para o Twilio,
poderemos voltar para o provedor antigo fazendo uma simples alterao no vnculo IoC. Como
voc pode ver, os benefcios ao se usar a injeo de dependncia vo alm do que imediatamente
bvio. Voc consegue pensar em algum outro benefcio do uso da injeo de dependncia e do
container IoC?
s vezes, voc vai querer resolver apenas uma instncia de uma determinada classe em toda a
sua aplicao. O mtodo singleton nos ajudar nisto:
https://github.com/fabpot/pimple
https://github.com/illuminate/container
http://twilio.com

10

Container IoC
1
2
3
4

App::singleton('BillingNotifierInterface', function()
{
return new SmsBillingNotifier;
});

Agora, o container resolver o notificador de cobrana uma vez e continuar usando a mesma
instncia sempre que a interface do notificador for solicita.
O mtodo instance do container similar ao singleton. Porm, nele voc pode passar uma
instncia de um objeto j existente. Esta instncia ser usada sempre que o container precisar de
uma instncia dessa classe:
1

$notifier = new SmsBillingNotifier;

2
3

App::instance('BillingNotifierInterface', $notifier);

Agora que j estamos familiarizados com o bsico da resoluo de container usando closures,
vamos conhecer os seus recursos mais poderosos: a habilidade de resolver classes via reflexo.

Container autnomo
Voc est trabalhando em um projeto que no usa o framework Laravel? Voc
pode usar o container IoC do Laravel mesmo assim, basta instalar o pacote
illuminate/container via Composer!

Resoluo Reflexiva
Um dos recursos mais poderosos do container do Laravel a sua habilidade de automaticamente
resolver dependncias via reflexo. Reflexo a habilidade de inspecionar uma classe e seus
mtodos. Por exemplo, a classe do PHP ReflectionClass permite que voc inspecione mtodos
disponveis em uma determinada classe. O mtodo do PHP method_exists tambm uma forma
de reflexo. Para brincar um pouco com a classe de reflexo do PHP, tente o cdigo a seguir em
uma de suas classes:
1

$reflection = new ReflectionClass('StripeBiller');

2
3

var_dump($reflection->getMethods());

4
5

var_dump($reflection->getConstants());

Tirando proveito deste poderoso recurso do PHP, o container IoC do Laravel pode fazer algumas
coisas muito interessantes! Como exemplo, considere a seguinte classe:

Container IoC
1

11

class UserController extends BaseController {

public function __construct(StripeBiller $biller)


{
$this->biller = $biller;
}

3
4
5
6
7
8

Note que o controlador exige que o objeto passado seja uma instncia da classe StripeBiller.
Ns podemos extrair esta induo de tipo usando a reflexo. Quando o resolvedor para uma
classe no explicitamente vinculado ao container do Laravel, ele tentar resolver tal classe via
reflexo. A ordem a seguinte:
1. Existe um resolvedor para StripeBiller?
2. No? Vamos refletir na classe StripeBiller para descobrir se ela possui alguma dependncia.
3. Resolva qualquer dependncia da classe StripeBiller (recursivo).
4. Crie uma nova instncia de StripeBiller via ReflectionClass->newInstanceArgs().
Como voc pode perceber, o container faz toda a parte pesada do servio, poupando voc de ter
que escrever resolvedores para cada uma das suas classes. Este um dos recursos mais poderosos
e nicos do container do Laravel. Entender bem como o container funciona muito benfico
quando se est construindo uma aplicao grande.
Vamos modificar nosso controlador um pouquinho. Que tal se ele ficasse assim?
1

class UserController extends BaseController {

public function __construct(BillerInterface $biller)


{
$this->biller = $biller;
}

3
4
5
6
7
8

Assumindo que um resolvedor para a interface BillerInterface no foi vinculado explicitamente, como o container saber qual classe injetar? Lembre-se, interfaces no podem ser
instanciadas, elas so apenas contratos. Sem darmos mais informao ao container, ele no
poder instanciar esta dependncia. Ns precisamos especificar uma classe que ser usada como
implementao padro desta interface; fazemos isto atravs do mtodo bind:
1

App::bind('BillerInterface', 'StripeBiller');

12

Container IoC

Aqui, ns passamos uma string ao invs de uma closure. Esta string dir ao container para
usar a classe StripeBiller sempre que ele precisar de uma implementao da interface
BillerInterface. Novamente, ns ganhamos a habilidade de alternar as implementaes de
servios mudando uma nica linha no container de vinculao. Por exemplo, se quisermos usar
o servio Balanced Payments para processar pagamentos, precisaremos apenas escrever uma
nova implementao BalancedBiller da interface BillerInterface e mudar o nosso container
desta forma:
1

App::bind('BillerInterface', 'BalancedBiller');

Automaticamente, esta nova implementao ser usada por toda a nossa aplicao!
Voc tambm pode usar o mtodo singleton para vincular implementaes s interfaces. Assim,
o container ir criar apenas uma instncia da classe por ciclo de request:
1

App::singleton('BillerInterface', 'StripeBiller');

Mestre em container
Deseja aprender mais sobre o container do Laravel? Leia seu cdigo-fonte! O container
apenas uma classe: Illuminate\Container\Container. Leia todo o seu cdigo para
entender melhor como ele funciona debaixo do cap.

https://balancedpayments.com

Interface Como Contrato


Tipagem Forte & Aves Aquticas
Nos captulos anteriores, ns vimos o bsico da injeo de dependncia: o que ela ; como
ela aplicada; e, muito dos seus benefcios. Os exemplos dados nesses captulos tambm
demonstraram a injeo de interfaces em nossas classes. Por isso, antes de avanarmos, acho
bom falar mais profundamente sobre as interfaces, visto que muitos programadores de PHP no
esto familiarizados como elas.
Antes de me tornar um programador de PHP, eu programava .NET. Eu gosto de sofrer ou o qu?
Em .NET, interfaces esto por toda parte. Na realidade, muitas interfaces so definidas como
parte do prprio ncleo do framework .NET, e por uma boa razo: muitas das suas linguagens,
como C# e VB.NET, possuem tipagem forte. Em outras palavras, voc precisa definir o tipo do
objeto ou um tipo primitivo ser passado para o mtodo. Por exemplo, considere este mtodo em
C#:
1
2
3
4

public int BillUser(User user)


{
this.biller.bill(user.GetId(), this.amount)
}

Observe que fomos obrigados a definir no somente o tipo dos argumentos que passaremos para
o mtodo, mas tambm o que o prprio mtodo dever retornar. C# encoraja tipagem segura.
No poderemos passar nada alm de um objeto User para o mtodo BillUser.
Entretanto, o PHP de modo geral usa duck typing (tipagem do pato). No duck typing, os mtodos
disponveis em um objeto determinam a forma como ele pode ser usado, ao invs da sua herana
ou da implementao de uma interface. Vejamos um exemplo:
1
2
3
4

public function billUser($user)


{
$this->biller->bill($user->getId(), $this->amount);
}

Em PHP, no precisamos dizer ao mtodo qual tipo de argumento ele deve aceitar. Podemos
passar qualquer tipo, desde que tal objeto responda ao mtodo getId. Este um exemplo de
duck typing. Se o objeto se parece com um pato, anda como um pato, grasna como um pato, ele
deve ser um pato. Ou, no nosso caso, se o objeto se parece com um usurio (user, em ingls) e
age como um usurio, ele deve ser um.

Interface Como Contrato

14

Mas, e o PHP? Ele no possui nenhum recurso da tipagem forte? Claro que possui! O PHP possui
uma mistura de tipagem forte e duck typing. Para ilustrar isso, vamos reescrever nosso mtodo
billUser:
1
2
3
4

public function billUser(User $user)


{
$this->biller->bill($user->getId(), $amount);
}

Aps induzirmos o tipo User em nosso mtodo, podemos garantir que todo objeto passado para
billUser ser uma instncia de User ou ser uma instncia de outra classe que extenda a classe
User.
H vantagens e desvantagens em ambos os sistemas de tipagem. Nas linguagens de tipagem
forte, o compilador pode lhe dar um minucioso relatrio de erros durante a compilao, o
que extremamente til. As entradas e sadas dos mtodos so muito mais explcitas em uma
linguagem de tipagem forte.
Mas ao mesmo tempo, o carter explcito da tipagem forte torna-a muito rgida. Por exemplo,
mtodos dinmicos como o whereEmailOrName, oferecidos pelo Eloquent, seriam impossveis
de serem implementados em liguagens de tipagem forte como C#. No entre em discusses
acaloradas sobre qual paradigma melhor e lembre-se, cada um possui suas vantagens e
desvantagens. No errado usar a tipagem forte disponvel em PHP nem errado usar duck
typing. Errado seria usar exclusivamente um, ou o outro, sem considerar o problema que precisa
ser resolvido.

Um Exemplo de Contrato
Interfaces so contratos. Elas no possuem nenhum cdigo, apenas definem alguns mtodos que
um objeto deve implementar. Se um objeto implementa uma interface, podemos ter certeza que
cada mtodo definido pela interface vlido e poder ser invocado naquele objeto. Uma vez que
o contrato garante a implementao de certos mtodos, a tipagem segura torna-se mais flexvel
atravs do polimorfismo.

Polioqu?
Polimorfismo uma palavra grande que essencialmente significa: uma substncia
que possui mltiplas formas. No contexto deste livro, uma interface que pode
ter muitas implementaes. Por exemplo, UserRepositoryInterface pode ter uma
implementao para MySQL e uma para Redis e ambas serem instncias vlidas da
interface UserRepositoryInterface.

Para ilustrar a flexibilidade que as interfaces trazem para as linguagens de tipagem forte, vamos
escrever um cdigo simples para realizar reservas em hoteis. Considere esta interface:

Interface Como Contrato


1
2
3
4

15

interface ProviderInterface {
public function getLowestPrice($location);
public function book($location);
}

Quando nosso usurio reserva um quarto, ns queremos que esta ao seja registrada pelo
sistema. Por isso, vamos adicionar alguns mtodos nossa classe User:
1

class User {

public function bookLocation(ProviderInterface $provider, $location)


{
$amountCharged = $provider->book($location);

3
4
5
6

$this->logBookedLocation($location, $amountCharged);

8
9
10

Como estamos induzindo o tipo ProviderInterface, nossa classe User pode seguramente
assumir que o mtodo book sempre estar disponvel. Isto nos d a flexibilidade de podermos
reutilizar o mtodo bookLocation independente do hotel escolhido pelo usurio. Para finalizar,
vamos endurecer esta flexibilidade:
1

$location = 'Hilton, Dallas';

2
3
4
5
6

$cheapestProvider = $this->findCheapest($location, array(


new PricelineProvider,
new OrbitzProvider,
));

7
8

$user->bookLocation($cheapestProvider, $location);

Maravilhoso! No importa qual provedor possui o menor preo, ns podemos pass-lo para a
instncia do nosso User para que a reserva seja efetuada. Como a classe User est pedindo
somente instncias de um objeto que esteja de acordo com o contrato ProviderInterface,
nosso cdigo continuar funcionando mesmo quando adicionarmos novas implementaes de
provedores.

Esquea os detalhes
Lembre-se, as interfaces no fazem nada. Elas apenas definem um conjunto de mtodos
que devem existir na classe que as implementa.

Interface Como Contrato

16

Interfaces e o Desenvolvimento em Equipe


Quando sua equipe est desenvolvendo uma aplicao grande, cada parte vai progredir em uma
velocidade diferente. Por exemplo, imagine que um desenvolvedor est trabalhando na camada
de dados, enquanto outro trabalha no front-end e na camada web (controlador). O desenvolvedor
do front-end deseja testar seus controladores, mas o back-end est atrasado. Que tal se estes dois
desenvolvedores concordassem em uma interface, ou contrato, que fosse implementada pelas
classes do back-end? Algo assim:
1
2
3

interface OrderRepositoryInterface {
public function getMostRecent(User $user);
}

Tendo concordado nesse contrato, o desenvolvedor do front-end poder testar seus controladores,
mesmo que nenhuma implementao real tenha sido escrita! Isso permitir que os componentes
de uma aplicao sejam escritos em velocidades diferentes, alm de permitir que os testes tambm
sejam devidamente escritos. E mais, esta abordagem permitir que implementaes inteiras
mudem sem quebrar outras. Lembre-se, esta ignorncia benfica. No queremos que nossas
classes saibam como uma dependncia realiza algo, mas apenas que ela capaz de realiz-lo.
Assim, agora que temos um contrato definido, vamos para o controlador:
1

class OrderController {

public function __construct(OrderRepositoryInterface $orders)


{
$this->orders = $orders;
}

3
4
5
6
7

public function getRecent()


{
$recent = $this->orders->getMostRecent(Auth::user()):

8
9
10
11

return View::make('orders.recent', compact('recent'));

12

13
14
15

O desenvolvedor do front-end poderia at mesmo escrever uma implementao fictcia da


interface. Desta forma, os views da aplicao recebero dados fictcios:

Interface Como Contrato


1

17

class DummyOrderRepository implements OrderRepositoryInterface {

public function getMostRecent(User $user)


{
return array('Order 1', 'Order 2', 'Order 3');
}

3
4
5
6
7
8

Aps ter escrito uma implementao fictcia, ela poder ser vinculada ao container IoC e usada
por toda a sua aplicao:
1

App::bind('OrderRepositoryInterface', 'DummyOrderRepository');

Ento, quando uma implementao real, como a RedisOrderRepository, for escrita pelo
desenvolvedor do back-end, o vnculo IoC poder ser alternado para a nova implementao e
sua aplicao j estar usando os pedidos armazenados em Redis.

Interface como esquemtica


Interfaces so teis no desenvolvimento do esqueleto das funcionalidades providas por sua aplicao. Use-as durante o planejamento de um componente
para facilitar a discusso entre sua equipe. Por exemplo, defina uma interface
BillingNotifierInterface e converse com sua equipe sobre os mtodos dela. Use
a interface para concordar em uma boa API antes de comear a escrever o cdigo de
implementao!

Provedores de Servios
Como Inicializador
Um provedor de servio no Laravel uma classe que registra vnculos do container IoC. O Laravel
j vem com dezenas de provedores de servios que gerenciam os vnculos dos containers dos
componentes do seu ncleo. Quase todos os componentes do framework so registrados com
um container em um provedor. A lista destes provedores usados por sua aplicao pode ser
encontrada no array providers no arquivo de configurao app/config/app.php.
Um provedor de servio deve possuir pelo menos um mtodo: register. O mtodo register
onde o provedor vincula classes ao container. Quando sua aplicao recebe uma request e o
framework inicializado, o mtodo register invocado em todos os provedores listados no
arquivo de configurao. Isto acontece no comeo do ciclo de vida da sua aplicao para que
todos os servios providos pelo Laravel estejam sua disposio quando os seus prprios arquivos
forem carregados, tais como aqueles na pasta start.

Register x Boot
Nunca tente usar um servio no mtodo register. A nica tarefa deste mtodo deve
ser registrar o vnculo de objetos ao container IoC. Qualquer resoluo e interao com
as classes vinculadas devem ser feitas dentro do mtodo boot.

Alguns pacotes de terceiros instalados via Composer j vem com um provedor de servio.
Tipicamente, as instrues de instalao destes pacotes solicitaro que voc registre o provedor
em sua aplicao no array providers, no arquivo de configurao. Assim que o provedor de um
pacote registrado, ele est pronto para ser usado.

Provedores de pacotes
Nem todos os pacotes de terceiros precisaro de um provedor de servio. Na realidade,
nenhum pacote requer um provedor, porque geralmente os provedores de servio
apenas inicializam componentes para que estes possam ser usados imediatamente. Eles
so apenas um local conveniente para se organizar a inicializao e os vnculos dos
containers.

Provedores Diferidos
Nem todos os provedores listados no array de configurao providers sero inicializados
em todas as requests. Isto afetaria a performance da sua aplicao, especialmente quando
esses provedores de servios no so usados em todas as requests. Por exemplo, o servio
QueueServiceProvider no necessrio sempre, apenas quando o componente Queue utilizado.

Provedores de Servios

19

Para poder instanciar apenas os provedores que sero utilizados em uma determinada request,
o Laravel gera uma manifesto de servio e o armazena na pasta app/storage/meta. Este
manifesto lista todos os provedores de servios da sua aplicao, bem como os nomes dos
containers de vinculao registrados por eles. Desta forma, quando sua aplicao precisa usar
o componente Queue, o Laravel sabe que hora de instanciar e rodar o QueueServiceProvider,
porque ele est listado como o provedor do servio queue no manifesto. Isto permite que o
framework carregue somente os provedores de servios necessrios em cada request, aumentando
consideravelmente a performance.

Gerao do manifesto
Quando voc acrescenta um novo provedor de servio ao array providers, o Laravel
gerar automaticamente um novo manifesto de servios na prxima vez em que a sua
aplicao for usada.

Quando voc tiver um tempinho, d uma olhada no manifesto de servios para conhecer o seu
contedo. Entender esta estrutura ser til quando voc precisar depurar um provedor de servio
diferido.

Como Organizador
Uma das chaves para se construir uma aplicao Laravel bem arquitetada aprender a usar
os provedores de servios como uma ferramenta organizacional. Quando voc registra muitas
classes com o container IoC nos seus arquivos app/start, todos estes vnculos comearo a polulos. Ao invs de registrar containers nesses arquivos, crie provedores de servios para registrar
servios relacionados.

Inicializando
Os arquivos start de sua aplicao vivem na pasta app/start. Eles so arquivos
inicializadores que so carregados com base no tipo de request. O arquivo global
start.php o primeiro a ser carregado, seguido pelo arquivo que possui o mesmo
nome que o environment (ambiente). J o arquivo start artisan.php carregado
sempre que um comando executado no console.

Vamos explorar um exemplo. Suponha que sua aplicao esteja usando o servio Pusher para
enviar (push, em ingls) mensagens para os clientes via WebSockets. Para no ficarmos presos
ao Pusher, acho melhor criarmos uma interface EventPusherInterface e uma implementao
PusherEventPusher. Isto nos permitir mudar de provedor de WebSocket no futuro conforme
nossas necessidades mudem e nossa aplicao cresa.

http://pusher.com

Provedores de Servios
1
2
3

20

interface EventPusherInterface {
public function push($message, array $data = array());
}

4
5

class PusherEventPusher implements EventPusherInterface {

public function __construct(PusherSdk $pusher)


{
$this->pusher = $pusher;
}

7
8
9
10
11

public function push($message, array $data = array())


{
// Enviar a mensagem via a SDK do Pusher...
}

12
13
14
15
16
17

Em seguida, vamos criar um provedor EventPusherServiceProvider:


1

use Illuminate\Support\ServiceProvider;

2
3

class EventPusherServiceProvider extends ServiceProvider {

public function register()


{
$this->app->singleton('PusherSdk', function()
{
return new PusherSdk('app-key', 'secret-key');
});

5
6
7
8
9
10
11

$this->app->singleton('EventPusherInterface', 'PusherEventPusher');

12

13
14
15

Excelente! Agora ns temos uma abstrao para o envio de mensagens, bem como um lugar
conveniente para registrar vnculos no container. Finalmente, ns s precisamos acrescentar
o EventPusherServiceProvider ao nosso array providers no arquivo app/config/app.php.
Agora j podemos injetar a interface EventPusherInterface em qualquer controlador ou classe
em nossa aplicao.

Quando devo usar singleton?


Voc deve avaliar se melhor usar bind ou singleton para vincular suas classes.
Caso voc precise de apenas uma instncia da classe por request, use singleton. Do
contrrio, use bind.

Provedores de Servios

21

Note que o provedor de servio possui uma instncia $app herdada da classe base, ServiceProvider.
Ela uma instncia completa de Illuminate\Foundation\Application, que extende a classe
Container. Por isso, podemos usar todos os mtodos do container IoC que j estamos acostumados. Se voc preferir usar a faade App no provedor de servio, fique vontade:
1

App::singleton('EventPusherInterface', 'PusherEventPusher');

claro que os provedores de servios no esto limitados a registrarem apenas certos tipos
de servios. Ns poderamos us-los para registrar um servio para armazenar arquivos na
nuvem, ou outra view engine como o Twig, etc. Eles so apenas ferramentas inicializadoras e
organizacionais para sua aplicao. Nada mais.
Ento, no fique como medo de criar os seus prprios provedores de servios. Eles no so
limitados a pacotes que sero distribudos, muito pelo contrrio, so uma excelente ferramenta
para ajud-lo na organizao das suas aplicaes. Seja criativo e use-os para inicializar os vrios
componentes da sua aplicao.

Provedores de Inicializao
Os provedores de servios sero inicializados durante o seu registro, em seguida, o mtodo
boot ser executado em cada provedor. Um erro muito comum tentar usar o servio de outro
provedor direto no mtodo register. Mas, como dentro do mtodo register ns no podemos
ter certeza de que os demais provedores foram carregados, o servio que voc est tentando usar
pode no estar disponvel ainda. Por isso, sempre use o mtodo boot no provedor de servio para
escrever um cdigo que usa outros servios. O mtodo register deve ser usado somente para
registrar servios com o container.
Dentro do mtodo boot, faa o que voc quiser: registre listeners de eventos, inclua um arquivo
com rotas, registre filtros ou o que voc conseguir imaginar. Novamente, use os provedores como
uma ferramenta organizacional. Voc gostaria de agrupar alguns listeners de eventos? Coloc-los
no mtodo boot de um provedor de servio uma tima ideia! Voc tambm poderia incluir um
arquivo PHP com eventos ou rotas:
1
2
3

public function boot()


{
require_once __DIR__.'/events.php';

require_once __DIR__.'/routes.php';

5
6

Agora que j aprendemos sobre a injeo de dependncia e como organizar nossos projetos
usando provedores, temos uma tima base para construir aplicaes Laravel bem arquitetadas,
fceis de manter e testar. Em seguida, vamos explorar como o prprio Laravel usa os provedores
e como o motor do framework funciona!

Provedores de Servios

22

Lembre-se
No assuma que os provedores de servios devem ser usados somente dentro de pacotes.
Crie o seu prprio provedor para ajud-lo a organizar os servios da sua aplicao.

Provedores do Ncleo
A essa altura, voc j deve saber que sua aplicao possui muitos provedores de servios
registrados no arquivo de configurao app.php. Cada um desses provedores inicializa uma
parte do ncleo do framework. Por exemplo, o provedor MigrationServiceProvider inicializa vrias classes que executam migraes e tambm o comando migrate do Artisan. O
EventServiceProvider inicializa e registra a classe Dispatcher. Alguns desses provedores so
maiores do que os outros, mas cada um deles inicializa uma parte do ncleo.

Conhea seus provedores


Uma das melhores formas de aprimorar os seus conhecimentos sobre o ncleo do
Laravel lendo o cdigo dos seus provedores de servios. Se voc j est familiarizado
com as funes dos provedores de servios e sabe o que cada provedor do ncleo
registra, voc ter uma compreenso muito melhor de como o framework Laravel
funciona debaixo do cap.

A maioria dos provedores so diferidos, ou seja, eles no so carregados em todas as requests. Entretanto, alguns deles, como o FilesystemServiceProvider e o ExceptionServiceProvider, so
carregados sempre, pois eles inicializam partes essenciais para o funcionamento do framework.
Algum pode at dizer que os provedores de servios do ncleo e o container da aplicao so
o Laravel. Eles so responsveis por unir as muitas partes do framework de uma forma coesa;
estes provedores formam o alicerce do framework.
Conforme j mencionado, se voc quiser entender profundamente como o framework funciona,
leia o cdigo-fonte dos provedores de servio do seu ncleo. Ao l-lo, voc compreender como
o framework formado e o que cada provedor pode oferecer sua aplicao. Alm do mais, voc
estar melhor preparado para contribuir para o prprio Laravel!

Estrutura de Aplicao
Introduo
Onde devo colocar esta classe? Esta uma pergunta muito comum quando se usa um
framework na criao de uma aplicao. Muitos desenvolvedores se fazem esta pergunta porque
eles j ouviram que Modelo (model, em ingls) significa Banco de Dados. Por isso, eles tm
controladores que interagem com HTTP, modelos que realizam algo com o banco de dados
e views que abrigam o cdigo HTML. Mas, e as classes que enviam e-mails, validam dados
e interagem com uma API para obter informaes? Neste captulo, vamos aprender como
estruturar uma boa aplicao usando o framework Laravel, alm de ver quais so alguns dos
obstculos que mais atrapalham os desenvolvedores no desenvolvimento de uma boa aplicao.

MVC est te Matando


O maior obstculo para que os desenvolvedores alcancem um bom design em suas aplicaes
uma sigla bem pequena: M-V-C. Modelos, views e controladores tm dominado o pensamento
dos frameworks para a web por vrios anos, em parte, devido popularidade do Ruby on Rails.
E, mesmo assim, pea a um desenvolvedor para definir modelos. Normalmente, voc ouvir
alguma coisa seguido pelas palavras banco de dados. Supostamente, o modelo o banco de
dados. onde tudo o que tem a ver com o banco de dados deve ir. Porm, voc rapidamente
aprender que sua aplicao precisa de muito mais lgica do que uma simples classe para acessar
o banco de dados. Ela precisar validar dados, acessar servios externos, enviar e-mails e muito
mais.

O que um modelo?
A palavra modelo tornou-se to ambgua que acabou perdendo o seu significado.
Desenvolver com um vocabulrio especfico nos ajudar a dividir nossa aplicao em
partes menores, classes mais limpas e com uma responsabilidade bem definida.

Ento, qual a soluo para este dilema? Muitos tem enchido seus controladores com lgica.
Quando estes controladores se tornam enormes, eles precisam reutilizar a lgica presente em outros controladores. Ao invs de extrair essa lgica para uma outra classe, muitos desenvolvedores
erroneamente assumem que eles precisam invocar controladores a partir de outros controladores;
um padro conhecido como HMVC. Infelizmente, esta soluo geralmente indica que uma
aplicao foi mal planejada e que os controladores se tornaram muito complicados.

Estrutura de Aplicao

24

HMVC (geralmente) indica falta de planejamento


J precisou invocar um controlador a partir de um outro controlador? Isso um
indicador de que a aplicao foi mal planejada e de que h muita lgica em seus
controladores. Extraia essa lgica para uma terceira classe para que ela possa ser
injetada em qualquer controlador.

Mas existe uma forma melhor de estruturar aplicaes. Precisamos limpar das nossas mentes
tudo o que j aprendemos sobre modelos. Na verdade, vamos excluir a pasta models e comear
do zero!

Adeus Models
J exluiu a pasta models? Ainda no? Livre-se dela logo! Vamos criar uma nova pasta dentro
da pasta app e dar a ela o nome da nossa aplicao. Para esta discusso, vamos chamar nossa
aplicao de QuickBill (Cobrana Rpida) e continuaremos usando algumas das interfaces e
classes dos captulos anteriores.

Lembre-se do contexto
Se voc est desenvolvendo uma aplicao pequena usando o Laravel, no h nenhum
problema em criar alguns modelos com o Eloquent na pasta models. Porm neste
captulo, estamos preocupados em descobrir uma arquitetura em camadas mais
adequada para projetos grandes e complexos.

Assim, j devemos ter uma pasta chamada QuickBill na pasta da nossa aplicao, no mesmo
nvel das pastas controllers e views. Podemos criar quantas pastas quisermos dentro de
app/QuickBill. Vamos criar apenas duas por enquanto: Repositories e Billing. Tendo criado
estas pastas, lembre-se de registr-las no arquivo composer.json para que o carregamento
automtico PSR-0 funcione:
1
2
3
4
5

"autoload": {
"psr-0": {
"QuickBill": "app/"
}
}

Por enquanto, vamos deixar nossas classes Eloquent na raiz da pasta QuickBill. Assim, poderemos acess-las facilmente como QuickBill\User, QuickBill\Payment, etc. A pasta Repositories
ser o lugar de classes como: PaymentRepository e UserRepository; estas devem conter todas as
funes que acessam dados, tais como getRecentPayments e getRichestUser. A pasta Billing
dever conter todas as classes e interfaces que trabalham com outros servios de pagamento,
como Stripe e Balanced. Ao final, a estrutura desta pasta ser algo assim:

Estrutura de Aplicao
1
2
3
4
5
6
7
8
9
10
11
12
13

25

// app
// QuickBill
// Repositories
-> UserRepository.php
-> PaymentRepository.php
// Billing
-> BillerInterface.php
-> StripeBiller.php
// Notifications
-> BillingNotifierInterface.php
-> SmsBillingNotifier.php
User.php
Payment.php

E a validao?
Onde realizar a validao uma dvida muito comum. Considere colocar os mtodos
de validao nas classes de entidades, como: User.php e Payment.php. Alguns
possveis nomes para estes mtodos seriam: validForCreation ou hasValidDomain.
Alternativamente, voc poder criar uma classe UserValidator dentro do namespace
Validation e injet-la no seu repositrio. Experimente com estas possibilidades para
descobrir qual lhe agrada mais!

Eliminar a pasta models geralmente lhe ajudar a retirar as barreiras mentais que atrapalham um
bom desenvolvimento, permitindo que voc crie uma pasta com uma estrutura mais adequada
sua aplicao. Com certeza suas aplicaes tero algumas semelhanas, porque toda aplicao
complexa necessita de uma camada de acesso a dados (repositrio), camadas de servios externos
e por a vai.

No tenha medo das pastas


No tenha medo de criar outras pastas para organizar sua aplicao. Sempre divida sua
aplicao em componentes menores, cada um com uma responsabilidade bem definida.
Pensar alm do modelo lhe ajudar muito. Por exemplo, voc pode criar uma pasta
Repositories para armazenar todas as classes que acessam dados.

Tudo Sobre as Camadas


Como voc j deve ter notado, a chave para uma aplicao slida a separao das responsabilidades, ou criao de camadas de responsabilidades. A responsabilidade dos controladores
receber requests HTTP e invocar as classes de camada de negcio apropriadas. Sua camada
de negcio, ou de domnio, a sua aplicao. Elas contm as classes que acessam os dados,
realizam a validao, processam os pagamentos, enviam os e-mails e qualquer outra funo de
sua aplicao. Na realidade, sua camada de domnio no precisa nem saber que a web existe! A

Estrutura de Aplicao

26

web apenas um meio de transporte que d acesso sua aplicao e o seu conhecimento no deve
ir alm das camadas de controle (controllers) e de roteamento. Alcanar uma boa arquitetura
um desafio enorme, mas traz como benefcios uma grande sustentabilidade e um cdigo limpo.
Por exemplo, ao invs de acessar uma instncia da request da web na classe, ns podemos enviar
a input do controlador para a classe. Esta simples mudana desacoplar a sua classe da web e
ela poder ser testada sem se preocupar com a simulao de uma request:
1

class BillingController extends BaseController {

public function __construct(BillerInterface $biller)


{
$this->biller = $biller;
}

3
4
5
6
7

public function postCharge()


{
$this->biller->chargeAccount(Auth::user(), Input::get('amount'));

8
9
10
11

return View::make('charge.success');

12

13
14
15

Agora ficou muito mais fcil testar o mtodo chargeAccount, pois ele no precisa usar as classes
Request ou Input dentro da nossa implementao da BillerInterface. Precisamos apenas lhe
dar o valor a ser cobrado como um nmero inteiro.
A separao das responsabilidades uma das chaves para escrevermos aplicaes sustentveis.
Pergunte-se sempre se uma classe sabe mais do que ela deveria. Pergunte-se: Esta classe deveria
de importar com X ? Se a resposta for no, extraia esta lgica para outra classe e injete-a como
uma dependncia.

A nica razo para mudar


Uma tima forma de saber se uma classe possui reponsabilidades alm do que deveria,
examinar a razo das mudanas realizadas nela. Por exemplo, nos deveramos alterar
o cdigo de uma implementao da interface Biller enquanto ajustamos a lgica da
notificao? Claro que no. Sua responsabilidade realizar a cobrana e trabalhar com
a lgica da notificao somente via um contrato. Manter isso em mente enquanto voc
programa ir ajud-lo a identificar as reas onde sua aplicaes pode melhorar.

Onde Colocar as Coisas


Ao desenvolver aplicaes usando o Laravel, s vezes voc ter dvidas sobre onde colocar algo.
Por exemplo, onde eu devo colocar as funes auxiliares (helpers, em ingls)? Onde eu devo pr

Estrutura de Aplicao

27

os listeners de eventos? E os view composers? Voc ficar surpreso, mas a resposta : Onde voc
quiser! O Laravel no tem muitas convenes sobre onde os arquivos devem existir. Mas, como
muitas vezes esta resposta no satisfatria, vamos explorar alguns possveis locais para tais
coisas antes de avanarmos.

Funes Auxiliares
O Laravel j vem com um arquivo repleto de funes auxiliares: support/helpers.php. Caso
voc queira criar um arquivo parecido com funes relevantes ao seu projeto e estilo de programao, um timo lugar para inclu-las nos arquivos start. No arquivo start/global.php, que
includo em todas as requests, voc pode requerer o seu prprio arquivo helpers.php:
1

// Dentro de app/start/global.php

2
3

require_once __DIR__.'/../helpers.php';

Listeners de Eventos
Como os listeners de eventos no pertencem ao arquivo routes.php e podem comear a poluir os
arquivos start, ns precisamos de um outro local para este cdigo. Uma boa opo seria criar um
provedor de servio. Conforme j aprendemos, os provedores de servios no servem apenas para
registrar vinculaes nos containers IoC. Eles podem ser usados para qualquer tipo de trabalho.
Agrupar o registro de eventos em um provedor de servio manter este cdigo perfeitamente
escondindo nos bastidores da sua aplicao. View composers, que so um tipo de evento, tambm
podem ser agrupados em um provedor de servio.
Por exemplo, um provedor de servio que registra eventos ficaria assim:
1

<?php namespace QuickBill\Providers;

2
3

use Illuminate\Support\ServiceProvider;

4
5

class BillingEventsProvider extends ServiceProvider {

public function boot()


{
Event::listen('billing.failed', function($bill)
{
// Fazer algo quando a cobrana falhar...
});
}

7
8
9
10
11
12
13
14
15

Aps criarmos este provedor, s precisamos acrescent-lo ao nosso array providers no arquivo
de configurao app/config/app.php.

Estrutura de Aplicao

28

Por que boot?


No exemplo acima, escolhemos usar o mtodo boot por um bom motivo, lembra? O
mtodo register em um provedor de servio s deve ser usado para vincular classes
ao container.

Manipulando Erros
Se sua aplicao lida com muitos erros, este cdigo logo tomar conta do arquivos start. Por
isso, como fizemos com os eventos, melhor mover este cdigo para um provedor de servio.
Seu nome pode ser algo assim: QuickBillErrorProvider. Todo o cdigo para manipular os erros
da sua aplicao pode ser registrado no mtodo boot desse provedor. Novamente, isso manter
esse tipo de cdigo, to comum, longe dos arquivos da sua aplicao. Vamos ver como ficaria um
provedor que manipula erros:
1

<?php namespace QuickBill\Providers;

2
3

use App, Illuminate\Support\ServiceProvider;

4
5

class QuickBillErrorProvider extends ServiceProvider {

public function register()


{
//
}

7
8
9
10
11

public function boot()


{
App::error(function(BillingFailedException $e)
{
// Fazer algo com a exceo da falha na cobrana...
});
}

12
13
14
15
16
17
18
19
20

Um pequena soluo
Se voc possui apenas um ou dois manipuladores de erros, claro que a melhor soluo
deix-los nos arquivos start.

Estrutura de Aplicao

29

O Resto
De forma geral, outras classes podem ser facilmente organizadas na pasta da sua aplicao
usando a estrutura PSR-0. Listeners de eventos, manipuladores de erro e outras operaes do
tipo registro podem ser colocados em um provedor de servio. Com o que j aprendemos at
aqui, voc j deve ser capaz de tomar decises sbias sobre onde colocar qualquer tipo de cdigo.
Mas, no hesite em experimentar. A beleza do framework Laravel que voc pode decidir o
que funciona melhor para voc. Descubra a estrutura que melhor se aplica s suas aplicaes. E,
tambm, no deixe de compartilhar o que voc aprender com outros desenvolvedores!
Por exemplo, como voc j deve ter notado acima, voc pode criar um namespace chamado
Providers para todos os provedores da sua aplicao. A estrutura da pasta ficar assim:
1
2
3
4
5
6
7
8
9
10
11

// app
// QuickBill
// Billing
// Extensions
// Pagination
-> Environment.php
// Providers
-> EventPusherServiceProvider.php
// Repositories
User.php
Payment.php

Observe que ns temos um namespace Providers e um outro Extensions. Todos os provedores


de servio da sua aplicao podero ser armazenados na pasta Providers. A pasta Extensions
um lugar conveniente para armazenar extenses de classes do framework feitas por voc.

Arquitetura Aplicada: Desacoplando


os Manipuladores
Introduo
Agora que j discutimos os vrios aspectos da arquitetura de aplicao estvel usando o Laravel
4, vamos ver algo mais especfico. Neste captulo, discutiremos dicas para desacoplar diversos
manipuladores, como o de queue e o de eventos, alm de outras estruturas, como o filtro de
rotas.

No polua a camada de transporte


A maioria dos manipuladores pode ser considerada um componente da camada de
transporte. Em outras palavras, eles so invocados atravs de queue workers, eventos
ou web requests. Trate estes manipuladores como se fossem controladores, evitando
entup-los com detalhes da implementao da sua aplicao.

Desacoplando os Manipuladores
Para comear, vamos direto ao primeiro exemplo. Considere um manipulador de queue que envia
uma mensagem SMS para um determinado usurio. Aps o envio da mensagem, o manipulador
registrar a mensagem para que ns possamos manter um histrico de todas as mensagens SMS
enviadas para aquele usurio. Nosso cdigo ficar assim:

Arquitetura Aplicada: Desacoplando os Manipuladores


1

31

class SendSMS {

public function fire($job, $data)


{
$twilio = new Twilio_SMS($apiKey);

3
4
5
6

$twilio->sendTextMessage(array(
'to' => $data['user']['phone_number'],
'message' => $data['message'],
));

7
8
9
10
11

$user = User::find($data['user']['id']);

12
13

$user->messages()->create([
'to' => $data['user']['phone_number'],
'message' => $data['message'],
]);

14
15
16
17
18

$job->delete();

19

20
21
22

Provavelmente voc notou vrios problemas ao examinar esta classe. Primeiro, ser difcil testla. A classe Twilio_SMS instanciada dentro do mtodo fire, isso significa que no poderemos
injetar um simulador deste servio. Segundo, estamos usando o Eloquent direto no manipulador,
o que exige acessar um banco de dados durante a execuo dos testes desta classe. E por ltimo,
ns no poderemos enviar mensagens SMS fora do queue. Toda a lgica de envio de SMS est
fortemente acoplada ao queue do Laravel.
Ao extrair esta lgica para uma classe de servio separada, ns estaremos desacoplando a lgica
de envio de SMS do queue do Laravel. Isso nos permitir enviar mensagens SMS de qualquer
lugar em nossa aplicao. Enquanto desacoplamos este processo do queue, vamos aproveitar e
torn-lo mais fcil de ser testado.

Arquitetura Aplicada: Desacoplando os Manipuladores

32

Vamos examinar uma alternativa:


1

class User extends Eloquent {

/**
* Envia uma mensagem SMS p/ o usurio
*
* @param SmsCourierInterface $courier
* @param string $message
* @return SmsMessage
*/
public function sendSmsMessage(SmsCourierInterface $courier, $message)
{
$courier->sendMessage($this->phone_number, $message);

3
4
5
6
7
8
9
10
11
12
13

return $this->sms()->create([
'to' => $this->phone_number,
'message' => $message,
]);

14
15
16
17

18
19
20

Neste novo exemplo, ns extramos a lgica de envio de SMS para o modelo User. Tambm injetamos uma implementao da interface SmsCourierInterface no mtodo, o que nos permitir
testar melhor este aspecto do processo. Agora, vamos reescrever o manipulador de queue:
1

class SendSMS {

public function __construct(UserRepository $users,


SmsCourierInterface $courier)
{
$this->users = $users;
$this->courier = $courier;
}

3
4
5
6
7
8
9

public function fire($job, $data)


{
$user = $this->users->find($data['user']['id']);

10
11
12
13

$user->sendSmsMessage($this->courier, $data['message']);

14
15

$job->delete();

16

17
18
19

Arquitetura Aplicada: Desacoplando os Manipuladores

33

Como voc pode ver neste exemplo, nosso manipulador de queue ficou mais leve. Agora ele
serve apenas como uma camada de interpretao entre o queue e a lgica real da sua aplicao.
Isso fantstico! Significa que ns podemos facilmente enviar mensagens SMS fora do contexto
do manipulador. Para concluir, vamos escrever alguns testes para nossa lgica de envio de SMS:
1

class SmsTest extends PHPUnit_Framework_TestCase {

public function testUserCanBeSentSmsMessages()


{
/**
* Preparar...
*/
$user = Mockery::mock('User[sms]');
$relation = Mockery::mock('StdClass');
$courier = Mockery::mock('SmsCourierInterface');

3
4
5
6
7
8
9
10
11

$user->shouldReceive('sms')->once()->andReturn($relation);

12
13

$relation->shouldReceive('create')->once()->with(array(
'to' => '555-555-5555',
'message' => 'Test',
));

14
15
16
17
18

$courier->shouldReceive('sendMessage')->once()->with(
'555-555-5555', 'Test'
);

19
20
21
22

/**
* Ao...
*/
$user->sms_number = '555-555-5555';
$user->sendSmsMessage($courier, 'Test');

23
24
25
26
27

28
29
30

Outros Manipuladores
Ns podemos aperfeioar outros tipos de manipuladores usando esta mesma tcnica. Transformar todos os seus manipuladores em meras camadas de interpretao ajudar a manter o
grosso da sua lgica organizado e desacoplado do resto do framework. Para entendermos isso
melhor, vamos examinar um filtro de rota que verifica se o usurio atual assinante do plano
premium.

Arquitetura Aplicada: Desacoplando os Manipuladores


1
2
3
4

34

Route::filter('premium', function()
{
return Auth::user() && Auth::user()->plan == 'premium';
});

primeira vista, este filtro parece inofensivo. O que poderia dar errado em um filtro to pequeno?
Entretanto, mesmo sendo pequeno, ainda faltam detalhes da implementao da nossa aplicao
neste cdigo. Observe que ns estamos checando manualmente o valor da varivel plan. A
representao dos planos nesta camada esto acopladas nossa camada de transporte (rota).
Se um dia a representao do plano premium for alterada no banco de dados, ou no modelo do
usurio, ns precisaremos reescrever este filtro!
Por isso, vamos fazer uma pequena alterao:
1
2
3
4

Route::filter('premium', function()
{
return Auth::user() && Auth::user()->isPremium();
});

Um mudana pequena como esta traz grandes benefcios a um custo pequeno. Ao permitir
que o modelo decida se um usurio premium ou no, ns removemos todos os detalhes da
implementao do filtro de rota. O filtro no mais o responsvel em determinar se um usurio
est no plano premium ou no, agora ele conta com a ajuda do modelo User. Assim, se a
representao do plano premium mudar no banco de dados, no haver necessidade de atualizar
o filtro de rota!

Quem o responsvel?
Estamos mais uma vez explorando o conceito de responsabilidade. Lembre-se, considere sempre a responsabilidade de cada classe. No deixe as camadas de transporte,
como os manipuladores, serem responsveis pela lgica da sua aplicao.

Extendendo o Framework
Introduo
O Laravel oferece muitos pontos de extenso para que voc possa personalizar os componentes
de seu ncleo, ou mesmo substitu-los completamente. Por exemplo, a classe Hash definido pelo
contrato HasherInterface, que pode ser implementado de acordo com as necessidades da sua
aplicao. Voc tambm pode extender o objeto Request, para ento adicionar os seus prprios
mtodos auxiliares. Voc pode at mesmo adicionar novos drivers de autenticao, cache e
sesso!
Geralmente, os componentes do Laravel podem ser extendidos de duas formas: vinculando uma
nova implementao ao container IoC ou registrando uma extenso com a classe Manager, que
uma implementao do padro de desenvolvimento factory (fbrica). Neste captulo, vamos
explorar as vrias formas de extender o framework e examinar o cdigo necessrio para faz-lo.

Mtodos de extenso
Lembre-se, normalmente, os componentes do Laravel so extendidos de duas formas:
vinculaes IoC e as classes gerenciadoras (manager, em ingls). Estas classes servem
como uma implementao do padro de desenvolvimento factory e responsvel por
instanciar componentes baseados em drivers, tais como: cache e sesso.

Gerenciadores e Fbricas
O Laravel possui muitas classes gerenciadores que administram a criao de componentes
baseados em drivers, que incluem: cache, sesso, autenticao e queue. Esta classe responsvel
por criar uma implementao particular do driver baseado na configurao da aplicao. Por
exemplo, a classe CacheManager pode criar uma implementao do driver de cache APC,
Memcached, Native e muitas outras.
Cada um desses gerenciadores possui um mtodo chamado extend, que facilita a injeo da
resoluo de novas funcionalidades dos drivers no gerenciador. A seguir, ns cobriremos cada
um desses gerenciadores vendo exemplos de como podemos injetar suporte para um driver
personalizado neles.

Aprenda sobre os seus gerenciadores


Separe um tempo para explorar as classes gerenciadoras inclusas no Laravel, como
as CacheManager e SessionManager. Ler estas classes lhe dar um conhecimento mais
profundo de como o framework Laravel funciona. Todas elas extendem a classe base
Illuminate\Support\Manager, que prov funcionalidades comuns muito teis aos
gerenciadores.

36

Extendendo o Framework

Cache
Para extender o cache usado no Laravel, usaremos o mtodo extend no CacheManager, que serve
para vincular ao gerenciador um resolvedor de driver personalizado. Este mtodo est presente
em todas as classes gerenciadoras. Por exemplo, para registrar um novo driver de cache chamado
mongo, faremos o seguinte:
1
2
3
4

Cache::extend('mongo', function($app)
{
// Retornar uma instncia de Illuminate\Cache\Repository...
});

O primeiro argumento passado ao mtodo extend o nome do novo driver. Este o nome que ser
usado na opo driver no arquivo de configurao app/config/cache.php. J o segundo argumento, uma closure que retorna uma instncia de Illuminate\Cache\Repository. A closure receber uma instncia de $app, que por sua vez uma instncia de Illuminate\Foundation\Application
e tambm um container IoC.
Para criar o nosso driver de cache personalizado, ns primeiro precisamos implementar o
contrato Illuminate\Cache\StoreInterface. Nossa implementao de cache para o MongoDB
dever ficar assim:
1

class MongoStore implements Illuminate\Cache\StoreInterface {

public
public
public
public
public
public
public

3
4
5
6
7
8
9

function
function
function
function
function
function
function

get($key) {}
put($key, $value, $minutes) {}
increment($key, $value = 1) {}
decrement($key, $value = 1) {}
forever($key, $value) {}
forget($key) {}
flush() {}

10
11

Agora, s precisamos implementar estes mtodos usando uma conexo com o MongoDB. Quando
esta implementao ficar pronta, poderemos finalizar o registro do nosso driver personalizado:
1

use Illuminate\Cache\Repository;

2
3
4
5
6

Cache::extend('mongo', function($app)
{
return new Repository(new MongoStore);
});

37

Extendendo o Framework

Como voc pode ver acima, possvel usar a classe base Illuminate\Cache\Repository na
criao de drivers de cache personalizados. Normalmente, voc no precisar criar a sua prpria
classe repositria.
Se voc tem dvida sobre onde este cdigo deve ir, considere disponibiliz-lo no Packgist! Ou ento, crie um namespace chamado Extensions na pasta principal da sua aplicao. Por exemplo, se
sua aplicao se chama Snappy, seu cdigo pode ser colocado em app/Snappy/Extensions/MongoStore.php.
Entretanto, tenha em mente que o Laravel no rgido quanto estrutura da sua aplicao, voc
livre para organiz-la conforme suas preferncias.

Onde extender
Sempre que voc estiver em dvida sobre onde colocar um cdigo, considere usar um
provedor de servios. Conforme j discutimos, eles so timos para organizar o cdigo
das suas extenses do framework.

Sesso
Extender o driver de sesso do Laravel to fcil quanto extender seu sistema de cache. Mais
uma vez, usaremos o mtodo extend para registrar nosso cdigo:
1
2
3
4

Session::extend('mongo', function($app)
{
// Retorna uma implementao de SessionHandlerInterface
});

Note que o nosso driver de cache deve implementar o contrato SessionHandlerInterface.


Esta interface faz parte do ncleo do PHP 5.4+. Se voc est usando a verso 5.3 do PHP, esta
interface ser definida automaticamente pelo Laravel. Ela contem apenas alguns mtodos que ns
precisamos implementar. Nossa implementao para o MongoDB ficar mais ou menos assim:
1

class MongoHandler implements SessionHandlerInterface {

public
public
public
public
public
public

3
4
5
6
7
8

function
function
function
function
function
function

open($savePath, $sessionName) {}
close() {}
read($sessionId) {}
write($sessionId, $data) {}
destroy($sessionId) {}
gc($lifetime) {}

9
10

Como estes mtodos no so to claros quanto os da StoreInterface do cache, veremos


rapidamente o que cada um deles faz:
https://packagist.org

Extendendo o Framework

38

O mtodo open normalmente usado em sistemas que armazenam sesses em arquivos.


Como o Laravel j vem como um driver de sesso native que utiliza o armazenamento
em arquivo nativo do PHP, voc quase nunca precisar colocar algo neste mtodo. Deixe-o
em branco. Este apenas um caso de mal planejamento de interface (que discutiremos a
seguir) por parte do PHP.
O mtodo close, assim como o mtodo open, na maioria dos casos poder ser desconsiderado.
O mtodo read deve retornar uma string dos dados da sesso associada com a $sessionId
fornecida. Os dados das sesses no precisam ser serializados nem codificados pelo driver
quando estes forem lidos ou gravados; o Laravel far isso por voc.
O mtodo write deve gravar os dados da string $data associados com a $sessionId em
algum tipo de sistema de armazenamento persistente, como o MongoDB, Dynamo e etc.
O mtodo destroy deve remover os dados associados com a $sessionId do armazenamento persistente.
O mtodo gc deve destruir os dados de todas as sesses anteriores data $lifetime, que
uma UNIX timestamp. Para os sistemas que expiram estes dados automaticamente, como
Memcached e Redis, este mtodo deve ser deixado em branco.
Assim que a implementao do contrato SessionHandlerInterface estiver pronta, poderemos
registr-la com o gerenciador de sesso:
1
2
3
4

Session::extend('mongo', function($app)
{
return new MongoHandler;
});

Feito o registro, o novo driver de sesso mongo estar pronto para ser usado no arquivo de
configurao app/config/session.php.

Compartilhe seu conhecimento


Lembre-se, se um dia voc escrever um driver de sesso personalizado, compartilhe-o
no Packagist!

Autenticao
A autenticao pode ser extendida da mesma forma que os componentes cache e sesso. Por isso,
continuaremos usando o mtodo extend que j estamos acostumados:

Extendendo o Framework
1
2
3
4
5

39

Auth::extend('riak', function($app)
{
// Retorna uma implementao de
// Illuminate\Auth\UserProviderInterface
});

As implementaes da UserProviderInterface so responsveis apenas por obter uma implementao da UserInterface de um sistema de armazenamento persistente, como MySQL, Riak
e etc. Estas duas interfaces permitem que o mecanismo de autenticao do Laravel funcione
independente de onde os dados do usurio esto armazenados ou de qual tipo de classe usada
para represent-lo.
Vamos dar uma espiada na UserProviderInterface:
1

interface UserProviderInterface {

public function retrieveById($identifier);


public function retrieveByCredentials(array $credentials);
public function validateCredentials(UserInterface $user, array $credentials);

3
4
5
6
7

A funo retrieveById tipicamente recebe uma chave numrica que representa o usurio, como
uma ID auto-incrementada do banco de dados MySQL. Este mtodo deve obter e retornar uma
implementao do contrato UserInterface correspondente ID fornecida.
O mtodo retrieveByCredentials recebe o array de credenciais passadas ao mtodo Auth::attempt
durante a tentativa de entrar em sua aplicao. Em seguida, este mtodo deve buscar um
usurio correspondente a tais credenciais no armazenamento persistente. Geralmente, este
mtodo executar uma query com uma condio where em $credentails['username']. Este
mtodo jamais deve tentar validar uma senha ou autenticar um usurio.
O mtodo validateCredentials deve comparar o $user fornecido com as $credentials para
autentic-lo. Por exemplo, este mtodo poderia comparar a string $user->getAuthPassword()
com uma Hash::make de $credentials['password'].
Agora que j exploramos todos os mtodos da UserProviderInterface, vamos dar uma olhada
na UserInterface. Lembre-se, o provedor deve retornar implementaes desta interface a partir
dos mtodos retrieveById e retrieveByCredentials.
1

interface UserInterface {

public function getAuthIdentifier();


public function getAuthPassword();

3
4
5
6

Extendendo o Framework

40

Esta interface simples. O mtodo getAuthIdentifier deve retornar a chave primria


do usurio. No caso do MySQL, esta seria a primary key auto-incrementada. J o mtodo
getAuthPassword deve retornar o hash da senha do usurio. Esta interface permite que o
sistema de autenticao funcione com qualquer classe User, independente do ORM ou sistema
de armazenamento escolhido. Por padro, o Laravel inclui uma classe User que implementa esta
interface na pasta app/models; use-a como um exemplo de implementao.
Para finalizar, quando implementarmos o contrato UserProviderInterface, nossa extenso
estar pronta para ser registrada com a faade Auth:
1
2
3
4

Auth::extend('riak', function($app)
{
return new RiakUserProvider($app['riak.connection']);
});

Tendo registrado o driver com o mtodo extend, voc poder escolh-lo no seu arquivo de
configurao app/config/auth.php usando a chave riak.

Extenses Baseadas no Container IoC


Quase todos os provedores de servios inclusos no Laravel vinculam objetos ao container
IoC. A lista dos provedores de servios da sua aplicao pode ser encontrada no arquivo
app/config/app.php. Tendo um tempo, seria bom que voc analisasse o cdigo-fonte de cada
provedor nesta lista. Fazer isso lhe ajudar a entender melhor o que cada provedor acrescenta ao
framework e quais chaves (nomes) so usadas para vincular vrios servios ao container IoC.
Por exemplo, o PaginationServiceProvider vincula a chave paginator ao container IoC,
que resolve em uma instncia de Illuminate\Pagination\Environment. Substituindo esta
vinculao, voc poder extender ou substituir esta classe facilmente. Por exemplo, vamos criar
uma classe que extende a classe base Environment:
1

namespace Snappy\Extensions\Pagination;

2
3

class Environment extends \Illuminate\Pagination\Environment {

//

5
6
7

Agora, voc j pode criar um novo provedor de servio SnappyPaginationProvider para


subtituir o paginador do Laravel usando o mtodo boot:

Extendendo o Framework
1

41

class SnappyPaginationProvider extends PaginationServiceProvider {

public function boot()


{
App::bind('paginator', function()
{
return new Snappy\Extensions\Pagination\Environment;
});

3
4
5
6
7
8
9

parent::boot();

10

11
12
13

Note que esta classe extende o provedor PaginationServiceProvider e no a classe base padro
ServiceProvider. Feito isto, substitua o provedor de servio PaginationServiceProvider por
sua nova extenso no arquivo de configurao app/config/app.php.
Esta a forma geral de extender qualquer classe do ncleo que est vinculada ao container.
Todas as classes do ncleo esto vinculadas ao container desta forma e podem ser facilmente
substitudas. Novamente, leia o cdigo dos provedores de servio inclusos no framework para
aprender onde as vrias classes so vinculadas ao container e quais chaves so usadas. Esta
uma excelente forma de aprender como as vrias partes do Laravel so unidas.

Request
A classe Request uma pea fundamental do framework e instanciada muito cedo no ciclo da
sua aplicao. Por isso, ela no extendida como os exemplos anteriores.
Primeiro, extenda a classe normalmente:
1

<?php namespace QuickBill\Extensions;

2
3

class Request extends \Illuminate\Http\Request {

// Personalizao e mtodos auxiliares vo aqui...

5
6
7

Agora, abra o arquivo bootstrap/start.php. Este um dos primeiros arquivos includos na sua
aplicao. Note que a primeira ao neste arquivo a criao de uma instncia da sua aplicao:
1

$app = new \Illuminate\Foundation\Application;

Extendendo o Framework

42

Quando esta nova instncia $app for criada, uma nova instncia de Illuminate\Http\Request
tambm ser criada e vinculada ao container IoC usando a chave request. Assim, ns precisamos
encontrar uma forma de especificar qual classe deve ser usada como tipo de request padro,
certo? Felizmente, o mtodo requestClass na instncia da sua aplicao faz exatamente isso! S
precisamos acrescentar esta linha no comeo do arquivo bootstrap/start.php:
1

use Illuminate\Foundation\Application;

2
3

Application::requestClass('QuickBill\Extensions\Request');

Agora, o Laravel sempre usar a classe personalizada que voc especificou para criar uma
instncia de Request. Assim, voc sempre ter uma instncia dessa classe sua disposio, at
mesmo nos testes de unidade!

Princpio da Responsabilidade nica


Introduo
Os princpios de desenvolvimento SOLID, articulados por Robert Uncle Bob Martin, so cinco
princpios que fornecem um alicerce firme para o desenvolvimento de aplicaes. So eles:

Princpio da Responsabilidade nica


Princpio do Aberto/Fechado
Princpio de Substituio de Liskov
Princpio da Segregao de Interface
Princpio da Inverso de Dependncia

Analisaremos a fundo todos estes princpios e veremos alguns exemplos de cdigo ilustrando
cada um deles. Logo voc descobrir que cada princpio completa os demais. Assim, se um deles
falhar, a maioria ou todos os demais tambm falharo.

Em Ao
O Princpio da Responsabilidade nica diz que uma classe deve mudar por um, e apenas um,
motivo. Em outras palavras, o escopo e responsabilidade da classe devem ser estritamente
focados. Como eu j disse, ignorncia uma bno em se tratando das responsabilidades de uma
classe. Ela deve executar o seu trabalho sem ser afetada pelas mudanas em suas dependncias.
Considere a seguinte classe:

Princpio da Responsabilidade nica


1

44

class OrderProcessor { // Processador de pedidos

public function __construct(BillerInterface $biller)


{
$this->biller = $biller;
}

3
4
5
6
7

public function process(Order $order)


{
$recent = $this->getRecentOrderCount($order);

8
9
10
11

if ($recent > 0)
{
throw new Exception('Pedido provavelmente duplicado.');
}

12
13
14
15
16

$this->biller->bill($order->account->id, $order->amount);

17
18

DB::table('orders')->insert(array(
'account'
=> $order->account->id,
'amount'
=> $order->amount;
'created_at' => Carbon::now();
));

19
20
21
22
23

24
25

protected function getRecentOrderCount(Order $order)


{
$timestamp = Carbon::now()->subMinutes(5);

26
27
28
29

return DB::table('orders')
->where('account', $order->account->id)
->where('created_at', '>=', $timestamps)
->count();

30
31
32
33

34
35
36

Quais so as responsabilidades da classe acima? Obviamente, seu nome implica que sua responsabilidade processar pedidos. Porm, analisando o mtodo getRecentOrderCount, percebemos
que ele examina o histrico de pedidos de uma conta no banco de dados para detectar se h
pedidos duplicados. Com estas responsabilidades extras, quando o armazenamento de dados ou
as regras de validao mudarem, ns precisaremos alterar esta classe tambm.
Estas responsabilidades extras devem ser extradas para uma outra classe, pode ser a classe
OrderRepository:

Princpio da Responsabilidade nica


1

45

class OrderRepository { // Repositrio de pedidos

public function getRecentOrderCount(Account $account)


{
$timestamp = Carbon::now()->subMinutes(5);

3
4
5
6

return DB::table('orders')
->where('account', $account->id)
->where('created_at', '>=', $timestamp)
->count();

7
8
9
10

11
12

public function logOrder(Order $order)


{
DB::table('orders')->insert(array(
'account'
=> $order->account->id,
'amount'
=> $order->amount;
'created_at' => Carbon::now();
));
}

13
14
15
16
17
18
19
20
21
22

Agora, ns podemos injetar nosso repositrio no OrderProcessor, aliviando assim a sua


responsabilidade de analisar o histrico de pedidos:

Princpio da Responsabilidade nica


1

46

class OrderProcessor { // Processador de pedidos

public function __construct(BillerInterface $biller,


OrderRepository $orders)
{
$this->biller = $biller;
$this->orders = $orders;
}

3
4
5
6
7
8
9

public function process(Order $order)


{
$recent = $this->orders->getRecentOrderCount($order->account);

10
11
12
13

if ($recent > 0)
{
throw new Exception('Pedido provavelmente duplicado.');
}

14
15
16
17
18

$this->biller->bill($order->account->id, $order->amount);

19
20

$this->orders->logOrder($order);

21

22
23
24

Tendo abstrado a responsabilidade de reunir os dados dos pedidos, no precisaremos mais alterar
nossa classe OrderProcessor quando os mtodos para obter e registrar pedidos mudarem. As
responsabilidades da nossa classe foram focadas e definidas, tornando nossa aplicao mais fcil
de manter e seu cdigo mais limpo e expressivo.
Tenha em mente que, aplicar o Princpio da Responsabilidade nica no apenas escrever
menos linhas de cdigo. escrever classes que possuem uma responsabilidade estrita e coesa
com os mtodos nela contidos. Certifique-se de que todos os mtodos esto alinhados com a
responsabilidade da classe. Aps criarmos uma biblioteca de classes limpas e pequenas, cada uma
tendo responsabilidades bem definidas, nosso cdigo estar desacoplado e poder ser testado e
modificado com mais facilidade.

Princpio do Aberto/Fechado
Introduo
Durante a vida de uma aplicao, mais tempo gasto acrescentando ao cdigo existente do que
adicionando novos recursos escritos do zero. E como voc j deve ter notado, isso pode ser um
processo entediante. Sempre que o cdigo modificado, voc corre o risco de introduzir novos
bugs ou quebrar funcionalidades completas. O ideal seria poder modificar um projeto to rpido
e fcil quanto comear do zero. Isso possvel se desenvolvermos nossa aplicao de acordo como
o princpio do aberto/fechado!

Definio
O Princpio do Aberto/Fechado diz que o cdigo deve estar aberto para extenso, mas
fechado para modificao.

Em Ao
Para demonstrar este princpio, continuaremos trabalhando com a classe OrderProcessor do
captulo anterior. Considere o mtodo process a seguir:
1

$recent = $this->orders->getRecentOrderCount($order->account);

2
3
4
5
6

if ($recent > 0)
{
throw new Exception('Pedido provavelmente duplicado.');
}

Este cdigo bem legvel e testvel, pois estamos injetando a dependncia. Entretanto, e se as
regras da validao dos pedidos mudarem? E se a nossa aplicao crescer a ponto de termos
muitas regras novas? O mtodo process crescer rapidamente e se tornar um monstro de
cdigo espaguete quase impossvel de ser mantido. Tudo isso porque este cdigo precisar ser
modificado sempre que uma nova regra de validao for adicionada. Ou seja, ele viola o princpio
do aberto/fechado, pois est aberto para modificao. Lembre-se, nosso queremos que o nosso
cdigo esteja aberto para extenso, no para modificao.
Ao invs de validar os pedidos no mtodo process, vamos definir uma nova interface OrderValidator:

Princpio do Aberto/Fechado
1
2
3

48

interface OrderValidatorInterface {
public function validate(Order $order);
}

Em seguida, vamos definir uma implementao para previnir a duplicidade de pedidos:


1

class RecentOrderValidator implements OrderValidatorInterface {

public function __construct(OrderRepository $orders)


{
$this->orders = $orders;
}

3
4
5
6
7

public function validate(Order $order)


{
$recent = $this->orders->getRecentOrderCount($order->account);

8
9
10
11

if ($recent > 0)
{
throw new Exception('Pedido provavelmente duplicado.');
}

12
13
14
15

16
17
18

timo! Agora ns temos um mtodo pequeno, testvel e encapsulado. Vamos escrever outra
implentao para verificar se a conta est suspensa:
1

class SuspendedAccountValidator implememts OrderValidatorInterface {

public function validate(Order $order)


{
if ($order->account->isSuspended())
{
throw new Exception("Contas suspensas no podem realizar pedidos.")
}
}

3
4
5
6
7
8
9
10
11

Agora que temos duas implementaes diferentes da nossa OrderValidatorInterface, vamos


us-la na classe OrderProcessor. Vamos injetar um array de validadores na instncia do
processador, assim poderemos acrescentar ou remover regras de validao conforme o nosso
cdigo for crescendo.

Princpio do Aberto/Fechado
1

49

class OrderProcessor {

public function __construct(BillerInterface $biller,


OrderRepository $orders,
array $validators = array())
{
$this->biller = $biller;
$this->orders = $orders;
$this->validators = $validators;
}

3
4
5
6
7
8
9
10
11
12

Em seguida, usando um loop, poderemos validar todas as regras dentro do mtodo process:
1
2
3
4
5
6

public function process(Order $order)


{
foreach ($this->validators as $validator)
{
$validator->validate($order);
}

// Processar pedido vlido...

8
9

Finalizando, vamos registrar a classe OrderProcessor no container IoC da aplicao:


1
2
3
4
5
6
7
8
9
10
11

App::bind('OrderProcessor', function()
{
return new OrderProcessor(
App::make('BillerInterface'),
App::make('OrderRepository'),
array(
App::make('RecentOrderValidator'),
App::make('SuspendedAccountValidator'),
),
);
});

Com estas pequenas modificaes, agora ns podemos acrescentar e remover novas regras de
validao sem mudar uma nica linha do cdigo existente. Cada regra nova ser simplesmente
uma implementao da interface OrderValidatorInterface e ser registrada no container IoC.
Ao invs de testarmos um mtodo process gigantesco, poderemos testar cada regra de validao
isoladamente. Nosso cdigo est aberto para extenso, mas fechado para modificao.

Princpio do Aberto/Fechado

50

Vazamentos em abstraes
Fique de olho nas dependncias que vazam detalhes da implementao. Uma mudana
de implementao numa dependncia no deve exigir mudanas em seus consumidores. Quando necessrio mudar o consumidor, ns dizemos que a dependncia
est vazando detalhes da implementao. Se isso acontecer em suas abstraes,
provavelmente voc est violando o Princpio do Aberto/Fechado.

Antes de avanarmos, lembre-se que estes princpios no so leis. No obrigatrio que cada
parte da sua aplicao seja plugvel. Uma aplicao pequena que acessa um banco de dados
MySQL para obter alguns registros no precisa aplicar todos os princpios de desenvolvimentos
imaginveis. No aplique estes princpios cegamente apenas para desencargo de conscincia,
pois em faz-lo voc estaria planejado excessivamente e criando um sistema complicado. Tenha
em mente que estes princpios foram criados para resolverem problemas estruturais comuns em
aplicaes grandes e robustas. Tendo dito isto, no use este pargrafo como uma desculpa para
sua preguia!

Princpio da Substituio de Liskov


Introduo
No se preocupe, o Princpio da Substituio de Liskov mais fcil de entender do que parece.
Ele afirma que voc deve poder usar qualquer implementao de uma abstrao onde quer que
a abstrao seja aceita. Simplificando, este princpio diz que: se uma classe usa a implementao
de uma interface, ele deve aceitar qualquer outra implementao dessa interface sem precisar de
qualquer modificao.

Definio
Objetos devem ser substituveis por instncias dos seus subtipos sem alterar o funcionamento do programa.

Em Ao
Para ilustrar este princpio, continuaremos usando como exemplo a classe OrderProcessor do
captulo anterior. D uma olhada neste mtodo:
1
2
3

public function process(Order $order)


{
// Validar pedido...

$this->orders->logOrder($order);

5
6

Observe que aps a validao do pedido, ns registramos o pedido usando a implementao


OrderRepositoryInterface. Vamos supor que no princpio, todos os nossos pedidos eram
processados e armazenados no formato CSV em um arquivo. A nica implementao de
OrderRepositoryInterface era CsvOrderRepository. Agora, como o nmero de pedidos cresceu, queremos usar um banco de dados relacional para armazen-los. Assim, vamos analisar uma
possvel implementao para o nosso novo repositrio:

Princpio da Substituio de Liskov


1

52

class DatabaseOrderRepository implements OrderRepositoryInterface {

protected $connection;

3
4

public function connect($username, $password)


{
$this->connection = new DatabaseConnection($username, $password);
}

5
6
7
8
9

public function logOrder(Order $order)


{
$this->connection->run('insert into orders values (?, ?)', array(
$order->id, $order->amount,
));
}

10
11
12
13
14
15
16
17

Agora, vamos examinar como podemos usar esta implementao:


1
2
3

public function process(Order $order)


{
// Validar pedido...

if ($this->repository instanceof DatabaseOrderRepository)


{
$this->repository->connect('root', 'password');
}

5
6
7
8
9

$this->respository->logOrder($order);

10
11

Note que fomos obrigados a verificar que a OrderRepositoryInterface uma implementao de um banco de dados na classe que processa os pedidos. E em sendo, a conexo
realizada. Isso no ser um problema em aplicaes menores, mas imagine se a interface
OrderRepositoryInterface for usada em vrias classes? Esse mesmo cdigo seria escrito
repetidas vezes e mant-lo nos daria uma grande dor de cabea, alm de poder criar bugs. E se
esquecermos de atualiz-lo em uma nica classe, nossa aplicao poder quebrar completamente.
O exemplo acima claramente no aplica o Princpio da Substituio de Liskov, pois no pudemos
injetar uma implementao da nossa interface sem mudar tambm a classe que invoca o mtodo
connect. Assim, tendo identificado o problema, agora ns vamos solucion-lo. Veja a nossa nova
implementao DatabaseOrderRepository

Princpio da Substituio de Liskov


1

53

class DatabaseOrderRepository implements OrderRepositoryInterface {

protected $connector;

3
4

public function __construct(DatabaseConnector $connector)


{
$this->connector = $connector;
}

5
6
7
8
9

public function connect()


{
return $this->connector->bootConnection();
}

10
11
12
13
14

public function logOrder(Order $order)


{
$connection = $this->connect();

15
16
17
18

$connection->run('insert into orders values (?, ?)', array(


$order->id, $order->amount,
));

19
20
21

22
23
24

Agora, ns podemos remover nosso cdigo de inicializao da classe consumidora e o repositrio


DatabaseOrderRepository gerenciar a conexo com o banco de dados:
1
2
3

public function process(Order $order)


{
// Validar pedido...

$this->respository->logOrder($order);

5
6

Com esta modificao, agora ns podemos usar os repositrios CsvOrderRepository ou DatabaseOrderRepositor


sem modificar a classe consumidora OrderProcessor. Nosso cdigo est de acordo com o
Princpio da Substituio de Liskov! Voc notou que muitos dos conceitos de arquitetura que
discutimos esto relacionados com o conhecimento? Especificamente, o conhecimento que uma
classe possui dos seus arredores, tais como cdigos perifricos e dependncias que ajudam uma
classe em sua tarefa. Na sua jornada rumo a uma aplicao com arquitetura robusta, limitar o
conhecimento de uma classe ser um tema importante e recorrente.
Note tambm a consequncia da violao deste princpio no que diz respeito ao demais princpios
j vistos. Ao quebrar este princpio, o Princpio do Aberto/Fechado tambm violado, pois ao

Princpio da Substituio de Liskov

54

verificar as instncias de vrias classes filhas, a classe consumidora precisar ser alterada sempre
que houver uma nova classe filha.

Cuidado com os vazamentos


Voc deve ter notado que este princpios est intimamente relacionado com a preveno
dos vazamentos de abstraes discutidos no captulo anterior. O vazamento da
abstrao no repositrio do banco de dados foi a primeira indicao de que o Princpio
da Substituio de Liskov estava sendo violado. Fique atento com estes vazamentos!

Princpio da Segregao de Interface


Introduo
O Princpio da Segregao de Interface afirma que nenhuma implementao de uma interface
deve ser forada a depender dos mtodos no usados por ela. Voc j precisou implementar
mtodos de uma interface que voc acabou no usando? Se sim, voc provavelmente criou
mtodos em branco em sua implementao. Este um exemplo de ser forado a usar uma
interface que viola o princpio deste captulo.
Em termos prticos, este princpio exige que as interfaces sejam granulares e focadas. Soa
familiar? Lembre-se, todos os cinco princpios SOLID esto to relacionados que, ao violar um,
geralmente voc estar violando os demais tambm. Para violar o Princpio da Segregao de
Interface, voc tambm precisa violar o Princpio da Responsabilidade nica.
Ao invs de uma interface gorda contendo mtodos que no sero usados por todas as
implementaes, melhor termos vrias interfaces menores que podem ser implementadas
individualmente conforme necessrio. Tendo contratos focados e menores, o cdigo consumidor
poder depender em interfaces menores sem depender das partes da aplicao que elas no usam.

Definio
Este princpio afirma que nenhuma implementao de uma interface deve ser forada
a depender dos mtodos no usados por ela.

Em Ao
Para ilustrar este princpio, vamos considerar uma biblioteca para manipular sesses. Na verdade,
vamos usar a interface SessionHandlerInterface do prprio PHP. Estes so os mtodos
definidos por esta interface, inclusos no PHP na verso 5.4:
1
2
3
4
5
6
7
8

interface SessionHandlerInterface {
public function close();
public function destroy($sessionId);
public function gc($maxLiftetime);
public function open($savePath, $name);
public function read($sessionId);
public function write($sessionId, $sessionData);
}

Princpio da Segregao de Interface

56

Agora que voc est familiarizado com os mtodos desta interface, considere uma implementao
usando Memcached. A implementao Memcached desta interface precisar definir funcionalidades para cada um destes mtodos? No! Na realidade, no precisaremos implementar nem
metade deles!
Como o Memcached expirar automaticamente os valores armazenados nele, no precisaremos
implementar o mtodo gc nesta interface, bem como os mtodos open e close. Assim, somos
forados a definir mtodos em branco para estes mtodos em nossa implementao. Para corrigir
este problema, vamos comear definindo uma interface menor e mais focada para coletar o lixo
(garbage collector ou GC, em ingls) das sesses:
1
2
3

interface GarbageCollectorInterface {
public function gc($maxLifetime);
}

Com uma interface menor, qualquer cdigo consumidor poder depender neste contrato conciso,
pois ele define um conjunto de funes pequeno e no cria uma dependncia completa no
manipulador de sesses.
Para compreendermos este princpio melhor, vamos consolidar nosso conhecimento com outro
exemplo. Imagine uma classe Eloquent Contact definida desta forma:
1

class Contact extends Eloquent {

public function getNameAttribute()


{
return $this->attributes['name'];
}

3
4
5
6
7

public function getEmailAttribute()


{
return $this->attributes['email'];
}

8
9
10
11
12
13

Agora, vamos assumir que nossa aplicao tambm emprega uma classe PasswordReminder que
responsvel pelo envio de lembretes de senha por e-mail. Abaixo temos uma possvel definio
para esta classe:

Princpio da Segregao de Interface


1

57

class PasswordReminder {

public function remind(Contact $contact, $view)


{
// Enviar o lembrete de senha por e-mail...
}

3
4
5
6
7
8

Como voc deve ter notado, nossa classe PasswordReminder depende da classe Contact, que
por sua vez depende do ORM Eloquent. No desejvel nem foi necessrio acoplar o sistema
de lembrete de senhas a uma implementao especfica do ORM Eloquent. Ao quebrar esta
dependncia, ns podemos alterar livremente o mecanismo de armazenamento no back-end
ou o ORM sem afetar este componente da nossa aplicao. Novamente, ao violarmos um dos
princpios SOLID, estamos dando classe consumidora muito conhecimento sobre o restante da
aplicao.
Para quebrar esta dependncia, vamos criar a interface RemindableInterface. Na verdade, tal
interface est inclusa no Laravel e por padro implementada pelo modelo User:
1
2
3

interface RemindableInterface {
public function getReminderEmail();
}

Quando a interface estiver pronta, poderemos implement-la no nosso modelo:


1

class Contact extends Eloquent implements RemindableInterface {

public function getReminderEmail()


{
return $this->email;
}

3
4
5
6
7
8

Para finalizar, agora ns podemos depender desta interface na classe PasswordReminder:

Princpio da Segregao de Interface


1

58

class PasswordReminder {

public function remind(RemindableInterface $remindable, $view)


{
// Enviar o lembrete de senha por e-mail...
}

3
4
5
6
7
8

Fazendo esta pequena alterao, ns removemos qualquer dependncia desnecessria do componente de lembrete de senhas e ele tornou-se flexvel o bastante para usar qualquer classe de
qualquer ORM, desde que tal classe implemente a nova RemindableInterface. exatamente
assim que o componente de lembrete de senhas do Laravel aceita qualquer banco de dados e
ORM!

Saber poder
Mais uma vez, discutimos as armadilhas de dar classe muito conhecimento sobre
os detalhes da implementao da aplicao. Se prestarmos bastante cuidado com a
quantidade de conhecimento passado para uma classe, conseguiremos aplicar todos
os princpios SOLID.

Princpio da Inverso de
Dependncia
Introduo
Chegamos ao destino final de nosso estudo sobre os cinco princpios de desenvolvimento SOLID!
O ltimo princpio o Princpio da Inverso de Dependncia; ele afirma que: o cdigo de alto
nvel no deve depender do cdigo de baixo nvel. Pelo contrrio, um cdigo de alto nvel deve
depender de uma camada de abstrao, que funciona como um mediador entre o cdigo de alto
e o de baixo nvel. Um segundo aspecto deste princpio que as abstraes no devem depender
dos detalhes, mas os detalhes devem depender das abstraes. Se tudo isso parece confuso agora,
no se preocupe, veremos sobre ambos os aspectos logo a seguir.

Definio
Este princpio afirma que o cdigo de alto nvel no deve depender do cdigo de baixo
nvel e que as abstraes no devem depender dos detalhes.

Em Ao
Se voc j leu os captulos anteriores deste livro, voc j compreendeu o Princpio da Inverso
de Dependncia! Para ilustr-lo, vamos considerar a seguinte classe:

Princpio da Inverso de Dependncia


1

60

class Authenticator {

public function __construct(DatabaseConnection $db)


{
$this->db = $db;
}

3
4
5
6
7

public function findUser($id)


{
return $this->db->exec('select * from users where id = ?', array($id));
}

8
9
10
11
12

public function authenticate($credentials)


{
// Autenticao do usurio...
}

13
14
15
16
17
18

Como voc deve ter adivinhado, a classe Authenticator responsvel por encontrar e autenticar
nossos usurios. Vamos examinar o construtor desta classe. Veja que estamos induzindo o tipo
de uma instncia do DatabaseConnection. Fazendo assim, estamos acoplando o autenticador ao
banco de dados, em outras palavras, estamos dizendo que os usrios sempre estaro registrados
em um banco de dados SQL. Alm do mais, o cdigo de alto nvel (o Authenticator) diretamente
depende do cdigo de baixo nvel (o DatabaseConnection).
Em primeiro lugar, vamos discutir cdigo de alto e de baixo nvel. O cdigo de baixo nvel
implementa operaes bsicas como: ler arquivos no disco ou interagir com o banco de dados. J
o cdigo de alto nvel, encapsula lgicas complexas e precisa do cdigo de baixo nvel para poder
funcionar, mas no devem estar diretamente acoplado um ao outro. Pelo contrrio, o cdigo
de alto nvel deve depender de uma abstrao entre ele e o cdigo de baixo nvel, como uma
interface. E no apenas isso, o cdigo de baixo nvel tambm deve depender de uma abstrao.
Por isso, vamos escrever uma interface para usarmos dentro do nosso Authenticator:
1
2
3
4

interface UserProviderInterface {
public function find($id);
public function findByUsername($username);
}

Agora, vamos injetar uma implementao desta interface na classe Authenticator:

Princpio da Inverso de Dependncia


1

61

class Authenticator {

public function __construct(UserProviderInterface $users,


HasherInterface $hash)
{
$this->hash = $hash;
$this->users = $users;
}

3
4
5
6
7
8
9

public function findUser($id)


{
return $this->users->find($id);
}

10
11
12
13
14

public function authenticate($credentials)


{
$user = $this->users->findByUsername($credentials['username']);

15
16
17
18

return $this->hash->make($credentials['password']) == $user->password;

19

20
21
22

Aps estas mudanas, nosso Authenticator passou depender de duas abstraes: UserProviderInterface
e HasherInterface. Estamos livres para injetar qualquer implementao destas interfaces no
Authenticator. Por exemplo, se os nossos usurios forem armazenados no Redis, poderemos
escrever um provedor RedisUserProvider que implementa o contrato UserProvider. Agora, o
nosso Authenticator no depende diretamento no sistema de armazenamento de baixo nvel.
E ainda mais, o nosso cdigo de baixo nvel agora depende da abstrao de alto nvel UserProviderInterface,
pois ele implementa a interface tambm:

Princpio da Inverso de Dependncia


1

62

class RedisUserProvider implements UserProviderInterface {

public function __construct(RedisConnection $redis)


{
$this->redis = $redis;
}

3
4
5
6
7

public function find($id)


{
$this->redis->get('users:'.$id);
}

8
9
10
11
12

public function findByUsername($username)


{
$id = $this->redis->get('user:id:'.$username);

13
14
15
16

return $this->find($id);

17

18
19
20

Pensamento invertido
Aplicar este princpio inverte o modo como muitos desenvolvedores desenvolvem suas
aplicaes. Ao invs de acoplar o cdigo de alto nvel diretamente a um cdigo de baixo
nvel, no estilo de cima para baixo, este princpio afirma que ambos os cdigos de alto
e de baixo nvel devem depender de uma abstrao de alto nvel.

Antes de invertermos as dependncias do nosso Authenticator, ele s poderia ser usado


com um banco de dados relacional. Se mudssemos algo no sistema de armazenamento, o
Authenticator precisaria ser modificado tambm, violando assim o Princpio do Aberto/Fechado. Novamente, mltiplos princpios andam lado-a-lado e caem juntos.
Ao forarmos o Authenticator a depender de uma abstrao e no da camada de armazenamento, ela passou a aceitar qualquer sistema de armazenamento que implemente o contrato
UserProviderInterface sem precisarmos modificar a classe Authenticator. A corrente convencional de dependncia foi invertida e o nosso cdigo tornou-se muito mais flexvel e acolhedor
de mudanas!

Você também pode gostar