Laravel unwrapped Session, Auth og Cache

I de senere år har Laravel blitt en av de mest fremtredende rammer som programvareingeniører bruker til å bygge sine webapplikasjoner. I likhet med populariteten som CodeIgniter likte i sin høytid, har Laravel blitt lovet for brukervennlighet, vennlighet til nybegynnere og overholdelse av industristandarder.

Introduksjon

En ting om at det ikke mange programmerere utnytter er Laravels komponentbaserte system. Siden konverteringen til komponentdrevne komponenter, har Laravel 4 blitt et veldig modulært system, som ligner på overflaten av mer modne rammer som Symfony. Dette kalles Belyse Gruppe av komponenter, som etter min mening ikke er selve rammen selv, men er en samling av biblioteker som et rammeverk potensielt kan bruke. Laravels faktiske rammeverk er representert av Laravel-skjelettapplikasjonen (funnet på laravel / laravel GitHub repository) som bruker disse komponentene til å bygge et webprogram.

I denne opplæringen dykker vi inn i en gruppe av disse komponentene, lærer hvordan de fungerer, hvordan de brukes av rammen, og hvordan vi kan utvide deres funksjonalitet.

Sesjonskomponenten

Laravel Session-komponenten håndterer økter for webapplikasjonen. Den bruker et driverbasert system kalt Laravel Manager, som fungerer som både en fabrikk og en wrapper for hvilken driver som er angitt i konfigurasjonsfilen. Med denne skrivingen har sesjonskomponenten drivere for:

  • fil - en filbasert øktdriver der øktdata lagres i en kryptert fil.
  • cookie - en informasjonskapselbasert øktdriver hvor øktdata krypteres i brukerens informasjonskapsler.
  • database - øktdata lagres i hvilken database som er konfigurert for applikasjonen.
  • APC - øktdata lagres i APC.
  • memcached - øktdata lagres i Memcached.
  • Redis - øktdata lagres i redis.
  • matrise - øktdata lagres i et PHP-array. Vær oppmerksom på at array-sjåføren ikke støtter utholdenhet og vanligvis bare brukes i konsollkommandoer.

Tjenestetilbydere

De fleste Laravel-brukere skjønner ikke, men en stor del av hvordan Laravel fungerer, er innenfor sine tjenesteleverandører. De er i hovedsak bootstrap-filer for hver komponent, og de er abstraherte nok, slik at brukere kan starte opp noen komponenter på noen måte.

En grov forklaring på hvordan dette virker er under:

  1. Laravel-applikasjonskomponenten er initiert. Dette er hoveddriveren i hele rammen, ansvarlig for håndtering av HTTP-forespørselen, kjøring av tjenesteleverandørene, samt å fungere som en avhengighetsbeholder for rammen.
  2. Når en tjenesteleverandør er kjørt, er den registrere Metoden kalles. Dette gjør det mulig for oss å instansere hvilken komponent vi ønsker.
    • Husk at alle tjenesteleverandører har tilgang til hoved Laravel-søknaden (via $ Dette-> app), som ville la tjenesteleverandørene skyve forekomster av de løste klassene inn i avhengighetsbeholderen.
  3. Når disse avhengighetene er lastet, bør vi være fri til å bruke dem ved å ringe på beholderen, for eksempel via Laravels fasadesystem, App :: make.

