Taming Slim 2.0

Slim är ett lättviktigt ramverk som packar mycket slag för sitt lilla fotavtryck. Den har ett otroligt routingsystem, och erbjuder en solid bas för att arbeta utan att komma iväg. Låt mig visa dig!

Men det är inte att säga att Slim inte har några problem; det är en-fil setup blir rörig när din ansökan växer. I den här artikeln granskar vi hur du strukturerar en Slim applikation för att inte bara upprätthålla, men förbättra dess funktionalitet och hålla sakerna snygga och systematiska.


Vanilj Slim

Låt oss börja med att titta på en vanlig Slim-kod för att identifiera problemet. När du har installerat Slim through Composer måste du skapa en instans av Smal objekt och definiera dina rutter:

 få ('/', funktion () echo "Hemsida";); $ app-> get ('/ testPage', funktion () använd ($ app) $ app-> render ('testpage.php');); $ App> run ();

Låt oss slänga Slim-objektet i "controller".

Det första metodsamtalet sätter en ny rutt för root-URI-en (/) och kopplar den angivna funktionen till den rutten. Detta är ganska ordentligt, men ändå lätt att installera. Det andra metodsamtalet definierar en rutt för URI testPage. Inuti den tillförda metoden använder vi Slim göra() metod för att göra en syn.

Här ligger det första problemet: den här funktionen (en stängning) kallas inte i det nuvarande sammanhanget och har ingen möjlighet att komma åt Slims funktioner. Det är därför vi behöver använda använda sig av sökord för att skicka referensen till Slim-appen.

Den andra frågan härrör från Slims arkitektur; Det är tänkt att definieras i en enda fil. Naturligtvis kan du lägga ut variabeln till en annan fil, men det blir bara rörigt. Helst vill vi ha möjlighet att lägga till controllers för att modulera ramverket i enskilda komponenter. Som en bonus skulle det vara trevligt om dessa controllers erbjöd inbyggd tillgång till Slim-funktionerna, vilket eliminerar behovet av att skicka referenser till stängningarna.


En liten omvänd teknik

Det är diskutabelt om läsning av källkod från ett open source-projekt betraktas som omvänd teknik, men det är termen jag ska hålla fast vid. Vi förstår hur man använder Slim, men vad händer under huven? Låt oss titta på en mer komplicerad rutt för att komma till roten till denna fråga:

 $ app-> get ('/ users /: name', funktion ($ namn) echo "Hello". $ name;);

Denna ruttdefinition använder ett kolon med ordet, namn. Detta är en platshållare, och värdet som används i sin plats skickas till funktionen. Till exempel, / Användare / gabriel matchar denna rutt och "gabriel" skickas till funktionen. Rutten, / användare, å andra sidan är inte en match eftersom det saknas parametern.

Om du tänker på det logiskt, finns det ett antal steg som måste slutföras för att kunna bearbeta en rutt.

  • Steg ett: Kontrollera om rutten överensstämmer med den aktuella URI.
  • Steg två: extrahera alla parametrar från URI.
  • Steg tre: ring den anslutna stängningen och överlämna de extraherade parametrarna.

För att bättre optimera processen, smal - använd regex callbacks och grupper - lagrar platshållarna när det kontrollerar matchningar. Detta kombinerar två steg i ett, vilket bara innebär att du behöver utföra den anslutna funktionen när Slim är klar. Det blir klart att färdvägsobjektet är självständigt, och uppriktigt sagt, allt som behövs.

I det föregående exemplet hade vi tillgång till Slim-särdrag när vi analyserade rutorna, men vi behövde skicka en Slim-objektreferens eftersom det annars inte skulle vara tillgängligt inom funktionens exekveringskontext. Det är allt du behöver för de flesta applikationer, eftersom programmets logik ska ske i kontrollenheten.

Med det i åtanke, låt oss ta bort "routing" -delen i en klass och vrid Slim-objektet till "controller".


Komma igång

Till att börja med, låt oss ladda ner och installera "Vanilla Slim" om du inte redan har gjort det. Jag antar att du har installerat Composer, men om inte, följ stegen .

Inne i en ny katalog skapar du en fil med namnet composer.json, och lägg till följande:

 "namn": "nät / slim-mvc", "kräver": "slim / slim": "*", "slim / extras": "*", "twig / twig": "*"

