Laravel Unwrapped Session, Auth och Cache

Under de senaste åren har Laravel blivit en av de mest framträdande ramar som mjukvaruutvecklare använder för att bygga sina webbapplikationer. Liknande populariteten som CodeIgniter åtnjutit i sin storhetstid, har Laravel blivit lovad för sin användarvänlighet, vänlighet till nybörjare och dess efterlevnad av industristandarder.

Introduktion

En sak men det är inte mycket programmerare som dra fördel av Laravels komponentbaserade system. Sedan omvandlingen till kompositdrivna komponenter har Laravel 4 blivit ett väldigt modulärt system, som liknar de vuxna ramarna som Symfony. Detta kallas Belysa grupp av komponenter, som enligt min mening inte är själva ramen själva, men är en samling av bibliotek som en ram kan använda. Laravels faktiska ramverk representeras av Laravel-skelettansökan (finns på laravel / laravel GitHub repository) som använder dessa komponenter för att bygga en webbapplikation.

I denna handledning dyker vi in ​​i en grupp av dessa komponenter, lär oss hur de fungerar, hur de används av ramverket och hur vi kan utöka deras funktionalitet.

Session-komponenten

Laravel-sessionen hanterar sessioner för webbapplikationen. Den använder sig av ett drivrutinsbaserat system som heter Laravel Manager, som fungerar som både en fabrik och ett omslag för vilken drivrutin som är inställd i konfigurationsfilen. Med denna skrivning har Session-komponenten drivrutiner för:

  • fil - en filbaserad sessiondrivrutin där sessionsdata sparas i en krypterad fil.
  • kaka - en cookiebaserad sessiondrivrutin där sessionsdata krypteras i användarens cookies.
  • databas - Sessionsdata sparas i vilken databas som är konfigurerad för applikationen.
  • apc - Sessionsdata sparas i APC.
  • memcached - Sessionsdata sparas i Memcached.
  • redis - Sessionsdata sparas i Redis.
  • array - Sessionsdata sparas i en PHP-array. Observera att array-sessiondrivrutinen inte stöder uthållighet och brukar vanligtvis endast användas i konsolkommandon.

Tjänsteleverantörer

De flesta Laravel-användare inser inte, men en stor del av hur Laravel fungerar, ligger inom sina tjänsteleverantörer. De är i huvudsak bootstrap-filer för varje komponent och de är abstraherade nog så att användare kan starta upp några komponenter på något sätt.

En grov förklaring till hur detta fungerar är nedan:

  1. Laravel-applikationskomponenten initieras. Detta är huvuddrivrutinen för hela ramen, ansvarig för hanteringen av HTTP-förfrågan, körning av tjänsteleverantörerna samt att fungera som en beroendebehållare för ramverket.
  2. När en tjänsteleverantör har sprang, är det Registrera Metoden heter. Detta gör det möjligt för oss att instansera vilken komponent vi vill ha.
    • Tänk på att alla tjänsteleverantörer har tillgång till huvud Laravel-applikationen (via $ This-> app), vilket skulle låta tjänsteleverantörer skicka instanser av de upplösta klasserna i beredskapsbehållaren.
  3. När dessa beroenden är laddade bör vi vara fria att använda dem genom att ringa på behållaren, till exempel via Laravels fasadsystem, App :: make.