Når vi går tilbake til øktene, la oss ta en rask titt på SessionServiceProivider:

 / ** * Registrer sessionsadministratorens forekomst. * * @return void * / beskyttet funksjon registerSessionManager () $ this-> app-> bindShare ('økt', funksjon ($ app) return new SessionManager ($ app););  / ** * Registrer sessionsdriverens forekomst. * * @return void * / beskyttet funksjon registerSessionDriver () $ this-> app-> bindShare ('session.store', funksjon ($ app) // Først skal vi opprette øktleder som er ansvarlig for / / opprettelse av de ulike sessionsdrivere når de trengs av // søknad instansen, og vil løse dem på lazy load basis. $ manager = $ app ['session'], returner $ manager-> driver ();) ; 

Disse to metodene kalles av registrere() funksjon. Den første, registerSessionManager (), er kalt til å opprinnelig registrere SessionManager. Denne klassen utvider sjef som jeg nevnte på toppen. Den andre, registerSessionDriver () registrerer en øktbehandler for lederen, basert på det vi har konfigurert. Dette kalles til slutt denne metoden i Belyse \ Support \ manager klasse:

/ ** * Opprett en ny driverinstans. * * @param streng $ driver * @return blandet * * @throws \ InvalidArgumentException * / beskyttet funksjon createDriver ($ driver) $ method = 'create'.ucfirst ($ driver).' Driver '; // Vi kontrollerer for å se om en skapningsmetode eksisterer for den oppgitte driveren. Hvis ikke, vil vi // sjekke om en tilpasset driveropprettelse, som gjør det mulig for utviklere å lage // drivere ved å bruke sin egen tilpassede driver skaperen Closure for å lage den. hvis (isset ($ this-> customCreators [$ driver])) return $ this-> callCustomCreator ($ driver);  elseif (method_exists ($ this, $ method)) return $ this -> $ metode ();  kaste nye \ InvalidArgumentException ("Driver [$ driver] støttes ikke."); 

Herfra kan vi se at basert på navnet på driveren, fra konfigurasjonsfilen, kalles en bestemt metode. Så, hvis vi har det konfigurert til å bruke fil økt behandler, vil det kalle denne metoden i SessionManager klasse:

/ ** * Opprett en forekomst av filsessionsdriveren. * * @return \ Illuminate \ Session \ Store * / beskyttet funksjon createFileDriver () return $ this-> createNativeDriver ();  / ** * Opprett en forekomst av filsessionsdriveren. * * @return \ Illuminate \ Session \ Store * / beskyttet funksjon createNativeDriver () $ path = $ this-> app ['config'] ['session.files']; returnere $ this-> buildSession (ny FileSessionHandler ($ this-> app ['files'], $ path)); 

Førerklassen injiseres deretter inn i a butikk klassen, som er ansvarlig for å ringe til de faktiske økt metodene. Dette lar oss faktisk skille implementeringen av SessionHandlerInterface fra SPL til driverne, den butikk klassen letter det.

Opprette vår egen sessionshåndterer

La oss lage vår egen Session Handler, en MongoDB Session Handler. Først må vi opprette en MongoSessionHandler inne i en nyinstallert Laravel prosjekt instans. (Vi skal låne tungt fra Symfony \ Component \ HttpFoundation \ Session \ Storage \ Handler \ MongoDbSessionHandler) .:

config = $ config; $ connection_string = 'mongodb: //'; hvis ! tom ($ this-> config ['brukernavn']) &&! tomt ($ this-> config ['passord'])) $ connection_string. = "$ this-> config ['bruker'] : $ dette-> konfig [ 'passord'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['samling']);  / ** * @inheritDoc * / offentlig funksjon åpen ($ savePath, $ sessionName) return true;  / ** * @inheritDoc * / offentlig funksjon lukk () return true;  / ** * @inheritDoc * / public function read ($ sessionId) $ session_data = $ this-> collection-> findOne (array ('_id' => $ sessionId,)); hvis is_null ($ session_data)) return "; annet return $ session_data ['session_data'] -> bin; / ** * @inheritDoc * / offentlig funksjon skrive ($ sessionId, $ data)  $ this-> collection-> update (array ('_id' => $ sessionId), array ('$ set' => array ('session_data' => ny MongoBinData ($ data, MongoBinData :: BYTE_ARRAY), 'tidsstempel' => ny MongoDate (),)), array ('upsert' => true, 'multiple' => false)); / ** * @inheritDoc * / offentlig funksjon ødelegge ($ sessionId) $ this- > samling-> fjern (array ('_id' => $ sessionId)); return true; / ** * @inheritDoc * / offentlig funksjon gc ($ levetid) $ time = new MongoDate $ levetid); $ dette-> samling-> fjern (array ('timestamp' => array ('$ lt' => $ tid),)); return true; 