I ett terminalfönster, navigera till nämnda katalog och typ kompositör installera. Jag går igenom dessa paket, om det här är första gången du använder Slim.

  • smal / slim - den verkliga Slim-ramen.
  • smal / extramaterial - en uppsättning valfria klasser för att förlänga Slim.
  • kvist / kvist - Twig-templerande motor.

Du behöver tekniskt inte de smarta extrakten eller Twig för denna handledning, men jag gillar att använda Twig istället för standard PHP-mallar. Om du använder Twig behöver du Slim-extras eftersom det ger ett gränssnitt mellan Twig och Slim.

Nu kan vi lägga till våra anpassade filer, och vi börjar med att lägga till en katalog till säljare mapp. Jag heter min Nettuts, men var god att namnge din, vad du än vill. Om du fortfarande befinner dig i terminalen, se till att terminalfönstret finns i projektets katalog och skriv följande:

mkdir-leverantör / Nettnät

Redigera nu composer.json genom att lägga till hänvisningen till den här nya mappen:

 "slim / slim": "*", "slim / extras": "*", "kvist / kvist": "*", " autoload ": " psr-0 ": " Webuts ":" leverantör / "

Vi vill att vår app automatiskt laddar klasser från Nettuts namespace, så det här berättar kompositören att kartlägga alla förfrågningar om Nettuts till PSR-0-standarden från och med Säljare mapp.

Utför nu:

kompositör dump-autoload

Detta kompilerar autoladdaren för att inkludera den nya referensen. Skapa sedan en fil med namnet Router.php, inom Nettuts katalog och ange följande:

  

Vi såg att varje rutobjekt har en fristående funktion som bestämmer om den matchar den angivna URI. Så vi vill ha en rad rutter och en funktion att analysera genom dem. Vi behöver också en annan funktion för att lägga till nya rutter och ett sätt att hämta URI från den aktuella HTTP-begäran.

Låt oss börja med att lägga till några medlemsvariabler och konstruktören:

 Klassruter skyddade $-rutter; skyddad $ request offentlig funktion __construct () $ env = \ Slim \ Miljö :: getInstance (); $ this-> request = new \ Slim \ Http \ Request ($ env); $ this-> routes = array (); 

Vi ställer in rutter variabel för att innehålla rutterna och begäran variabel för att lagra Slim Begäran objekt. Därefter behöver vi möjlighet att lägga till rutter. För att hålla fast vid bästa praxis kommer jag att bryta detta i två steg:

public function addRoutes ($ rutter) foreach ($ rutter som $ route => $ path) $ method = "any"; om (strpos ($ path, "@")! == false) lista ($ path, $ method) = explodera ("@", $ path);  $ func = $ this-> processCallback ($ path); $ r = new \ Slim \ Route ($ rutt, $ func); $ R-> setHttpMethods (strtoupper ($ metod)); array_push ($ this-> rutter, $ r); 

Denna offentliga funktion accepterar en associativ grupp av rutter i formatet av route => väg, var rutt är en vanlig Slim-rutt och väg är en sträng med följande konvention:

Eventuellt kan du lämna vissa parametrar för att använda ett standardvärde. Klassnamnet kommer till exempel att ersättas med Huvudsaklig om du lämnar ut det, index är standard för utelämnade funktionsnamn, och standard för HTTP-metoden är några. Självklart, några är inte en riktig HTTP-metod, men det är ett värde som Slim använder för att matcha alla HTTP-metortyper.

De addRoutes funktionen börjar med a för varje slinga som cyklar genom rutterna. Därefter ställer vi in ​​standard HTTP-metoden, eventuellt överstyrar den med den angivna metoden om @ symbolen är närvarande. Sedan skickar vi återstoden av sökvägen till en funktion för att hämta en återuppringning och bifoga den till en rutt. Slutligen lägger vi till rutten till matrisen.

Nu ska vi titta på processCallback () fungera:

skyddad funktion processCallback ($ path) $ class = "Main"; om (strpos ($ path, ":")! == false) list ($ class, $ path) = explodera (":", $ path);  $ function = ($ path! = "")? $ bana: "index"; $ func = function () använd ($ class, $ function) $ class = '\ Controllers \\'. $ Klass; $ class = new $ class (); $ args = func_get_args (); returnera call_user_func_array (array ($ class, $ function), $ args); ; returnera $ func; 