Kommer tillbaka till Sessioner, låt oss ta en snabb titt på SessionServiceProivider:

 / ** * Registrera session manager instans. * * @return void * / protected function registerSessionManager () $ this-> app-> bindShare ('session', funktion ($ app) returnera nya SessionManager ($ app););  / ** * Registrera sessionens drivrutinsinstans. * * @return void * / skyddad funktion registerSessionDriver () $ this-> app-> bindShared ('session.store', funktion ($ app) // Först ska vi skapa session manager som ansvarar för / / skapandet av de olika sessionsdrivrutinerna när de behövs av // ansökningsinstansen och löser dem på en lat belastning. $ manager = $ app ['session'], returnera $ manager-> driver ();) ; 

Dessa två metoder kallas av Registrera() fungera. Den första, registerSessionManager (), kallas för att först registrera Session. Denna klass sträcker sig Chef som jag nämnde ovanpå. Den andra, registerSessionDriver () registrerar en sessionhanterare för chefen baserat på vad vi har konfigurerat. Detta kallar så småningom den här metoden i Illuminate \ Support \ chef klass:

/ ** * Skapa en ny drivrutinsinstans. * * @paramsträng $ driver * @return mixed * * @throws \ InvalidArgumentException * / skyddad funktion createDriver ($ driver) $ method = 'create'.ucfirst ($ driver).' Driver '; // Vi kontrollerar om det finns en skapningsmetod för den angivna drivrutinen. Om inte vi // kommer att kolla efter en anpassad drivrutinsskapare, som tillåter utvecklare att skapa // drivrutiner med egen anpassad drivrutinsuppsättning för att skapa den. om (isset ($ this-> customCreators [$ driver])) return $ this-> callCustomCreator ($ driver);  elseif (method_exists ($ this, $ method)) return $ this -> $ method ();  kasta nytt \ InvalidArgumentException ("Driver [$ driver] stöds inte."); 

Härifrån kan vi se att baserat på drivrutins namn, från konfigurationsfilen, kallas en viss metod. Så, om vi har det konfigurerat att använda fil session handler, kommer det att ringa denna metod i Session klass:

/ ** * Skapa en instans av filsessionen drivrutinen. * * @return \ Illuminate \ Session \ Store * / skyddad funktion createFileDriver () return $ this-> createNativeDriver ();  / ** * Skapa en instans av filsessionen drivrutinen. * * @return \ Illuminate \ Session \ Store * / skyddad funktion createNativeDriver () $ path = $ this-> app ['config'] ['session.files']; returnera $ this-> buildSession (ny FileSessionHandler ($ this-> app ['files'], $ path)); 

Förarklassen injiceras sedan i a Lagra klass, som är ansvarig för att ringa själva sessionen metoder. Det gör att vi faktiskt skiljer genomförandet av SessionHandlerInterface från SPL till förare, den Lagra klassen underlättar det.

Skapa vår egen session handler

Låt oss skapa vår egen Session Handler, en MongoDB Session Handler. Först måste vi skapa en MongoSessionHandler inuti en nyinstallerad Laravel-projektinstans. (Vi kommer att låna tungt från Symfony \ Component \ HttpFoundation \ Session \ Förvaring \ Handler \ MongoDbSessionHandler) .:

config = $ config; $ connection_string = 'mongodb: //'; om ! tom ($ this-> config ['användarnamn']) &&! tomt ($ this-> config ['password'])) $ connection_string. = "$ this-> config ['user'] : $ this-> config [ 'password'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['databas'], $ this-> config ['collection']);  / ** * @inheritDoc * / allmän funktion öppen ($ savePath, $ sessionName) return true;  / ** * @inheritDoc * / public function close () return true;  / ** * @inheritDoc * / public function read ($ sessionId) $ session_data = $ this-> collection-> findOne (array ('_id' => $ sessionId,)); om is_null ($ session_data)) return "; annars return $ session_data ['session_data'] -> bin; / ** * @inheritDoc * / public function write ($ sessionId, $ data)  $ this-> collection-> update (array ('_id' => $ sessionId), array ('$ set' => array ('session_data' => ny MongoBinData ($ data, MongoBinData :: BYTE_ARRAY) => ny MongoDate (),)), array ('upsert' => true, 'multipel' => false)); / ** * @inheritDoc * / public function förstöra ($ sessionId) $ this- > collection-> remove (array ('_id' => $ sessionId)); return true; / ** * @inheritDoc * / allmän funktion gc ($ livstid) $ time = new MongoDate $ lifetime); $ this-> collection-> remove (array ('timestamp' => array ('$ lt' => $ tid),)); returnera true; 