Du bør lagre dette i leverandør / laravel / rammeverk / src / Illuminate / økt mappe. I forbindelse med dette prosjektet legger vi det her, men ideelt sett bør denne filen være innenfor eget navneområde for biblioteket.

Deretter må vi sørge for at sjef klassen kan ringe denne driveren. Vi kan gjøre dette ved å benytte Manager :: forlenge metode. Åpen leverandør / laravel / rammeverk / src / Illuminate / økt / SessionServiceProvider.php og legg til følgende kode. Ideelt sett bør vi utvide tjenesteleverandøren, men det er utenfor omfanget av denne opplæringen.

/ ** * Konfigurer Mongo Driver tilbakeringing * * @return void * / offentlig funksjon setupMongoDriver () $ manager = $ this-> app ['session']; $ manager-> utvide ('mongo', funksjon ($ app) returner ny MongoSessionHandler (array ('host' => $ app ['config'] -> get ('session.mongo.host'), 'brukernavn' => $ app ['config'] -> få ('session.mongo.username'), 'passord' => $ app ['config'] -> få ('session.mongo.password'), 'database' => $ app ['config'] -> få ('session.mongo.database'), 'samling' => $ app ['config'] -> få ('session.mongo.collection'))); ); 

Pass på at du oppdaterer registrere() metode for å ringe denne metoden:

/ ** * Registrer tjenesteleverandøren. * * @return void * / public function register () $ this-> setupDefaultDriver (); $ Dette-> registerSessionManager (); $ Dette-> setupMongoDriver (); $ Dette-> registerSessionDriver (); 

Deretter må vi definere Mongo DB-konfigurasjonen. Åpen app / config / session.php og definer følgende konfigurasjonsinnstillinger:

/ ** * Mongo DB-innstillinger * / 'mongo' => array ('host' => '127.0.0.1', 'brukernavn' => ", 'passord' =>", 'database' => 'laravel' 'samling' => 'laravel_session_collection')

Mens vi er på denne filen, bør vi også oppdatere sjåfør konfigurasjon på toppen:

'driver' => 'mongo'

Nå, prøv å få tilgang til hovedsiden (vanligvis, localhost / somefolder / offentlig). Hvis denne siden laster uten å vise Hoppsann siden, så gratulerer, vi har opprettet en helt ny sesjonsdriver! Test det ved å sette inn noen dummy data på økten, via Session :: satt () og deretter ekko det tilbake via Session :: får ().

Auth-komponenten

Laravel Auth-komponenten håndterer brukerautentisering for rammen, samt passordhåndtering. Hva Laravel-komponenten har gjort her, er å skape en abstrakt tolkning av det typiske brukerhåndteringssystemet som kan brukes i de fleste webapplikasjoner, noe som igjen hjelper programmereren å enkelt implementere et innloggingssystem. Som Session-komponenten benytter den også Laravel Manager. For tiden har Auth-komponenten drivere for:

  • veltalende - Dette gjør bruk av Laravels innebygde ORM kalt Veltalende. Den benytter også pre-laget User.php klasse inne i modeller mappe.
  • database - Dette bruker hvilken databaseforbindelse som er konfigurert som standard. Det gjør bruk av a GenericUser klassen for å få tilgang til brukerdataene.

Siden dette følger samme implementering som Økt komponent, tjenesteleverandøren er svært lik det vi har sett på toppen:

/ ** * Registrer tjenesteleverandøren. * * @return void * / public function register () $ this-> app-> bindShare ('auth', funksjon ($ app) // Når autentiseringstjenesten faktisk er blitt forespurt av utvikleren // en variabel i applikasjonen som indikerer slik. Dette hjelper oss / / vet at vi må sette inn noen kø i informasjonskapsler senere. $ app ['auth.loaded'] = true; returner ny AuthManager ($ app);) ; 

Her kan vi se at det i utgangspunktet skaper en AuthManager klasse som bryter rundt hvilken som helst sjåfør vi bruker, samt å fungere som en fabrikk for den. Inne i AuthManager, det skaper igjen den riktige driveren, innpakket rundt a Vakt klassen, som fungerer på samme måte som butikk klasse fra Økt.

Opprette vår egen Auth Handler

Som før, la oss begynne med å lage en MongoUserProvider:

config = $ config; $ connection_string = 'mongodb: //'; hvis ! tom ($ this-> config ['brukernavn']) &&! tomt ($ this-> config ['passord'])) $ connection_string. = "$ this-> config ['bruker'] : $ dette-> konfig [ 'passord'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['samling']);  / ** * Hent en bruker etter deres unike identifikator. * * @param blandet $ identifikator * @return \ Illuminate \ Auth \ UserInterface | null * / offentlig funksjon retrieveById ($ identifier) ​​$ user_data = $ this-> collection-> findOne (array ('_id' => $ identifiserer, )); hvis (! er_null ($ user_data)) returner ny GenericUser ((array) $ user_data);  / ** * Hent en bruker av de oppgitte legitimasjonene. * * @param array $ credentials * @return \ Illuminate \ Auth \ UserInterface | null * / offentlig funksjon retrieveByCredentials (array $ credentials) // Forsøk å se etter brukeren først uansett passord // Vi gjør det i validateCredentials metode hvis (isset ($ credentials ['passord'])) unset ($ credentials ['passord']);  $ user_data = $ this-> collection-> findOne ($ credentials); hvis (! er_null ($ user_data)) returner ny GenericUser ((array) $ user_data);  / ** * Bekreft en bruker mot de oppgitte legitimasjonene. * * @param \ Illuminate \ Auth \ UserInterface $ bruker * @param array $ credentials * @return bool * / offentlig funksjon validateCredentials (UserInterface $ bruker, array $ credentials) if (! isset ($ credentials ['passord']) ) return false;  returnere ($ credentials ['passord'] === $ bruker-> getAuthPassword ()); 

Det er viktig å merke seg at jeg ikke sjekker mot et hashed-passord, dette ble gjort for enkelhets skyld, for å gjøre det lettere for oss å lage dummy-data og teste dette senere. I produksjonskode må du passe på å hash passordet. Sjekk ut Belyse \ Auth \ DatabaseUserProvider klasse for et godt eksempel på hvordan du gjør dette.

Etterpå må vi registrere vår tilpassede driver tilbakeringing på AuthManager. For å gjøre det, må vi oppdatere tjenesteleverandøren registrere metode:

/ ** * Registrer tjenesteleverandøren. * * @return void * / public function register () $ this-> app-> bindShare ('auth', funksjon ($ app) // Når autentiseringstjenesten faktisk er blitt forespurt av utvikleren // en variabel i applikasjonen som indikerer slik. Dette hjelper oss / / vet at vi må sette inn køer i køen etterpå senere. $ app ['auth.loaded'] = true; $ auth_manager = ny AuthManager ($ app); $ auth_manager-> utvide ('mongo', funksjon ($ app) returner ny MongoUserProvider (array ('host' => $ app ['config'] -> get ('auth.mongo.host'), 'brukernavn' => $ app ['config'] -> get ('auth.mongo.username'), 'passord' => $ app ['config'] -> get ('auth.mongo.password'), 'database' => $ app ['config'] -> få ('auth.mongo.database'), 'samling' => $ app ['config'] -> get ('auth.mongo.collection'))); ); return $ auth_manager;); 

Til slutt må vi også oppdatere auth.php konfigurasjonsfil for å gjøre bruk av Mongo-driveren, samt gi den den rette Mongo-konfigurasjonsverdien:

'driver' => 'mongo', ... / ** * Mongo DB innstillinger * / 'mongo' => array ('host' => '127.0.0.1', 'brukernavn' => ", 'passord' => , 'database' => 'laravel', 'collection' => 'laravel_auth_collection')

Testing dette er litt vanskeligere, for å gjøre det, bruk Mongo DB CLI til å sette inn en ny bruker i samlingen:

mongo> bruk laravel_auth byttet til db laravel_auth> db.laravel_auth_collection.insert (id: 1, email: "[email protected]", passord: "test_password")> db.laravel_auth_collection.find ()> "_id" : ObjectId ("530c609f2caac8c3a8e4814f"), "id" 1, "email": "[email protected]", "passord": "test_password"

Nå, teste det ut ved å prøve en Auth :: validere metodeanrop:

var_dump (Auth :: validere (array ('email' => '[email protected]', 'passord' => 'test_password'))));

Dette bør dumpe en bool (sann). Hvis det gjør det, har vi vellykket opprettet vår egen Auth-driver!

Cache-komponenten

Laravel Cache-komponenten håndterer cachemekanismer for bruk i rammen. Som begge komponentene som vi har diskutert, benytter den også Laravel Manager (merker du et mønster?). Cachekomponenten har drivere for:

  • APC
  • memcached
  • Redis
  • fil - en filbasert cache. Data lagres i app / lagring / cache sti.
  • database - database-basert cache. Data lagres i rader i databasen. Databaseskjemaet er beskrevet i Laravel-dokumentasjonen.
  • matrise - data blir "cached" i en matrise. Husk at matrise hurtigbufferen er ikke vedvarende og ryddes på hver sidebelastning.

Siden dette følger samme implementering som begge komponenter som vi har diskutert, kan du trygt anta at tjenesteleverandøren er ganske lik:

/ ** * Registrer tjenesteleverandøren. * * @return void * / public function register () $ this-> app-> bindShare ('cache', funksjon ($ app) returner ny CacheManager ($ app);); $ this-> app-> bindShare ('cache.store', funksjon ($ app) return $ app ['cache'] -> driver ();); $ this-> app-> bindShared ('memcached.connector', funksjon () returner ny MemcachedConnector;); $ Dette-> registerCommands (); 

De registrere() Metoden her lager en CacheManager, som igjen fungerer som en wrapper og fabrikk for sjåførene. Innenfor sjefen bryter den sjåføren rundt en Oppbevaringssted klasse, ligner på butikk og Vakt klasser.

Opprette vår egen Cache Handler

Opprett MongoStore, som skal utvide Belyse \ Cache \ StoreInterface:

config = $ config; $ connection_string = 'mongodb: //'; hvis ! tom ($ this-> config ['brukernavn']) &&! tomt ($ this-> config ['passord'])) $ connection_string. = "$ this-> config ['bruker'] : $ dette-> konfig [ 'passord'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['samling']);  / ** * Hent et element fra hurtigbufferen med nøkkel. * * @param string $ key * @return mixed * / offentlig funksjon få ($ key) $ cache_data = $ this-> getObject ($ key); hvis (! $ cache_data) return null;  returner unserialize ($ cache_data ['cache_data']);  / ** * Returner hele objektet i stedet for bare cache_data * * @param string $ key * @return array | null * / beskyttet funksjon getObject ($ key) $ cache_data = $ this-> collection-> findOne ('key' => $ key,)); hvis (is_null ($ cache_data)) return null;  hvis (isset ($ cache_data ['utløper']) && time ()> = $ cache_data ['utløper']) $ this-> glemmer ($ key); return null;  returner $ cache_data;  / ** * Lagre et element i hurtigbufferen i et gitt antall minutter. * * @param streng $ nøkkel * @param blandet $ verdi * @param int $ minutter * @return void * / offentlig funksjon satt ($ nøkkel, $ verdi, $ minutter) $ utløp = $ this-> utløp ); $ this-> collection-> update (array ('key' => $ nøkkel), array ('$ set' => array ('cache_data' => serialize ($ verdi), 'utløp' => $ utløp, ' ttl '=> ($ minutter * 60))), array (' upsert '=> true,' multiple '=> false));  / ** * Øk verdien av et element i hurtigbufferen. * * @param streng $ nøkkel * @param blandet $ verdi * @return void * * @throws \ LogicException * / offentlig funksjon økning ($ key, $ value = 1) $ cache_data = $ this-> getObject ($ key) ; hvis (! $ cache_data) $ new_data = array ('cache_data' => serialize ($ value), 'utløp' => $ this-> utløp (0), 'ttl' => $ this-> utløp );  andre $ new_data = array ('cache_data' => serialize (unserialize ($ cache_data ['cache_data']) + $ verdi), 'utløp' => $ this-> utløp ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('key' => $ nøkkel), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Reduser verdien av et element i hurtigbufferen. * * @param streng $ nøkkel * @param blandet $ verdi * @return void * * @throws \ LogicException * / offentlig funksjon reduksjon ($ key, $ value = 1) $ cache_data = $ this-> getObject ($ key) ; hvis (! $ cache_data) $ new_data = array ('cache_data' => serialize ((0 - $ verdi)), 'utløp' => $ this-> utløp (0), 'ttl' => $ this-> utløp (0));  ellers $ new_data = array ('cache_data' => serialize (unserialize ($ cache_data ['cache_data']) - $ verdi), 'utløp' => $ this-> utløp ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('key' => $ nøkkel), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Lagre et element i hurtigbufferen på ubestemt tid. * * @param streng $ nøkkel * @param blandet $ verdi * @return void * / offentlig funksjon for alltid ($ nøkkel, $ verdi) return $ this-> put ($ key, $ value, 0);  / ** * Fjern et element fra hurtigbufferen. * * @param string $ key * @return void * / offentlig funksjon glemme ($ key) $ this-> collection-> remove (array ('key' => $ nøkkel));  / ** * Fjern alle elementene fra hurtigbufferen. * * @return void * / public function flush () $ this-> collection-> remove ();  / ** * Få utløptidspunktet basert på de angitte minuttene. * * @param int $ minutes * @return int * / beskyttet funksjon utløp ($ minutter) hvis ($ minutter === 0) returnere 9999999999; returtid () + ($ minutter * 60);  / ** * Hent hurtigbuffer-prefikset. * * @return streng * / offentlig funksjon getPrefix () return ";