Den andra frågan härrör från Slims arkitektur; Det är tänkt att definieras i en enda fil.

Vi ställer in standardkursen till Huvudsaklig, och åsidosätta den klassen om kolonsymbolen finns. Därefter bestämmer vi om en funktion är definierad och använder standardmetoden index om nödvändigt. Vi skickar sedan klassen och funktionsnamnen till en stängning och returnerar den till rutten.

Inuti stängningen förbereder vi klassnamnet med namespace. Vi skapar sedan en ny instans av den angivna klassen och hämtar listan över argument som skickats till den här funktionen. Om du kommer ihåg, medan Slim kontrollerar om en rutt matchar, bygger den långsamt en lista med parametrar baserat på jokertecken från rutten. Denna funktion (func_get_args ()) kan användas för att få de överförda parametrarna i en array. Sedan använder du call_user_func_array () Metoden gör att vi kan ange klassen och funktionen medan parametrarna överförs till regulatorn.

Det är inte en väldigt komplicerad funktion när du förstår det, men det är ett mycket bra exempel på när stängningar är till nytta.

För att återskapa, lade vi till en funktion till vår router som låter dig passera en associativ array som innehåller rutter och banor som kartlägger klasser och funktioner. Det sista steget är att bearbeta rutterna och utföra alla som matchar. Att hålla sig till Slim-namngivningskonventionen, låt oss kalla det springa:

public function run () $ display404 = true; $ uri = $ this-> request-> getResourceUri (); $ method = $ this-> request-> getMethod (); ($ this-> rutter som $ i => $ rutt) om ($ route-> matchar ($ uri)) om ($ route-> supportsHttpMethod ($ method) || $ route-> supportsHttpMethod ")) call_user_func_array ($ route-> getCallable (), array_values ​​($ route-> getParams ())); $ display404 = false;  om ($ display404) echo "404 - rutt ej hittad"; 

Vi börjar med att ställa in display404 variabel, representerar inga rutter som hittats, till Sann. Om vi ​​hittar en matchande rutt ställer vi in ​​det här falsk och förbikoppla felmeddelandet. Därefter använder vi Slims begäranobjekt för att hämta den aktuella URI- och HTTP-metoden.

Vi använder den här informationen för att bläddra igenom och hitta matchningar från vår array.

När ruttobjektet är klart tändstickor() funktionen körs, du kan ringa getParams () för att hämta de analyserade parametrarna. Använda den funktionen och getCallable () metod, vi kan genomföra stängningen och överföra nödvändiga parametrar. Slutligen visar vi ett 404 meddelande om ingen rutt matchade den aktuella URI.

Låt oss skapa kontrollerklassen som innehåller callbacks för dessa rutter. Om du har följt med, kanske du har insett att vi aldrig tvingat ett protokoll eller en klasstyp. Om du inte vill skapa en kontrollerklass, fungerar någon klass bra.

Så varför skapar du en kontrollerklass? Det korta svaret är att vi fortfarande inte har använt Slim! Vi använde delar av Slim för HTTP-förfrågan och -rutter, men hela punkten var att ha enkel tillgång till alla Slim-egenskaper. Vår controller klass kommer att förlänga den verkliga Slim-klassen, få tillgång till alla Slim-metoder.

Du kan lika enkelt hoppa över detta och underklassen Slim direkt från dina controllers.


Bygga kontrollenheten

Denna kontroller gör det möjligt att modifiera Slim medan du fortfarande håller den vanilj. Namnge filen Controller.php, och skriv följande kod:

data = $ inställningar ['modell'];  förälder :: __ konstruera ($ inställningar); 

När du initierar Slim kan du passera i olika inställningar, allt från programmets debug-läge till templerande motor. Istället för att hårdkodning av några värden i konstruktorn laddar jag dem från en fil som heter settings.php och överför den uppsättningen till förälderens konstruktör.

Eftersom vi utökar Slim, tyckte jag att det skulle vara coolt att lägga till en "modell" -inställning, så att människor kunde haka sitt dataobjekt direkt i kontrollenheten.

Det är det avsnitt du kan se mitt i ovanstående kod. Vi kontrollerar om modell inställningen har ställts in och tilldelats den till regulatorns data egendom vid behov.

Skapa nu en fil med namnet settings.php i roten till ditt projekt (mappen med composer.json fil) och ange följande:

 new \ Slim \ Extras \ Visningar \ Twig (), 'templates.path' => '... / Visningar', 'model' => (Objekt) array ("message" => "Hello World")); returnera $ inställningar;