Du borde spara det här i leverantör / laravel / ram / src / Illuminate / Session mapp. För det här projektet kommer vi att lägga den här, men helst den här filen ska vara i sitt eget namn på biblioteket.

Därefter måste vi se till att Chef klassen kan ringa den här drivrutinen. Vi kan göra detta genom att använda Chef :: förlänga metod. Öppna leverantör / laravel / ram / src / Illuminate / Session / SessionServiceProvider.php och lägg till följande kod. Helst bör vi utöka tjänsteleverantören, men det ligger utanför ramen för denna handledning.

/ ** * Ställ in Mongo-drivrutinen återuppringning * * @return void * / allmän funktion setupMongoDriver () $ manager = $ this-> app ['session']; $ manager-> extend ('mongo', funktion ($ app) returnera ny MongoSessionHandler (array ('host' => $ app ['config'] -> get ('session.mongo.host'), 'användarnamn' => $ app ['config'] -> få ('session.mongo.username'), 'lösenord' => $ app ['config'] -> get ('session.mongo.password'), 'databas' => $ app ['config'] -> få ('session.mongo.database'), 'samling' => $ app ['config'] -> get ('session.mongo.collection'))); ); 

Se till att uppdatera Registrera() metod för att ringa denna metod:

/ ** * Registrera tjänsteleverantören. * * @return void * / public function register () $ this-> setupDefaultDriver (); $ This-> registerSessionManager (); $ This-> setupMongoDriver (); $ This-> registerSessionDriver (); 

Därefter måste vi definiera Mongo DB-konfigurationen. Öppna app / config / session.php och definiera följande konfigurationsinställningar:

/ ** * Mongo DB-inställningar * / 'mongo' => array ('host' => '127.0.0.1', 'användarnamn' => ", 'lösenord' =>", 'database' => 'laravel' 'samling' => 'laravel_session_collection')

Medan vi är på den här filen borde vi också uppdatera förare konfiguration upptill:

'förare' => 'mongo'

Nu kan du försöka komma åt huvudsidan (vanligtvis, localhost / somefolder / public). Om denna sida laddas utan att visa OJ DÅ sida, grattis, vi har framgångsrikt skapat en helt ny sessionsdrivrutin! Testa det genom att ange några dummy data på sessionen, via Session :: set () och sedan echo den tillbaka via Session :: få ().

Auth-komponenten

Laravel Auth-komponenten hanterar användarautentisering för ramverket, såväl som lösenordshantering. Vad Laravel-komponenten har gjort här är att skapa en abstrakt tolkning av det typiska användarhanteringssystemet som kan användas i de flesta webbapplikationer, vilket i sin tur hjälper programmeraren att enkelt implementera ett inloggningssystem. Liksom Session-komponenten använder den också Laravel Manager. För närvarande har Auth-komponenten drivrutiner för:

  • vältalig - Detta använder sig av Laravels inbyggda ORM som heter Vältalig. Det utnyttjar också den färdiga user.php klass inne i modeller mapp.
  • databas - Detta använder vilken databasanslutning som standard är konfigurerad. Det använder sig av a GenericUser klass för att få tillgång till användardata.

Eftersom detta följer samma implementering som Session komponent, tjänsteleverantören är mycket lik den som vi sett på toppen:

/ ** * Registrera tjänsteleverantören. * * @return void * / public function register () $ this-> app-> bindShared ('auth', funktion ($ app) // När autentiseringstjänsten faktiskt har begärts av utvecklaren // en variabel i applikationen som indikerar sådan. Det hjälper oss // att vi behöver ställa in några kökkök i efterhand senare. $ app ['auth.loaded'] = true, returnera nya AuthManager ($ app);) ; 

Här kan vi se att det i grunden skapar en authManager klass som sveper runt vilken förare vi använder, samt fungerar som en fabrik för den. Inuti authManager, det skapar igen den lämpliga drivrutinen, insvept runt a Vakt klass, som fungerar på samma sätt som Lagra klass från Session.

Skapa vår egen Auth Handler

Som tidigare, låt oss börja med att skapa en MongoUserProvider:

config = $ config; $ connection_string = 'mongodb: //'; om ! tom ($ this-> config ['användarnamn']) &&! tomt ($ this-> config ['password'])) $ connection_string. = "$ this-> config ['user'] : $ this-> config [ 'password'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['databas'], $ this-> config ['collection']);  / ** * Hämta en användare med sin unika identifierare. * * @param blandad $ identifierare * @return \ Illuminate \ Auth \ UserInterface | null * / allmän funktion retrieveById ($ identifierare) $ user_data = $ this-> collection-> findOne (array ('_id' => $ identifierare, )); om (! is_null ($ user_data)) returnera nya GenericUser ((array) $ user_data);  / ** * Hämta en användare med angivna referenser. * * @param array $ credentials * @return \ Illuminate \ Auth \ UserInterface | null * / public function retrieveByCredentials (array $ credentials) // Försök att leta efter användaren först oavsett lösenord // Vi gör det i validateCredentials metod om (isset ($ credentials ['password'])) unset ($ credentials ['password']);  $ user_data = $ this-> collection-> findOne ($ credentials); om (! is_null ($ user_data)) returnera nya GenericUser ((array) $ user_data);  / ** * Validera en användare mot angivna referenser. * * @param \ Illuminate \ Auth \ UserInterface $ användare * @param array $ credentials * @return bool * / allmän funktion validateCredentials (UserInterface $ user, array $ credentials) om (! isset ($ credentials ['lösenord']) ) return false;  returnera ($ credentials ['password'] === $ användare-> getAuthPassword ()); 

Det är viktigt att notera här att jag inte kontrollerar ett lösenord, det var för enkelhetens skull att göra det enklare för oss att skapa dummy-data och testa det senare. I produktionskod måste du se till att lösenordet har hash. Kolla in Illuminate \ Auth \ DatabaseUserProvider klass för ett bra exempel på hur man gör det här.

Efteråt måste vi registrera vår anpassade drivrutinsanrop på authManager. För att göra det måste vi uppdatera tjänsteleverantörens Registrera metod:

/ ** * Registrera tjänsteleverantören. * * @return void * / public function register () $ this-> app-> bindShared ('auth', funktion ($ app) // När autentiseringstjänsten faktiskt har begärts av utvecklaren // en variabel i ansökan som indikerar sådant. Det hjälper oss // att vi behöver ställa in några kökkök i efterhand senare. $ app ['auth.loaded'] = true; $ auth_manager = ny AuthManager ($ app); $ auto [edit => $ app ['config'] -> get ('auth.mongo.username'), 'lösenord' => $ app ['config'] -> get ('auth.mongo.password'), 'databas' => $ app ['config'] -> få ('auth.mongo.database'), 'samling' => $ app ['config'] -> get ('auth.mongo.collection'))); ); returnera $ auth_manager;); 

Slutligen behöver vi också uppdatera auth.php konfigurationsfil för att utnyttja Mongo-drivrutinen, samt ge den rätt Mongo-konfigurationsvärdena:

'driver' => 'mongo', ... / ** * Mongo DB-inställningar * / 'mongo' => array ('host' => '127.0.0.1', 'användarnamn' => ", 'lösenord' => , 'database' => 'laravel', 'collection' => 'laravel_auth_collection')

Testa detta är lite svårare, för att göra det, använd Mongo DB CLI för att infoga en ny användare i samlingen:

mongo> använd laravel_auth bytt till db laravel_auth> db.laravel_auth_collection.insert (id: 1, email: "[email protected]", lösenord: "test_password")> db.laravel_auth_collection.find ()> "_id" : ObjectId ("530c609f2caac8c3a8e4814f"), "id" 1, "email": "[email protected]", "lösenord": "test_password"

Nu testa det genom att prova en Auth :: validera metodsamtal:

var_dump (Auth :: validera (array ('email' => '[email protected]', 'lösenord' => 'test_password'))));

Detta bör dumpa en bool (true). Om det gör så har vi framgångsrikt skapat vår egen Auth-drivrutin!

Cache-komponenten

Laravel Cache-komponenten hanterar cachemekanismer för användning i ramen. Liksom de båda komponenterna som vi har diskuterat använder den också Laravel Manager (märker du ett mönster?). Cacheskomponenten har drivrutiner för:

  • apc
  • memcached
  • redis
  • fil - en filbaserad cache. Data sparas i app / lagring / cache väg.
  • databas - databasbaserad cache. Data sparas i rader i databasen. Databasschemat beskrivs i Laravel-dokumentationen.
  • array - data "cachas" i en array. Tänk på att array cacheminnet är inte ihållande och rensas på varje sidlast.

Eftersom det här följer samma implementering som båda komponenterna som vi har diskuterat kan du säkert anta att tjänsteleverantören är ganska likadan:

/ ** * Registrera tjänsteleverantören. * * @return void * / public function register () $ this-> app-> bindShare ('cache', funktion ($ app) returnera nya CacheManager ($ app);); $ this-> app-> bindShared ('cache.store', funktion ($ app) return $ app ['cache'] -> drivrutin ();); $ this-> app-> bindShared ('memcached.connector', funktion () returnera ny MemcachedConnector;); $ This-> registerCommands (); 

De Registrera() metod här skapar en CacheManager, Det fungerar igen som omslag och fabrik för förarna. Inom chefen slingrar den föraren runt en Repository klass, som liknar Lagra och Vakt klasser.

Skapa vår egen Cache Handler

Skapa MongoStore, vilken bör förlänga Illuminate \ Cache \ StoreInterface:

config = $ config; $ connection_string = 'mongodb: //'; om ! tom ($ this-> config ['användarnamn']) &&! tomt ($ this-> config ['password'])) $ connection_string. = "$ this-> config ['user'] : $ this-> config [ 'password'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['databas'], $ this-> config ['collection']);  / ** * Hämta ett objekt från cachen med nyckel. * * @paramsträng $ key * @return mixed * / public function get ($ key) $ cache_data = $ this-> getObject ($ key); om (! $ cache_data) return null;  returnera unserialize ($ cache_data ['cache_data']);  / ** * Returnera hela objektet istället för bara cache_data * * @param string $ key * @return array | null * / skyddad funktion getObject ($ key) $ cache_data = $ this-> collection-> findOne ('key' => $ key,)); om (is_null ($ cache_data)) return null;  om (isset ($ cache_data ['expire']) && time ()> = $ cache_data ['expire']) $ this-> glöm ($ key); returnera null;  returnera $ cache_data;  / ** * Spara ett objekt i cacheminnet under ett visst antal minuter. * * @param sträng $ key * @param blandat $ värde * @param int $ minuter * @return void * / public function put ($ key, $ värde, $ minuter) $ expiry = $ this-> utgång ); $ this-> collection-> update (array ('key' => $ nyckel), array ('$ set' => array ('cache_data' => serialize ($ värde), 'expiry' => $ expiry ttl '=> ($ minuter * 60))), array (' upsert '=> true,' multiple '=> false));  / ** * Öka värdet på ett objekt i cacheminnet. * * @param sträng $ key * @param blandat $ värde * @return void * * @throws \ LogicException * / allmän funktion inkrement ($ key, $ value = 1) $ cache_data = $ this-> getObject ($ key) ; om (! $ cache_data) $ new_data = array ('cache_data' => serialize ($ värde), 'expiry' => $ this-> utgången (0), 'ttl' => $ this-> utgången );  andra $ new_data = array ('cache_data' => serialize (unserialize ($ cache_data ['cache_data']) + $ värde), 'expiry' => $ this-> utgången ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('key' => $ nyckel), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Minska värdet på ett objekt i cacheminnet. * * @param sträng $ key * @param blandat $ värde * @return void * * @throws \ LogicException * / allmän funktion minskning ($ key, $ value = 1) $ cache_data = $ this-> getObject ($ key) ; om (! $ cache_data) $ new_data = array ('cache_data' => serialize ((0 - $ värde)), 'expiry' => $ this-> utgången (0), 'ttl' => $ this-> utgången (0));  $ $ new_data = array ('cache_data' => serialize (unserialize ($ cache_data ['cache_data']) - $ värde), 'expiry' => $ this-> utgå ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('key' => $ nyckel), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Spara ett objekt i cachen på obestämd tid. * * @param sträng $ key * @param blandat $ värde * @return void * / allmän funktion för alltid ($ key, $ value) return $ this-> put ($ key, $ value, 0);  / ** * Ta bort ett objekt från cacheminnet. * * @param string $ key * @return void * / allmän funktion glömma ($ key) $ this-> collection-> remove (array ('key' => $ key));  / ** * Ta bort alla objekt från cacheminnet. * * @return void * / public function flush () $ this-> collection-> remove ();  / ** * Få utgångstiden baserat på angivna minuter. * * @param int $ minutes * @return int * / skyddad funktionens utgång ($ minuter) if ($ minutes === 0) returnera 9999999999; returtid () + ($ minuter * 60);  / ** * Hämta cache-nyckel prefixet. * * @returnsträng * / offentlig funktion getPrefix () return ";

