() translation by (you can also view the original English article)
Nos últimos anos, Laravel tornou-se uma das frameworks mais importantes e utilizadas por engenheiros de software na hora de construir suas aplicações web. Com popularidade semelhante à do CodeIgniter em seus dias de glória, Laravel tem sido louvado pela sua simplicidade, facilidade de uso por parte dos iniciantes e aderência aos padrões da indústria.
Introdução
Algo que muitos programadores deixam de aproveitar do Laravel é a sua componentização. Desde sua conversão à utilização de componentes que lançam utilizam o Composer, Laravel 4 tornou-se um sistema bastante modular, semelhante às frameworks mais maduras, como a Symfony. Ela usa o grupo de componentes chamado de Illuminate
, que, na minha opinião, não é a framework em si, mas uma compilação de bibliotecas que uma framework pode chegar a usar. A framework Laravel, de verdade, é representada pela aplicação base do Laravel (encontrada no repositório do GitHub chamado laravel/laravel
), que faz uso desses componente para construir uma aplicação web.
Nesse tutorial, nós nos aprofundaremos em alguns dos componentes desse grupo, aprendendo como eles funcionam, como eles são utilizados pela framework e como estender a funcionalidade deles.
O Componente Session
O componente Session do Laravel lida com as sessões das aplicações web. Ele faz uso de um sistema de drivers chamado Laravel Manager, que age tanto como uma fábrica (do padrão de programação Factory) quanto um envólucro para qualquer driver atribuído no arquivo de configuração. Até o momento da escrita desse post, o componente Session tem os seguintes drivers:
-
file
- driver baseado em arquivo, onde os dados da sessão são salvos em um arquivo criptografado. -
cookie
- driver baseados em cookies, onde os dados da sessão são salvos de forma criptografada nos cookies (do navegador) do usuário. -
database
- os dados da sessão são salvos na base de dados configurada para a aplicação. -
apc
- os dados da sessão são salvos no APC (Alternative PHP Cache). -
memcached
- os dados da sessão são salvas no Memcached. -
redis
- os dados da sessão são salvo no Redis. -
array
- os dados da sessão são salvos em uma array do PHP. Atente que o driver de sessão baseado em array não dá suporte a persistência de dados e, geralmente, é usado somente em linha de comando.
Provedores de Serviço
A maioria dos usuários de Laravel não percebem mas, grande parte de como o Laravel funciona se deve aos seus provedores de serviço. Os provedores de serviços são, em essência, arquivos inicializadores de cada componente e eles são tão abstraídos que os usuários podem utilizá-los para inicializar qualquer componente, de qualquer forma.
Uma explicação grosseira de como isso funciona segue abaixo:
- O componente Application do Laravel é inicializado. Esse é o driver principal de toda a framework, responsável por lidar com as Requisições HTTP, executar os provedores de serviços, e agir como recipiente de dependências para a framework.
- Uma vez que um provedor de serviço é executado, o método
register
dele é chamado. Isso permite instanciar qualquer componente que quisermos.- Tenha em mente que todos os provedores de serviço tem acesso à aplicação principal do Laravel (via
$this->app
), o que permite que os provedores de serviços tenham acesso a instâncias de classes criadas de dentro do recipiente de depedência.
- Tenha em mente que todos os provedores de serviço tem acesso à aplicação principal do Laravel (via
- Uma vez que essas dependências são carregadas, nós estamos livres para chamar o recipiente, por exemplo, pelo sistema Facade do Laravel, através do
App::make
.
Voltando ao componente Sessions, daremos uma rápida olhada no SessionServiceProivider
:
1 |
/**
|
2 |
* Register the session manager instance.
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
protected function registerSessionManager() |
7 |
{
|
8 |
$this->app->bindShared('session', function($app) |
9 |
{
|
10 |
return new SessionManager($app); |
11 |
});
|
12 |
}
|
13 |
|
14 |
/**
|
15 |
* Register the session driver instance.
|
16 |
*
|
17 |
* @return void
|
18 |
*/
|
19 |
protected function registerSessionDriver() |
20 |
{
|
21 |
$this->app->bindShared('session.store', function($app) |
22 |
{
|
23 |
// First, we will create the session manager which is responsible for the
|
24 |
// creation of the various session drivers when they are needed by the
|
25 |
// application instance, and will resolve them on a lazy load basis.
|
26 |
$manager = $app['session']; |
27 |
|
28 |
return $manager->driver(); |
29 |
});
|
30 |
}
|
Esses dois métodos são chamados pela função register()
. O primeiro, registerSessionManager()
, é chamado para registrar inicialmente o SessionManager
. Essa classe estende a classe Manager
que mencionei mais acima. O segundo, registerSessionDriver()
, registrar um manipulador de registros para o manager, baseado nas configurações que instituimos no início. Isso, eventualmente, invoca esse método da classe Illuminate\Support\Manager
:
1 |
/**
|
2 |
* Create a new driver instance.
|
3 |
*
|
4 |
* @param string $driver
|
5 |
* @return mixed
|
6 |
*
|
7 |
* @throws \InvalidArgumentException
|
8 |
*/
|
9 |
protected function createDriver($driver) |
10 |
{
|
11 |
$method = 'create'.ucfirst($driver).'Driver'; |
12 |
|
13 |
// We'll check to see if a creator method exists for the given driver. If not we
|
14 |
// will check for a custom driver creator, which allows developers to create
|
15 |
// drivers using their own customized driver creator Closure to create it.
|
16 |
if (isset($this->customCreators[$driver])) |
17 |
{
|
18 |
return $this->callCustomCreator($driver); |
19 |
}
|
20 |
elseif (method_exists($this, $method)) |
21 |
{
|
22 |
return $this->$method(); |
23 |
}
|
24 |
|
25 |
throw new \InvalidArgumentException("Driver [$driver] not supported."); |
26 |
}
|
Aqui, podemos ver que, baseado no nome do driver, escolhido no arquivo de configuração, um método específico é chamado. Então, se tivermos configurados o sistema para usar o manipulador de sessão do tipo file
, ele invocará esse método na classe SessionManager
:
1 |
/**
|
2 |
* Create an instance of the file session driver.
|
3 |
*
|
4 |
* @return \Illuminate\Session\Store
|
5 |
*/
|
6 |
protected function createFileDriver() |
7 |
{
|
8 |
return $this->createNativeDriver(); |
9 |
}
|
10 |
|
11 |
/**
|
12 |
* Create an instance of the file session driver.
|
13 |
*
|
14 |
* @return \Illuminate\Session\Store
|
15 |
*/
|
16 |
protected function createNativeDriver() |
17 |
{
|
18 |
$path = $this->app['config']['session.files']; |
19 |
|
20 |
return $this->buildSession(new FileSessionHandler($this->app['files'], $path)); |
21 |
}
|
A classe correta do driver é, então, injetada na classe Store
, que é responsável por invocar os métodos verdadeiros para manipulação da sessão. Isso permite que separemos a implementação da classe SessionHandlerInterface
da SPL (Standard PHP Library), a classe Store
facilita isso.
Criando Nossa Própria Manipuladora de Sessão
Vamos criar nossa própria classe manipuladora de sessão, usando MongoDB. Primeiro, precisaremos criar a classe MongoSessionHandler
em uma instalação recém criada do Laravel (Nós pegaremos muitos códigos emprestados da classe Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler
):
1 |
<?php namespace Illuminate\Session; |
2 |
|
3 |
use Mongo; |
4 |
use MongoDate; |
5 |
use MongoBinData; |
6 |
|
7 |
class MongoSessionHandler implements \SessionHandlerInterface |
8 |
{
|
9 |
/**
|
10 |
* Configuração do Mongo DB
|
11 |
*
|
12 |
* @var array
|
13 |
*/
|
14 |
protected $config; |
15 |
|
16 |
/**
|
17 |
* Conexão do Mongo DB
|
18 |
*
|
19 |
* @var \Mongo
|
20 |
*/
|
21 |
protected $connection; |
22 |
|
23 |
/**
|
24 |
* Coleção do Mongo DB
|
25 |
*
|
26 |
* @var \MongoCollection
|
27 |
*/
|
28 |
protected $collection; |
29 |
/**
|
30 |
* Cria uma nova instância do Manipulador do driver do Mongo DB.
|
31 |
*
|
32 |
* @param array $config
|
33 |
* - $config['host'] Mongodb host
|
34 |
* - $config['username'] Mongodb username
|
35 |
* - $config['password'] Mongodb password
|
36 |
* - $config['database'] Mongodb database
|
37 |
* - $config['collection'] Mongodb collection
|
38 |
* @return void
|
39 |
*/
|
40 |
public function __construct(array $config) |
41 |
{
|
42 |
$this->config = $config; |
43 |
|
44 |
$connection_string = 'mongodb://'; |
45 |
|
46 |
if (!empty($this->config['username']) && !empty($this->config['password'])) { |
47 |
$connection_string .= "{$this->config['user']}:{$this->config['password']}@"; |
48 |
}
|
49 |
|
50 |
$connection_string .= "{$this->config['host']}"; |
51 |
|
52 |
$this->connection = new Mongo($connection_string); |
53 |
|
54 |
$this->collection = $this->connection->selectCollection($this->config['database'], $this->config['collection']); |
55 |
}
|
56 |
|
57 |
/**
|
58 |
* {@inheritDoc}
|
59 |
*/
|
60 |
public function open($savePath, $sessionName) |
61 |
{
|
62 |
return true; |
63 |
}
|
64 |
|
65 |
/**
|
66 |
* {@inheritDoc}
|
67 |
*/
|
68 |
public function close() |
69 |
{
|
70 |
return true; |
71 |
}
|
72 |
|
73 |
/**
|
74 |
* {@inheritDoc}
|
75 |
*/
|
76 |
public function read($sessionId) |
77 |
{
|
78 |
$session_data = $this->collection->findOne(array( |
79 |
'_id' => $sessionId, |
80 |
));
|
81 |
|
82 |
if (is_null($session_data)) { |
83 |
return ''; |
84 |
} else { |
85 |
return $session_data['session_data']->bin; |
86 |
}
|
87 |
}
|
88 |
|
89 |
/**
|
90 |
* {@inheritDoc}
|
91 |
*/
|
92 |
public function write($sessionId, $data) |
93 |
{
|
94 |
$this->collection->update( |
95 |
array( |
96 |
'_id' => $sessionId |
97 |
),
|
98 |
array( |
99 |
'$set' => array( |
100 |
'session_data' => new MongoBinData($data, MongoBinData::BYTE_ARRAY), |
101 |
'timestamp' => new MongoDate(), |
102 |
)
|
103 |
),
|
104 |
array( |
105 |
'upsert' => true, |
106 |
'multiple' => false |
107 |
)
|
108 |
);
|
109 |
}
|
110 |
|
111 |
/**
|
112 |
* {@inheritDoc}
|
113 |
*/
|
114 |
public function destroy($sessionId) |
115 |
{
|
116 |
$this->collection->remove(array( |
117 |
'_id' => $sessionId |
118 |
));
|
119 |
|
120 |
return true; |
121 |
}
|
122 |
|
123 |
/**
|
124 |
* {@inheritDoc}
|
125 |
*/
|
126 |
public function gc($lifetime) |
127 |
{
|
128 |
$time = new MongoDate(time() - $lifetime); |
129 |
|
130 |
$this->collection->remove(array( |
131 |
'timestamp' => array('$lt' => $time), |
132 |
));
|
133 |
|
134 |
return true; |
135 |
}
|
136 |
}
|
Você deve salvar esse arquivo no diretório vendor/laravel/framework/src/Illuminate/Session
. Para os propósitos desse projeto, nós colocaremos nesse diretório, porém, idealmente, ele deve ficar localizado dentro de um diretório usando seu próprio namespace.
Feito isso, precisamos garantir que a classe Manager
pode invocar o driver. Nós podemos fazer isso utilizando o método Manager::extend
. Abra o arquivo vendor/laravel/framework/src/Illuminate/Session/SessionServiceProvider.php
e adicione o código a seguir. Idealmente, deveríamos estender o provedor de serviços, porém, essa parte está além do escopo desse tutorial.
1 |
/**
|
2 |
* Prepara a callback do Mongo Driver
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
public function setupMongoDriver() |
7 |
{
|
8 |
$manager = $this->app['session']; |
9 |
|
10 |
$manager->extend('mongo', function($app) { |
11 |
return new MongoSessionHandler(array( |
12 |
'host' => $app['config']->get('session.mongo.host'), |
13 |
'username' => $app['config']->get('session.mongo.username'), |
14 |
'password' => $app['config']->get('session.mongo.password'), |
15 |
'database' => $app['config']->get('session.mongo.database'), |
16 |
'collection' => $app['config']->get('session.mongo.collection') |
17 |
));
|
18 |
});
|
19 |
}
|
Atualize o método register()
para que ele invoce esse método:
1 |
/**
|
2 |
* Register the service provider.
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
public function register() |
7 |
{
|
8 |
$this->setupDefaultDriver(); |
9 |
|
10 |
$this->registerSessionManager(); |
11 |
|
12 |
$this->setupMongoDriver(); |
13 |
|
14 |
$this->registerSessionDriver(); |
15 |
}
|
Depois disso, precisamos definir a configuração do Mongo DB. Abra o arquivo app/config/session.php
e defina as seguintes configurações:
1 |
/**
|
2 |
* Configurações do Mongo DB
|
3 |
*/
|
4 |
'mongo' => array( |
5 |
'host' => '127.0.0.1', |
6 |
'username' => '', |
7 |
'password' => '', |
8 |
'database' => 'laravel', |
9 |
'collection' => 'laravel_session_collection' |
10 |
)
|
Já que estamos nesse arquivo, também devemos atualizar a configuração do driver na parte superior driver
:
1 |
'driver' => 'mongo' |
Agora, tente acessar a página inicial (geralmente, localhost/diretorio_instalacao_laravel/public
). Se a página carregar sem mostrar a página WHOOPS
(página de erros), então, parabéns, nós fomos bem sucedidos em criar um driver de sessão novinho em folha! Teste-o, adicionando alguns dados fictícios na sessão, através do método Session::set()
e, então, recupere-o através do Session::get()
para mostrá-lo na página.
O Componente Auth
O componente Auth do Laravel é responsável por manipular a autenticação dos usuários na framework e, também, é responsável por administrar as senhas. O que o componente do Laravel fez, foi criar uma abstração de um típico sistema de administração de usuários, usavél na maioria das aplicações web, o que ajuda aos programadores a, facilmente, implementar um sistemade login. Da mesma forma que o componente Session, ele também faz uso do Laravel Manager. Atualmente, o componente Auth tem drivers para:
-
eloquent
- esse driver faz uso do ORM embutido no Laravel, oEloquent
. Ele também lança mão de uma classeUser.php
pré-existente, que fica no diretóriomodels
. -
database
- Essa daqui usa qualquer conexão de base de dados que estiver configurada por padrão. Ela faz uso da classeGenericUser
para acessar os dados dos usuários.
Já que aqui também seguiremos a mesma implementação do componente Session
, o provedor de serviços é bem semelhante ao que vimos mais acima:
1 |
/**
|
2 |
* Register the service provider.
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
public function register() |
7 |
{
|
8 |
$this->app->bindShared('auth', function($app) |
9 |
{
|
10 |
// Once the authentication service has actually been requested by the developer
|
11 |
// we will set a variable in the application indicating such. This helps us
|
12 |
// know that we need to set any queued cookies in the after event later.
|
13 |
$app['auth.loaded'] = true; |
14 |
|
15 |
return new AuthManager($app); |
16 |
});
|
17 |
}
|
Nós podemos ver que, basicamente, ele cria uma classe AuthManager
que envolve qualquer driver que estamos usando, além de agir como uma fábrica para esse driver. Dentro da classe AuthManager
, ela, novamente, cria o driver apropriado, envolta na classe Guard
, que age da mesma forma que a classe Store
para a Session
.
Criando Nosso Próprio Manipulador de Autenticação
Da mesma forma que antes, vamos criar uma classe MongoUserProvider
:
1 |
<?php namespace Illuminate\Auth; |
2 |
|
3 |
use Mongo; |
4 |
use Illuminate\Hashing\HasherInterface; |
5 |
|
6 |
class MongoUserProvider implements UserProviderInterface { |
7 |
|
8 |
/**
|
9 |
* Uma instância do MongoDB
|
10 |
*
|
11 |
* @param \Mongo
|
12 |
*/
|
13 |
protected $connection; |
14 |
|
15 |
/**
|
16 |
* A instância da conexão com o MongoDB
|
17 |
*
|
18 |
* @param \MongoConnection
|
19 |
*/
|
20 |
protected $collection; |
21 |
|
22 |
/**
|
23 |
* A array de configuração do Mongo
|
24 |
*
|
25 |
* @var array
|
26 |
*/
|
27 |
protected $config; |
28 |
|
29 |
/**
|
30 |
* Create a new Mongo user provider.
|
31 |
*
|
32 |
* @param array $config
|
33 |
* - $config['host'] Mongodb host
|
34 |
* - $config['username'] Mongodb username
|
35 |
* - $config['password'] Mongodb password
|
36 |
* - $config['database'] Mongodb database
|
37 |
* - $config['collection'] Mongodb collection
|
38 |
* @return void
|
39 |
*/
|
40 |
public function __construct(array $config) |
41 |
{
|
42 |
$this->config = $config; |
43 |
|
44 |
$connection_string = 'mongodb://'; |
45 |
|
46 |
if (!empty($this->config['username']) && !empty($this->config['password'])) { |
47 |
$connection_string .= "{$this->config['user']}:{$this->config['password']}@"; |
48 |
}
|
49 |
|
50 |
$connection_string .= "{$this->config['host']}"; |
51 |
|
52 |
$this->connection = new Mongo($connection_string); |
53 |
|
54 |
$this->collection = $this->connection->selectCollection($this->config['database'], $this->config['collection']); |
55 |
}
|
56 |
|
57 |
/**
|
58 |
* Retorna um usuário a partir do seu identificador único.
|
59 |
*
|
60 |
* @param mixed $identifier
|
61 |
* @return \Illuminate\Auth\UserInterface|null
|
62 |
*/
|
63 |
public function retrieveById($identifier) |
64 |
{
|
65 |
$user_data = $this->collection->findOne(array( |
66 |
'_id' => $identifier, |
67 |
));
|
68 |
|
69 |
if (!is_null($user_data)) { |
70 |
return new GenericUser((array) $user_data); |
71 |
}
|
72 |
}
|
73 |
|
74 |
/**
|
75 |
* Retorna um usuário a partir de credenciais apresentadas.
|
76 |
*
|
77 |
* @param array $credentials
|
78 |
* @return \Illuminate\Auth\UserInterface|null
|
79 |
*/
|
80 |
public function retrieveByCredentials(array $credentials) |
81 |
{
|
82 |
// Attempt to look for the user first regardless of password
|
83 |
// We'll do that in the validateCredentials method
|
84 |
if (isset($credentials['password'])) { |
85 |
unset($credentials['password']); |
86 |
}
|
87 |
|
88 |
$user_data = $this->collection->findOne($credentials); |
89 |
|
90 |
if (!is_null($user_data)) { |
91 |
return new GenericUser((array) $user_data); |
92 |
}
|
93 |
}
|
94 |
|
95 |
/**
|
96 |
* Valida um usuário em relação credenciais apresentadas.
|
97 |
*
|
98 |
* @param \Illuminate\Auth\UserInterface $user
|
99 |
* @param array $credentials
|
100 |
* @return bool
|
101 |
*/
|
102 |
public function validateCredentials(UserInterface $user, array $credentials) |
103 |
{
|
104 |
if (!isset($credentials['password'])) { |
105 |
return false; |
106 |
}
|
107 |
|
108 |
return ($credentials['password'] === $user->getAuthPassword()); |
109 |
}
|
110 |
}
|
É importante atentar que não estamos temos métodos de comparação com senhas criptografadas, uma vez que esse código foi criado para facilitar a criação de dados fictícios e poder testá-la depois. No código de produção, você precisará criptgrafar as senhas. Verifique a classe Illuminate\Auth\DatabaseUserProvider
para um ótimo exemplo de como fazer isso.
Depois, precisamos registrar a callback do nosso driver customizado junto ao AuthManager
. Para fazer isso, precisamos atualizar o método register
do provedor de serviços:
1 |
/**
|
2 |
* Register the service provider.
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
public function register() |
7 |
{
|
8 |
$this->app->bindShared('auth', function($app) |
9 |
{
|
10 |
// Once the authentication service has actually been requested by the developer
|
11 |
// we will set a variable in the application indicating such. This helps us
|
12 |
// know that we need to set any queued cookies in the after event later.
|
13 |
$app['auth.loaded'] = true; |
14 |
|
15 |
$auth_manager = new AuthManager($app); |
16 |
|
17 |
$auth_manager->extend('mongo', function($app) { |
18 |
return new MongoUserProvider( |
19 |
array( |
20 |
'host' => $app['config']->get('auth.mongo.host'), |
21 |
'username' => $app['config']->get('auth.mongo.username'), |
22 |
'password' => $app['config']->get('auth.mongo.password'), |
23 |
'database' => $app['config']->get('auth.mongo.database'), |
24 |
'collection' => $app['config']->get('auth.mongo.collection') |
25 |
)
|
26 |
);
|
27 |
});
|
28 |
|
29 |
return $auth_manager; |
30 |
});
|
31 |
}
|
Por último, também precisamos atualizar o arquivo de configuração auth.php
para usar o driver do Mongo, assim como prover os valores de configuração do Mongo:
1 |
'driver' => 'mongo', |
2 |
...
|
3 |
...
|
4 |
...
|
5 |
/**
|
6 |
* Configurações do Mongo DB
|
7 |
*/
|
8 |
'mongo' => array( |
9 |
'host' => '127.0.0.1', |
10 |
'username' => '', |
11 |
'password' => '', |
12 |
'database' => 'laravel', |
13 |
'collection' => 'laravel_auth_collection' |
14 |
)
|
Testar isso é um pouco complicado, mas, para fazê-lo, lançaremos mão da linha de comando do Mongo DB para inserir um novo usuário na coleção:
1 |
mongo |
2 |
|
3 |
> use laravel_auth
|
4 |
switched to db laravel_auth |
5 |
> db.laravel_auth_collection.insert({id: 1, email:"nikko@nikkobautista.com", password:"test_password"}) |
6 |
> db.laravel_auth_collection.find() |
7 |
> { "_id" : ObjectId("530c609f2caac8c3a8e4814f"), "id" 1, "email" : "nikko@emailtest.com", "password" : "test_password" } |
Agora, testaremos o método Auth::validate
:
1 |
var_dump(Auth::validate(array('email' => 'nikko@emailtest.com', 'password' => 'test_password'))); |
Isso deveria retornar um bool(true)
. Se ele retornar, então acabamos de criar nosso próprio driver de auntenticação!
O Componente Cache
O componente de Cache do Laravel manipula o mecanismo de cache para usarmos na framework. Da mesma forma que discutimos para os outros componentes, ele também faz uso do Laravel Manager (você percebeu algum padrão?). O componente Cache tem drivers para:
apc
memcached
redis
-
file
- cache baseado em arquivos. Os dados são salvos no diretóriosapp/storage/cache
. -
database
- cache salvos na base de dados. Os dados são salvos em registros na base de dados. O schema da base de dados para isso é descrito na Documentação do Laravel. -
array
- dados são "guardados" em um array. Lembre-se que o cache baseado emarray
não persiste os dados e é apagado a cada carregamento.
Como, novamente, segue a mesma implementação dos componente anteriores, você pode assumir que o processo de criar o provedor de serviços é bem semelhante:
1 |
/**
|
2 |
* Register the service provider.
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
public function register() |
7 |
{
|
8 |
$this->app->bindShared('cache', function($app) |
9 |
{
|
10 |
return new CacheManager($app); |
11 |
});
|
12 |
|
13 |
$this->app->bindShared('cache.store', function($app) |
14 |
{
|
15 |
return $app['cache']->driver(); |
16 |
});
|
17 |
|
18 |
$this->app->bindShared('memcached.connector', function() |
19 |
{
|
20 |
return new MemcachedConnector; |
21 |
});
|
22 |
|
23 |
$this->registerCommands(); |
24 |
}
|
O método register()
cria um CacheManager
, que serve como envólucro e fábrica para os drivers. Dentro do manager, ele envolve o driver em relação à classe Repository
, de forma semelhante às classes Store
e Guard
.
Criando Nosso Próprio Manipulador de Cache
Crie a classe MongoStore
, que estenderá a classe Illuminate\Cache\StoreInterface
:
1 |
<?php namespace Illuminate\Cache; |
2 |
|
3 |
use Mongo; |
4 |
|
5 |
class MongoStore implements StoreInterface |
6 |
{
|
7 |
/**
|
8 |
* A instância do Mongo DB
|
9 |
*
|
10 |
* @param \Mongo
|
11 |
*/
|
12 |
protected $connection; |
13 |
|
14 |
/**
|
15 |
* A instância da conexão com o MongoDB
|
16 |
*
|
17 |
* @param \MongoConnection
|
18 |
*/
|
19 |
protected $collection; |
20 |
|
21 |
/**
|
22 |
* A array de configuração do Mongo DB
|
23 |
*
|
24 |
* @var array
|
25 |
*/
|
26 |
protected $config; |
27 |
|
28 |
/**
|
29 |
* Criar a nova "loja" de cache com Mongo.
|
30 |
*
|
31 |
* @param array $config
|
32 |
* - $config['host'] Mongodb host
|
33 |
* - $config['username'] Mongodb username
|
34 |
* - $config['password'] Mongodb password
|
35 |
* - $config['database'] Mongodb database
|
36 |
* - $config['collection'] Mongodb collection
|
37 |
* @return void
|
38 |
*/
|
39 |
public function __construct(array $config) |
40 |
{
|
41 |
$this->config = $config; |
42 |
|
43 |
$connection_string = 'mongodb://'; |
44 |
|
45 |
if (!empty($this->config['username']) && !empty($this->config['password'])) { |
46 |
$connection_string .= "{$this->config['user']}:{$this->config['password']}@"; |
47 |
}
|
48 |
|
49 |
$connection_string .= "{$this->config['host']}"; |
50 |
|
51 |
$this->connection = new Mongo($connection_string); |
52 |
|
53 |
$this->collection = $this->connection->selectCollection($this->config['database'], $this->config['collection']); |
54 |
}
|
55 |
|
56 |
/**
|
57 |
* Retorna um item do cache usando uma chave.
|
58 |
*
|
59 |
* @param string $key
|
60 |
* @return mixed
|
61 |
*/
|
62 |
public function get($key) |
63 |
{
|
64 |
$cache_data = $this->getObject($key); |
65 |
|
66 |
if (!$cache_data) { |
67 |
return null; |
68 |
}
|
69 |
|
70 |
return unserialize($cache_data['cache_data']); |
71 |
}
|
72 |
|
73 |
/**
|
74 |
* Retorna todo o objeto, ao invés de retorna somente os dados
|
75 |
*
|
76 |
* @param string $key
|
77 |
* @return array|null
|
78 |
*/
|
79 |
protected function getObject($key) |
80 |
{
|
81 |
$cache_data = $this->collection->findOne(array( |
82 |
'key' => $key, |
83 |
));
|
84 |
|
85 |
if (is_null($cache_data)) { |
86 |
return null; |
87 |
}
|
88 |
|
89 |
if (isset($cache_data['expire']) && time() >= $cache_data['expire']) { |
90 |
$this->forget($key); |
91 |
return null; |
92 |
}
|
93 |
|
94 |
return $cache_data; |
95 |
}
|
96 |
|
97 |
/**
|
98 |
* Salva um item na loja por um dado número de minutos.
|
99 |
*
|
100 |
* @param string $key
|
101 |
* @param mixed $value
|
102 |
* @param int $minutes
|
103 |
* @return void
|
104 |
*/
|
105 |
public function put($key, $value, $minutes) |
106 |
{
|
107 |
$expiry = $this->expiration($minutes); |
108 |
|
109 |
$this->collection->update( |
110 |
array( |
111 |
'key' => $key |
112 |
),
|
113 |
array( |
114 |
'$set' => array( |
115 |
'cache_data' => serialize($value), |
116 |
'expiry' => $expiry, |
117 |
'ttl' => ($minutes * 60) |
118 |
)
|
119 |
),
|
120 |
array( |
121 |
'upsert' => true, |
122 |
'multiple' => false |
123 |
)
|
124 |
);
|
125 |
}
|
126 |
|
127 |
/**
|
128 |
* Incrementa o valor de um item no cache.
|
129 |
*
|
130 |
* @param string $key
|
131 |
* @param mixed $value
|
132 |
* @return void
|
133 |
*
|
134 |
* @throws \LogicException
|
135 |
*/
|
136 |
public function increment($key, $value = 1) |
137 |
{
|
138 |
$cache_data = $this->getObject($key); |
139 |
|
140 |
if (!$cache_data) { |
141 |
$new_data = array( |
142 |
'cache_data' => serialize($value), |
143 |
'expiry' => $this->expiration(0), |
144 |
'ttl' => $this->expiration(0) |
145 |
);
|
146 |
} else { |
147 |
$new_data = array( |
148 |
'cache_data' => serialize(unserialize($cache_data['cache_data']) + $value), |
149 |
'expiry' => $this->expiration((int) ($cache_data['ttl']/60)), |
150 |
'ttl' => $cache_data['ttl'] |
151 |
);
|
152 |
}
|
153 |
|
154 |
$this->collection->update( |
155 |
array( |
156 |
'key' => $key |
157 |
),
|
158 |
array( |
159 |
'$set' => $new_data |
160 |
),
|
161 |
array( |
162 |
'upsert' => true, |
163 |
'multiple' => false |
164 |
)
|
165 |
);
|
166 |
}
|
167 |
|
168 |
/**
|
169 |
* Decrementa o valor de um item no cache.
|
170 |
*
|
171 |
* @param string $key
|
172 |
* @param mixed $value
|
173 |
* @return void
|
174 |
*
|
175 |
* @throws \LogicException
|
176 |
*/
|
177 |
public function decrement($key, $value = 1) |
178 |
{
|
179 |
$cache_data = $this->getObject($key); |
180 |
|
181 |
if (!$cache_data) { |
182 |
$new_data = array( |
183 |
'cache_data' => serialize((0 - $value)), |
184 |
'expiry' => $this->expiration(0), |
185 |
'ttl' => $this->expiration(0) |
186 |
);
|
187 |
} else { |
188 |
$new_data = array( |
189 |
'cache_data' => serialize(unserialize($cache_data['cache_data']) - $value), |
190 |
'expiry' => $this->expiration((int) ($cache_data['ttl']/60)), |
191 |
'ttl' => $cache_data['ttl'] |
192 |
);
|
193 |
}
|
194 |
|
195 |
$this->collection->update( |
196 |
array( |
197 |
'key' => $key |
198 |
),
|
199 |
array( |
200 |
'$set' => $new_data |
201 |
),
|
202 |
array( |
203 |
'upsert' => true, |
204 |
'multiple' => false |
205 |
)
|
206 |
);
|
207 |
}
|
208 |
|
209 |
/**
|
210 |
* Salva um item no cache, indefinidamente.
|
211 |
*
|
212 |
* @param string $key
|
213 |
* @param mixed $value
|
214 |
* @return void
|
215 |
*/
|
216 |
public function forever($key, $value) |
217 |
{
|
218 |
return $this->put($key, $value, 0); |
219 |
}
|
220 |
|
221 |
/**
|
222 |
* Apaga um item do cache.
|
223 |
*
|
224 |
* @param string $key
|
225 |
* @return void
|
226 |
*/
|
227 |
public function forget($key) |
228 |
{
|
229 |
$this->collection->remove(array( |
230 |
'key' => $key |
231 |
));
|
232 |
}
|
233 |
|
234 |
/**
|
235 |
* Apaga todos os itens do cache.
|
236 |
*
|
237 |
* @return void
|
238 |
*/
|
239 |
public function flush() |
240 |
{
|
241 |
$this->collection->remove(); |
242 |
}
|
243 |
|
244 |
/**
|
245 |
* Retorna o tempo de expiração de um item.
|
246 |
*
|
247 |
* @param int $minutes
|
248 |
* @return int
|
249 |
*/
|
250 |
protected function expiration($minutes) |
251 |
{
|
252 |
if ($minutes === 0) return 9999999999; |
253 |
|
254 |
return time() + ($minutes * 60); |
255 |
}
|
256 |
|
257 |
/**
|
258 |
* Get the cache key prefix.
|
259 |
*
|
260 |
* @return string
|
261 |
*/
|
262 |
public function getPrefix() |
263 |
{
|
264 |
return ''; |
265 |
}
|
266 |
}
|
Nós também adicionaremos a callback do Mongo, novamente, no manager:
1 |
/**
|
2 |
* Register the service provider.
|
3 |
*
|
4 |
* @return void
|
5 |
*/
|
6 |
public function register() |
7 |
{
|
8 |
$this->app->bindShared('cache', function($app) |
9 |
{
|
10 |
$cache_manager = new CacheManager($app); |
11 |
|
12 |
$cache_manager->extend('mongo', function($app) { |
13 |
return new MongoStore( |
14 |
array( |
15 |
'host' => $app['config']->get('cache.mongo.host'), |
16 |
'username' => $app['config']->get('cache.mongo.username'), |
17 |
'password' => $app['config']->get('cache.mongo.password'), |
18 |
'database' => $app['config']->get('cache.mongo.database'), |
19 |
'collection' => $app['config']->get('cache.mongo.collection') |
20 |
)
|
21 |
);
|
22 |
});
|
23 |
|
24 |
return $cache_manager; |
25 |
});
|
26 |
|
27 |
$this->app->bindShared('cache.store', function($app) |
28 |
{
|
29 |
return $app['cache']->driver(); |
30 |
});
|
31 |
|
32 |
$this->app->bindShared('memcached.connector', function() |
33 |
{
|
34 |
return new MemcachedConnector; |
35 |
});
|
36 |
|
37 |
$this->registerCommands(); |
38 |
}
|
Por último, precisamos atualizar o arquivo de configuração cache.php
:
1 |
'driver' => 'mongo', |
2 |
...
|
3 |
...
|
4 |
...
|
5 |
/**
|
6 |
* Configuração do MongoDB
|
7 |
*/
|
8 |
'mongo' => array( |
9 |
'host' => '127.0.0.1', |
10 |
'username' => '', |
11 |
'password' => '', |
12 |
'database' => 'laravel', |
13 |
'collection' => 'laravel_cache_collection' |
14 |
)
|
Agora, tente usar os métodos Cache::put()
e Cache::get()
. Se tiver feito tudo certo, você foi capaz de guardar e recuperar dados de um sistema de cache que usa MongoDB!
Conclusão
Nesse tutorial aprendemos:
- Sobre o sistema de componentes do Laravel, chamado
Illuminate
, que é usado pela framework Laravel. - Sobre os provedores de serviços do Laravel e um pouco sobre como eles funcionam.
- Sobre o sistema Manager do Laravel, que age tanto como envólucro quanto uma fábrica para os drivers.
- Sobre os componentes Session, Auth e Cache e como criar novos drivers para cada um deles.
- As bibliotecas Store, Guard e Repository que utilizam esses drivers.
Espero que esse artigo possa ajudar os programadores a criarem seus próprios drivers para que possam estender as funcionalidades do Laravel.
Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!