Dessa är vanliga Slim-inställningar med undantag av modellen. Vilket värde som helst tilldelas modell egendom överförs till data variabel; Det här kan vara en matris, en annan klass, en sträng, etc ... Jag satte den på ett objekt eftersom jag gillar att använda -> notation i stället för konsolen (array) notation.

Vi kan nu testa systemet. Om du kommer ihåg i router klass, förklara vi klassnamnet med "Kontrollant"namespace. Öppna upp composer.json lägg till följande direkt efter fso-0-definitionen för Nettuts namespace:

"name": "netts / slim_advanced", "require": "slim / slim": "2.2.0", "slim / extras": "*", "kvist / kvist": "*", " autoload ": " psr-0 ": " Webuts ":" leverantör / "," Controller ":" ./ "

Därefter som tidigare, bara dumpa autoloader:

kompositör dump-autoload

Om vi ​​bara ställa in basvägen till rotkatalogen, så namnrymden Kontrollant kommer att kartlägga till en mapp med namnet "Kontrollant"i roten till vår app. Skapa så den mappen:

mkdir Controller

Inne i den här mappen skapar du en ny fil med namnet main.php. Inuti filen måste vi deklarera namnrymden och skapa en klass som utökar vår Kontrollant basklass:

data-> meddelande;  offentliga funktionstest () echo "testsida"; 

Detta är inte komplicerat, men låt oss ta det med måtta. I denna klass definierar vi två funktioner; deras namn spelar ingen roll eftersom vi kommer att kartlägga dem till rutter senare. Det är viktigt att märka att jag direkt åtkomst till egenskaper från styrenheten (dvs modellen) i den första funktionen och i själva verket får du full tillgång till alla Slim-kommandon.

Låt oss nu skapa den faktiska offentliga filen. Skapa en ny katalog i roten till ditt projekt och namnge den offentlig. Som namnet antyder, det här är var alla offentliga saker kommer att bo. Inne i den här mappen skapar du en fil som heter index.php och ange följande:

 'Huvud: index @ get', '/ test' => 'Huvud: test @ get'); $ Router-> addRoutes ($ linjer); $ Router-> run ();

Vi inkluderar Composer's autoloading-bibliotek och skapar en ny instans av vår router. Då definierar vi två rutter, lägger dem till routerns objekt och kör det.

Du måste också aktivera mod_rewrite i Apache (eller motsvarande med en annan webbserver). För att ställa in detta, skapa en fil med namnet .htaccess inuti offentlig katalog och fyll i det med följande:

RewriteEngine On RewriteCond% REQUEST_FILENAME! -F RewriteRule ^ index.php [QSA, L]

Nu kommer alla förfrågningar till den här mappen (som inte matchar en faktisk fil) att överföras till index.php.

I din webbläsare, navigera till din offentlig katalog och du bör se en sida som säger "Hello World". Navigera till "/testa", och du borde se meddelandet" Testsida ". Det är inte hemskt spännande, men vi har framgångsrikt flyttat all logikkod till enskilda kontroller.


Rond två

Slim är inte CodeIgniter, det är inte Symfony och det är inte Laravel.

Så vi har grundläggande funktionalitet, men det finns några grova kanter. Låt oss börja med routern.

Från och med nu visar vi ett enkelt felmeddelande om en rutt inte existerar. I en riktig applikation vill vi ha samma funktion som att ladda en vanlig sida. Vi vill dra nytta av Slims förmåga att ladda visningar, samt ställa in svarets felkod.

Låt oss lägga till en ny klassvariabel som innehåller en valfri sökväg (precis som de andra rutterna). Överst i filen lägger du till följande rad direkt efter definitionen för begäran objekt:

skyddad $ errorHandler;

Låt oss sedan skapa en funktion som accepterar en sökväg och tilldelar den en återuppringningsfunktion. Det här är relativt enkelt eftersom vi redan abstraherade denna funktion:

allmän funktion set404Handler ($ path) $ this-> errorHandler = $ this-> processCallback ($ path); 

Låt oss nu justera springa kommandot att valfritt utföra återuppringningen istället för att bara visa felmeddelandet:

om ($ display404) om (is_callable ($ this-> errorHandler)) call_user_func ($ this-> errorHandler);  annars echo "404 - rutt ej hittad"; 

Öppna kontrollerklassen. Här kan du anpassa Slim: s funktionalitet till dina egna personliga preferenser. Till exempel vill jag ha möjlighet att släppa filutvidgningen när du läser in visningar. Så istället för att skriva $ This-> render ( "home.php");, Jag vill bara skriva: $ This-> render ( "hem");. För att göra detta, åsidosätter vi renderingsmetoden:

public function render ($ namn, $ data = array (), $ status = null) om (strpos ($ namn, ".php") === false) $ name = $ name. ".Php";  förälder :: render ($ namn, $ data, $ status); 

Vi accepterar samma parametrar som föräldrafunktionen, men vi kontrollerar om filtillägget tillhandahålls och lägg till det om det behövs. Efter den här ändringen överför vi filen till överordnad metod för bearbetning.

Detta är bara ett enda exempel, men vi borde lägga några andra ändringar här i göra() metod. Om du till exempel lägger samma sidhuvud och sidfot på alla dina dokument kan du lägga till en funktion renderPage (). Den här funktionen skulle ladda den överförda bilden mellan samtalen för att ladda den vanliga rubriken och sidfoten.

Låt oss nu titta på att ladda vissa visningar. I roten till ditt projekt skapar du en mapp med namnet "Visningar"(plats och namn kan justeras i settings.php fil). Låt oss bara skapa två visade visningar test.php och error.php.

Inuti test.php, lägg till följande:

titel

Detta är sidan name!

Och inuti error.php fil, ange det här:

404

Rutten du letade efter kunde inte hittas

Ändra också Huvudsaklig controller genom att ändra index() funktion till följande:

public function index () $ this-> render ("test", array ("title" => $ this-> data-> meddelande, "namn" => "Hem")); 

Här gör vi testvyn som vi just gjort och skickar den data som ska visas. Låt oss sedan prova en rutt med parametrar. Ändra testa() funktion till följande:

offentliga funktionstest ($ title) $ this-> render ("test", array ("title" => $ title, "name" => "Test")); 

Här tar vi det ett steg längre genom att hämta sidans titel från URI själv. Sist men inte minst, låt oss lägga till en funktion för 404-sidan:

allmän funktion notFound () $ this-> render ('error', array (), 404); 

Vi använder göra() funktionens tredje valfria parameter som anger svarets HTTP-statuskod.

Vår sista redigering finns i index.php att införliva våra nya linjer:

$ routes = array ('/' => ", '/ test /: title' => 'Main: test @ get'); $ router-> addRoutes ($ rutter); $ router-> set404Handler (" Main: notFound "); $ router-> run ();

Du ska nu kunna navigera till de tre linjerna och se deras respektive åsikter.


Slutsats

Med allt som vi åstadkom, har du säkert några frågor om varför Slim inte redan erbjuder dessa ändringar. De verkar logiska, de avviker inte från Slims genomförande för långt, och de ger mycket mening. Josh Lockhart (Slim's Creator) gjorde det bäst:

"Slim är inte CodeIgniter, det är inte Symfony, och det är inte Laravel. Slim är Slim. Det byggdes för att vara lätt och roligt, men kan fortfarande lösa cirka 80% av de vanligaste problemen. I stället för att oroa sig för kanten fall fokuserar det på att vara enkelt och ha en lättläst kodbas. "

Ibland, som utvecklare, blir vi så vana upp som täcker galna scenarier som vi glömmer om vad som är väldigt viktigt: koden. Mods, som i den här handledningen, är bara möjliga på grund av kodens enkelhet och verbositet. Så ja, det kan finnas några kantfall som behöver särskild uppmärksamhet, men du får ett aktivt samhälle som enligt min mening väger mycket.

Jag hoppas att du haft den här artikeln. Om du har några frågor eller kommentarer, lämna ett meddelande nedan. Du kan också kontakta mig via IRC-kanalen på Freenode på #nettuts kanalisera.