Vi måste också lägga till Mongo-återuppringningen igen till chefen:

/ ** * Registrera tjänsteleverantören. * * @return void * / public function register () $ this-> app-> bindShare (cache), funktion ($ app) $ cache_manager = ny CacheManager ($ app); $ cache_manager-> extend ', funktion ($ app) returnera nya MongoStore (array (' host '=> $ app [' config '] -> get (' cache.mongo.host '),' användarnamn '=> $ app [' config ' ] -> få ('cache.mongo.username'), 'lösenord' => $ app ['config'] -> få ('cache.mongo.password'), 'database' => $ app ['config' ] -> få ('cache.mongo.database'), 'samling' => $ app ['config'] -> get ('cache.mongo.collection'));); returnera $ cache_manager;) ; $ this-> app-> bindShared ('cache.store', funktion ($ app) return $ app ['cache'] -> drivrutin ();); $ this-> app-> bindShared ('memcached.connector', funktion () returnera ny MemcachedConnector;); $ This-> registerCommands (); 

Slutligen måste vi uppdatera cache.php config-fil:

'driver' => 'mongo', ... / ** * Mongo DB-inställningar * / 'mongo' => array ('host' => '127.0.0.1', 'användarnamn' => ", 'lösenord' => , 'database' => 'laravel', 'collection' => 'laravel_cache_collection')

Försök nu att använda Cache :: sätta () och Cache :: få () metoder. Om det görs korrekt borde vi kunna använda MongoDB för att cache data!

Slutsats

I denna handledning lärde vi oss om följande:

  • Laravels komponentbaserade system kallas Belysa, som används av Laravel ramverket.
  • Laravel Service Providers och lite om hur de fungerar.
  • Laravel's Manager-system, som fungerar som både omslag och fabrik för förarna.
  • Session, Auth och Cache-komponenter och hur man skapar nya drivrutiner för varje.
  • Store, Guard och Repository-bibliotek som använder dessa drivrutiner.

Förhoppningsvis hjälper det programmerare att skapa egna drivrutiner och utöka den nuvarande funktionaliteten i Laravel-ramen.