Laravel Unwrapped: Sitzung, Authentifizierung und Cache
() translation by (you can also view the original English article)
In den letzten Jahren hat sich Laravel zu einem der bekanntesten Frameworks entwickelt, mit denen Softwareentwickler ihre Webanwendungen erstellen. Ähnlich wie die Popularität, die CodeIgniter in seiner Blütezeit genoss, wurde Laravel für seine Benutzerfreundlichkeit, Freundlichkeit für Anfänger und die Einhaltung von Industriestandards gelobt.
Einführung
Eine Sache, die nicht viele Programmierer nutzen, ist das komponentenbasierte System von Laravel. Seit seiner Umstellung auf Komponenten mit Komponistenunterstützung ist Laravel 4 zu einem sehr modularen System geworden, ähnlich der Ausführlichkeit ausgereifterer Frameworks wie Symfony. Dies wird als Illuminate
-Gruppe von Komponenten bezeichnet, die meiner Meinung nach nicht das eigentliche Framework selbst ist, sondern eine Zusammenstellung von Bibliotheken, die ein Framework möglicherweise verwenden kann. Das eigentliche Framework von Laravel wird durch die Laravel-Skeleton-Anwendung (im GitHub-Repository von laravel/laravel
) dargestellt, die diese Komponenten zum Erstellen einer Webanwendung verwendet.
In diesem Tutorial werden wir uns mit einer Gruppe dieser Komponenten befassen und lernen, wie sie funktionieren, wie sie vom Framework verwendet werden und wie wir ihre Funktionalität erweitern können.
Die Sitzungskomponente
Die Laravel-Sitzungskomponente verarbeitet Sitzungen für die Webanwendung. Es verwendet ein treiberbasiertes System namens Laravel Manager, das sowohl als Factory als auch als Wrapper für den in der Konfigurationsdatei festgelegten Treiber fungiert. Zum jetzigen Zeitpunkt verfügt die Sitzungskomponente über Treiber für:
-
file
- Ein dateibasierter Sitzungstreiber, bei dem Sitzungsdaten in einer verschlüsselten Datei gespeichert werden. -
cookie
- Ein Cookie-basierter Sitzungstreiber, bei dem Sitzungsdaten in den Cookies des Benutzers verschlüsselt werden. -
database
- Sitzungsdaten werden in der für die Anwendung konfigurierten Datenbank gespeichert. -
apc
- Sitzungsdaten werden in APC gespeichert. -
memcached
- Sitzungsdaten werden in Memcached gespeichert. -
redis
- Sitzungsdaten werden in Redis gespeichert. -
array
- Sitzungsdaten werden in einem PHP-Array gespeichert. Beachten Sie, dass der Array-Sitzungstreiber keine Persistenz unterstützt und normalerweise nur in Konsolenbefehlen verwendet wird.
Dienstleister
Die meisten Laravel-Benutzer wissen nicht, aber ein großer Teil der Funktionsweise von Laravel liegt bei seinen Dienstleistern. Sie sind im Wesentlichen Bootstrap-Dateien für jede Komponente und sie sind so weit abstrahiert, dass Benutzer beliebige Komponenten auf beliebige Weise booten können.
Eine grobe Erklärung, wie dies funktioniert, finden Sie unten:
- Die Laravel-Anwendungskomponente wird initiiert. Dies ist der Haupttreiber des gesamten Frameworks, der für die Verarbeitung der HTTP-Anforderung, die Ausführung der Dienstanbieter sowie als Abhängigkeitscontainer für das Framework verantwortlich ist.
- Sobald ein Dienstanbieter ausgeführt wird, wird seine
register
-Methode aufgerufen. Auf diese Weise können wir die gewünschte Komponente instanziieren.- Beachten Sie, dass alle Dienstanbieter Zugriff auf die Hauptanwendung von Laravel haben (über
$this->app
), mit der Dienstanbieter Instanzen der aufgelösten Klassen in den Abhängigkeitscontainer verschieben können.
- Beachten Sie, dass alle Dienstanbieter Zugriff auf die Hauptanwendung von Laravel haben (über
- Sobald diese Abhängigkeiten geladen sind, sollten wir sie verwenden können, indem wir den Container beispielsweise über Laravels Facade-System
App::make
aufrufen.
Gehen wir zurück zu den Sitzungen und werfen einen kurzen Blick auf den 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 |
}
|
Diese beiden Methoden werden von der Funktion register()
aufgerufen. Der erste, registerSessionManager()
, wird aufgerufen, um den SessionManager
zunächst zu registrieren. Diese Klasse erweitert den oben erwähnten Manager
. Das zweite, registerSessionDriver()
, registriert einen Sitzungshandler für den Manager, basierend auf dem, was wir konfiguriert haben. Dies ruft schließlich diese Methode in der Klasse Illuminate\Support\Manager
auf:
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 |
}
|
Von hier aus können wir sehen, dass basierend auf dem Namen des Treibers aus der Konfigurationsdatei eine bestimmte Methode aufgerufen wird. Wenn wir es für die Verwendung des file
-Sitzungshandlers konfiguriert haben, ruft es diese Methode in der SessionManager
-Klasse auf:
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 |
}
|
Die Treiberklasse wird dann in eine Store
-Klasse eingefügt, die für den Aufruf der eigentlichen Sitzungsmethoden verantwortlich ist. Auf diese Weise können wir die Implementierung des SessionHandlerInterface
von der SPL in die Treiber trennen. Die Store
-Klasse erleichtert dies.
Erstellen eines eigenen Sitzungshandlers
Lassen Sie uns unseren eigenen Session Handler erstellen, einen MongoDB Session Handler. Zunächst müssen wir einen MongoSessionHandler
in einer neu installierten Laravel-Projektinstanz erstellen. (Wir werden viel von 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 |
* Mongo db config
|
11 |
*
|
12 |
* @var array
|
13 |
*/
|
14 |
protected $config; |
15 |
|
16 |
/**
|
17 |
* Mongo db connection
|
18 |
*
|
19 |
* @var \Mongo
|
20 |
*/
|
21 |
protected $connection; |
22 |
|
23 |
/**
|
24 |
* Mongodb collection
|
25 |
*
|
26 |
* @var \MongoCollection
|
27 |
*/
|
28 |
protected $collection; |
29 |
/**
|
30 |
* Create a new Mongo driven handler instance.
|
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 |
}
|
Sie sollten dies im Ordner vendor/laravel/framework/src/Illuminate/Session
speichern. Für die Zwecke dieses Projekts werden wir es hier einfügen, aber im Idealfall sollte sich diese Datei in einem eigenen Bibliotheks-Namespace befinden.
Als nächstes müssen wir sicherstellen, dass die Manager
-Klasse diesen Treiber aufrufen kann. Wir können dies tun, indem wir die Manager::extend
-Methode verwenden. Öffnen Sie vendor/laravel/framework/src/Illuminate/Session/SessionServiceProvider.php
und fügen Sie den folgenden Code hinzu. Im Idealfall sollten wir den Dienstanbieter erweitern, dies liegt jedoch außerhalb des Bereichs dieses Lernprogramms.
1 |
/**
|
2 |
* Setup the Mongo Driver callback
|
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 |
}
|
Stellen Sie sicher, dass Sie die register()
-Methode aktualisieren, um diese Methode aufzurufen:
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 |
}
|
Als nächstes müssen wir die Mongo DB-Konfiguration definieren. Öffnen Sie app/config/session.php
und definieren Sie die folgenden Konfigurationseinstellungen:
1 |
/**
|
2 |
* Mongo DB settings
|
3 |
*/
|
4 |
'mongo' => array( |
5 |
'host' => '127.0.0.1', |
6 |
'username' => '', |
7 |
'password' => '', |
8 |
'database' => 'laravel', |
9 |
'collection' => 'laravel_session_collection' |
10 |
)
|
Während wir uns in dieser Datei befinden, sollten wir auch die driver
-Konfiguration oben aktualisieren:
1 |
'driver' => 'mongo' |
Versuchen Sie nun, auf die Hauptseite zuzugreifen (normalerweise localhost/somefolder/public
). Wenn diese Seite geladen wird, ohne die WHOOPS
-Seite anzuzeigen, dann herzlichen Glückwunsch, wir haben erfolgreich einen brandneuen Sitzungstreiber erstellt! Testen Sie es, indem Sie einige Dummy-Daten in der Sitzung über Session::set()
festlegen und dann über Session::get()
zurücksenden.
Die Auth-Komponente
Die Laravel Auth-Komponente übernimmt die Benutzerauthentifizierung für das Framework sowie die Kennwortverwaltung. Die Laravel-Komponente hat hier eine abstrakte Interpretation des typischen Benutzerverwaltungssystems erstellt, das in den meisten Webanwendungen verwendet werden kann, was wiederum dem Programmierer hilft, ein Anmeldesystem einfach zu implementieren. Wie die Sitzungskomponente wird auch der Laravel-Manager verwendet. Derzeit verfügt die Auth-Komponente über Treiber für:
-
eloquent
- dies nutzt Laravels eingebautes ORM namensEloquent
. Es wird auch die vorgefertigteUser.php
-Klasse immodels
-Ordner verwendet. -
database
- Hierbei wird die standardmäßig konfigurierte Datenbankverbindung verwendet. Es verwendet eineGenericUser
-Klasse für den Zugriff auf die Benutzerdaten.
Da dies der gleichen Implementierung wie die Session
-komponente folgt, ist der Dienstanbieter dem, was wir oben gesehen haben, sehr ähnlich:
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 |
}
|
Hier können wir sehen, dass im Grunde genommen eine AuthManager
-Klasse erstellt wird, die den von uns verwendeten Treiber umschließt und als Factory dafür fungiert. Im AuthManager
wird erneut der entsprechende Treiber erstellt, der um eine Guard
-Klasse gewickelt ist und sich genauso verhält wie die Store
-Klasse aus Session
.
Erstellen eines eigenen Auth-Handlers
Beginnen wir wie zuvor mit der Erstellung eines MongoUserProviders
:
1 |
<?php namespace Illuminate\Auth; |
2 |
|
3 |
use Mongo; |
4 |
use Illuminate\Hashing\HasherInterface; |
5 |
|
6 |
class MongoUserProvider implements UserProviderInterface { |
7 |
|
8 |
/**
|
9 |
* The mongo instance
|
10 |
*
|
11 |
* @param \Mongo
|
12 |
*/
|
13 |
protected $connection; |
14 |
|
15 |
/**
|
16 |
* The mongo connection instance
|
17 |
*
|
18 |
* @param \MongoConnection
|
19 |
*/
|
20 |
protected $collection; |
21 |
|
22 |
/**
|
23 |
* The Mongo config array
|
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 |
* Retrieve a user by their unique identifier.
|
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 |
* Retrieve a user by the given credentials.
|
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 |
* Validate a user against the given credentials.
|
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 |
}
|
Es ist wichtig zu beachten, dass ich nicht gegen ein Hash-Passwort prüfe. Dies wurde der Einfachheit halber durchgeführt, um es uns zu erleichtern, Dummy-Daten zu erstellen und diese später zu testen. Im Produktionscode müssen Sie sicherstellen, dass Sie das Kennwort hashen. In der Klasse Illuminate\Auth\DatabaseUserProvider
finden Sie ein hervorragendes Beispiel dafür.
Anschließend müssen wir unseren benutzerdefinierten Treiberrückruf im AuthManager
registrieren. Dazu müssen wir die register
-Methode des Dienstanbieters aktualisieren:
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 |
}
|
Zuletzt müssen wir auch die Konfigurationsdatei auth.php
aktualisieren, um den Mongo-Treiber verwenden zu können, und ihm die richtigen Mongo-Konfigurationswerte bereitstellen:
1 |
'driver' => 'mongo', |
2 |
...
|
3 |
...
|
4 |
...
|
5 |
/**
|
6 |
* Mongo DB settings
|
7 |
*/
|
8 |
'mongo' => array( |
9 |
'host' => '127.0.0.1', |
10 |
'username' => '', |
11 |
'password' => '', |
12 |
'database' => 'laravel', |
13 |
'collection' => 'laravel_auth_collection' |
14 |
)
|
Das Testen ist etwas schwieriger. Verwenden Sie dazu die Mongo DB-CLI, um einen neuen Benutzer in die Sammlung einzufügen:
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" } |
Testen Sie es jetzt, indem Sie einen Auth::validate
-Methodenaufruf ausprobieren:
1 |
var_dump(Auth::validate(array('email' => 'nikko@emailtest.com', 'password' => 'test_password'))); |
Dies sollte einen bool(true)
ausgeben. Wenn ja, haben wir erfolgreich unseren eigenen Auth-Treiber erstellt!
Die Cache-Komponente
Die Laravel-Cache-Komponente verwaltet Caching-Mechanismen zur Verwendung im Framework. Wie die beiden Komponenten, die wir besprochen haben, wird auch der Laravel-Manager verwendet (bemerken Sie ein Muster?). Die Cache-Komponente verfügt über Treiber für:
apc
memcached
redis
-
file
- ein dateibasierter Cache. Daten werden im Pfadapp/storage/cache
gespeichert. -
database
- Datenbankbasierter Cache. Daten werden in Zeilen in der Datenbank gespeichert. Das Datenbankschema ist in der Laravel-Dokumentation beschrieben. -
array
- Daten werden in einem Array "cached". Beachten Sie, dass derarray
-Cache nicht persistent ist und bei jedem Laden der Seite gelöscht wird.
Da dies der gleichen Implementierung folgt wie die beiden Komponenten, die wir besprochen haben, können Sie davon ausgehen, dass der Dienstanbieter ziemlich ähnlich ist:
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 |
}
|
Die register()
-Methode erstellt hier einen CacheManager
, der wiederum als Wrapper und Factory für die Treiber fungiert. Innerhalb des Managers wird der Treiber um eine Repository
-Klasse gewickelt, ähnlich wie bei den Store
- und Guard
-Klassen.
Erstellen eines eigenen Cache-Handlers
Erstellen Sie den MongoStore
, der das Illuminate\Cache\StoreInterface
erweitern soll:
1 |
<?php namespace Illuminate\Cache; |
2 |
|
3 |
use Mongo; |
4 |
|
5 |
class MongoStore implements StoreInterface |
6 |
{
|
7 |
/**
|
8 |
* The mongo instance
|
9 |
*
|
10 |
* @param \Mongo
|
11 |
*/
|
12 |
protected $connection; |
13 |
|
14 |
/**
|
15 |
* The mongo connection instance
|
16 |
*
|
17 |
* @param \MongoConnection
|
18 |
*/
|
19 |
protected $collection; |
20 |
|
21 |
/**
|
22 |
* The Mongo config array
|
23 |
*
|
24 |
* @var array
|
25 |
*/
|
26 |
protected $config; |
27 |
|
28 |
/**
|
29 |
* Create a new Mongo cache store.
|
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 |
* Retrieve an item from the cache by key.
|
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 |
* Return the whole object instead of just the cache_data
|
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 |
* Store an item in the cache for a given number of minutes.
|
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 |
* Increment the value of an item in the 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 |
* Decrement the value of an item in the 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 |
* Store an item in the cache indefinitely.
|
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 |
* Remove an item from the 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 |
* Remove all items from the cache.
|
236 |
*
|
237 |
* @return void
|
238 |
*/
|
239 |
public function flush() |
240 |
{
|
241 |
$this->collection->remove(); |
242 |
}
|
243 |
|
244 |
/**
|
245 |
* Get the expiration time based on the given minutes.
|
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 |
}
|
Wir müssen dem Manager auch den Mongo-Rückruf erneut hinzufügen:
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 |
}
|
Zuletzt müssen wir die Konfigurationsdatei cache.php
aktualisieren:
1 |
'driver' => 'mongo', |
2 |
...
|
3 |
...
|
4 |
...
|
5 |
/**
|
6 |
* Mongo DB settings
|
7 |
*/
|
8 |
'mongo' => array( |
9 |
'host' => '127.0.0.1', |
10 |
'username' => '', |
11 |
'password' => '', |
12 |
'database' => 'laravel', |
13 |
'collection' => 'laravel_cache_collection' |
14 |
)
|
Versuchen Sie nun, die Methoden Cache::put()
und Cache::get()
zu verwenden. Wenn es richtig gemacht wird, sollten wir MongoDB verwenden können, um die Daten zwischenzuspeichern!
Abschluss
In diesem Tutorial haben wir Folgendes gelernt:
- Laravels komponentenbasiertes System namens
Illuminate
, das vom Laravel-Framework verwendet wird. - Laravel Service Providers und ein bisschen darüber, wie sie funktionieren.
- Laravels Manager-System, das sowohl als Wrapper als auch als Factory für die Treiber fungiert.
- Sitzungs-, Authentifizierungs- und Cache-Komponenten und Erstellen neuer Treiber für jede Komponente.
- Store-, Guard- und Repository-Bibliotheken, die diese Treiber verwenden.
Hoffentlich hilft dies Programmierern dabei, ihre eigenen Treiber zu erstellen und die aktuelle Funktionalität des Laravel-Frameworks zu erweitern.