Vi må også legge til Mongo-tilbakeringingen igjen til sjefen:

/ ** * Registrer tjenesteleverandøren. * * @return void * / offentlig funksjon register () $ this-> app-> bindShare ('cache', funksjon ($ app) $ cache_manager = ny CacheManager ($ app); $ cache_manager-> utvide ('mongo ', funksjon ($ app) return new MongoStore (array (' host '=> $ app [' config '] -> get (' cache.mongo.host '),' brukernavn '=> $ app [' config ' ] -> få ('cache.mongo.username'), 'passord' => $ app ['config'] -> få ('cache.mongo.password'), 'database' => $ app ['config' ] -> få ('cache.mongo.database'), 'samling' => $ app ['config'] -> få ('cache.mongo.collection'));); return $ cache_manager;) ; $ this-> app-> bindShare ('cache.store', funksjon ($ app) return $ app ['cache'] -> driver ();); $ this-> app-> bindShared ('memcached.connector', funksjon () returner ny MemcachedConnector;); $ Dette-> registerCommands (); 

Til slutt må vi oppdatere cache.php config-fil:

'driver' => 'mongo', ... / ** * Mongo DB innstillinger * / 'mongo' => array ('host' => '127.0.0.1', 'brukernavn' => ", 'passord' => , 'database' => 'laravel', 'collection' => 'laravel_cache_collection')

Nå, forsøk å bruke Cache :: sette () og Cache :: får () metoder. Hvis det gjøres riktig, bør vi kunne bruke MongoDB til å cache dataene!

Konklusjon

I denne opplæringen lærte vi om følgende:

  • Laravels komponentbaserte system kalt Belyse, som brukes av Laravel-rammeverket.
  • Laravel Service Providers og litt om hvordan de jobber.
  • Laravel's Manager system, som fungerer som både en wrapper og fabrikk for driverne.
  • Session, Auth og Cache-komponenter og hvordan du oppretter nye drivere for hver.
  • Store, Guard og Repository biblioteker som bruker disse driverne.

Forhåpentligvis hjelper dette programmerere til å lage sine egne drivere og utvide den nåværende funksjonaliteten til Laravel-rammeverket.